Escribe un Administrador de Render para Nuke usando Python

Aprenda cómo escribir un administrador de render personalizado para Nuke usando Python, lo que le permite renderizar uno o más proyectos de Nuke sin necesidad de abrir el software.

1. Introducción

El propósito de este tutorial es explicar cómo escribir un software que le permita administrar el proceso de renderizado en Nuke. Es posible que tenga varios componentes Nuke que se deban procesar, por lo que al usar un programa de este tipo puede renderizarlos todos a la vez sin abrir Nuke, lo que significa que el sistema no está cargando la interfaz gráfica de Nuke, por lo que puede reservar más memoria para el renderizado. proceso. Aquí puedes ver un ejemplo del programa que vas a construir:

Interfaz gráfica de usuario.El programa de tres proyectos..

El programa tiene una interfaz de usuario clara que le permite organizar y poner en cola tantas representaciones como sea necesario.

Requerimientos

En este tutorial, asumo que tienes una comprensión básica de Python y algunos comandos dos. Este software está diseñado para ejecutarse en el sistema operativo Windows. Las herramientas que necesitarás son las siguientes:

Python 2.x instalado (https://www.python.org) No use la versión 3.x porque Nuke no la admite.

Biblioteca wxPython (http://www.wxpython.org) Esto le permite crear una interfaz de usuario. También puede usar Tkinter, Qt, pero esto no se trata en este tutorial..

Estructura de software

Vamos a llamar a este software NukeRenderManager. El programa consta de tres archivos:

  • NukeRenderingManager.py

  • exeNuke.bat

  • Rendering.py

NukeRenderingManager.py: Contiene todo sobre la interfaz gráfica de usuario y toda la información sobre la ubicación de los proyectos de Nuke y todos los rangos de marcos..

exeNuke.bat: se encarga de lanzar Nuke en modo terminal al pasar por toda la información que proviene del archivo NukeRenderingManager.py. Se llama a este archivo para cada procesamiento, por lo que si se deben procesar tres composiciones de Nuke, este archivo se ejecutará tres veces.

Rendering.py: obtiene toda la información de exeNuke.bat y realiza el renderizado. Este archivo se ejecuta para cada proyecto de Nuke..

2. Escribiendo el NukeRenderingManager.py

Descripción

El NukeRenderingManager.py gestiona la interfaz de usuario y organiza la lista de los proyectos para representar.

La interfaz de usuario

Para construir nuestra interfaz de usuario utilizamos la biblioteca wxPython. Como dije antes, puedes usar una biblioteca diferente pero para el propósito de este tutorial, explicaré wxPython. Para instalarlo, solo necesita descargar el instalador, iniciarlo y todo está listo (puede encontrar el enlace de arriba). Después de instalar la biblioteca, debe iniciar Python 2.x IDLE y esto le dará el shell de Python. Desde el Expediente menú elegir Archivo nuevo, Ahora tienes un editor vacío. Si lo desea, puede utilizar cualquier otro editor con el que se sienta cómodo.. 

Editor de Python vacío.

 Guarde el archivo como NukeRenderingManager.py y ponlo en la carpeta que quieras.

Lo primero que debemos hacer es importar los módulos que necesitamos. El primero es OS que nos permite usar las funciones del sistema operativo, el segundo es el wx que será útil para construir una interfaz gráfica de usuario:

importar o importar wx

Vamos a crear una ventana que contenga todo lo que necesitamos, por lo que lograremos este objetivo al crear una clase personalizada derivada de wx.Frame:

Clase mainWindow (wx.Frame):

Luego implementamos el constructor llamando a wx.Frame.__ init__:

def __init __ (self): #constructor wx.Frame .__ init __ (self, None, title = "Nuke Rendering Manager", tamaño = (600,300), style = wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX)

Luego creamos una barra de estado:

self.CreateStatusBar ()

 Añadimos un control de texto para mostrar qué proyectos de Nuke se procesarán:

# Preparar la lista de scripts de Nuke en la pantalla self.NukeScriptsList = wx.TextCtrl (self, style = wx.TE_MULTILINE) self.NukeScriptsList.SetEditable pou. , 50)) self.NukeScriptsList.SetValue ('Scripts de Nuke: \ n')

los wx.TextCtrl nos proporciona un área donde podemos escribir la lista, la necesitamos como multilínea, por lo que declaramos wx.TE_MULTILINE. No necesitamos que sea editable, así que usamos. SetEditable (Falso), Luego definimos algunos colores y finalmente mostramos un texto..

Luego creamos un botón de render:

# crea el botón de render self.RenderButton = wx.Button (self, label = "Render", pos = (8,200))

 Una cosa muy importante es el medidor. El medidor nos permite definir un diseño, usaremos el BoxSizer que coloca los elementos horizontal y verticalmente, elegimos una posición vertical para el control de texto y el botón:

self.layout = wx.BoxSizer (wx.VERTICAL) self.layout.Add (self.NukeScriptsList, 1, wx.EXPAND) self.layout.Add (self.RenderButton, 0, wx.EXPAND) self.SetSizer (self. PlaySizer) diseño)

El segundo parámetro en el Añadir El método es un número que describe cómo ocupa el espacio de hongos cada elemento., 0 Significa que se utilizará el tamaño mínimo., 1 significa que el espacio disponible estará ocupado, en nuestro caso queremos que el botón se minimice y que el control de texto tenga el espacio restante.

Preparamos algunas variables:

self.NukeScripts = [] self.dirName = "" self.fileName = ""

Luego preparamos el menú. Comenzamos creando una barra de menú como wx.MenuBar (), Creamos un menú llamado filemenu como wx.Menu (), agregamos el Añadir Nuke Scripts y Salida Artículos y anexarlos al menú. Y por último le adjuntamos filemenu a menuBar:

# crea elementos de menú menuBar = wx.MenuBar () filemenu = wx.Menu () addNukeScript = filemenu.Append (wx.ID_ANY, "Add Nuke script", "Add Nuke script") ClearList = filemenu.Append (wx.ID_ANY , "Borrar lista", "Borrar lista") exitEvt = filemenu.Append (wx.ID_EXIT, "Exit", "Exit") menuBar.Append (filemenu, "File") self.SetMenuBar (menuBar)

wx.ID_ANY wx.ID_EXIT se utilizan para proporcionar una CARNÉ DE IDENTIDAD A los elementos, en el primer caso obtenemos una identificación para el artículo, pero en el segundo caso tenemos una ID_EXIT que crea una identificación especial para la acción de salida.

El siguiente paso es dejar que estos elementos realicen alguna operación, para eso utilizamos el wx.Enlazar Función que nos permite vincular el elemento a una función específica:

self.Bind (wx.EVT_MENU, self.onAdd, addNukeScript)

El primer argumento dice que estamos tratando con un evento de menú, el segundo llama a la función que queremos vincular a este elemento y el tercero es el elemento en sí. En este caso es el addNukeScritp Elemento en el menú. Todavía tenemos que implementar el auto.onAdd Función, lo haremos más tarde:

self.Bind (wx.EVT_MENU, self.onClearList, ClearList)

 los Limpiar lista la acción está ligada a la onClearList método:

self.Bind (wx.EVT_BUTTON, self.onRender, self.RenderButton)

Aquí nos unimos el self.RenderButton al self.onRender Función que tenemos que implementar:

self.Bind (wx.EVT_MENU, self.onExit, exitEvt)

 Finalmente le asignamos el self.onExit función a la exitEvt elemento.

Para completar el constructor os mostramos el ventana principal:

# muestra la ventana principal self.Show (Verdadero)

Hasta ahora tenemos nuestro constructor:

importar o importar wx clase mainWindow (wx.Frame): def __init __ (self): #constructor wx.Frame .__ init __ (self, None, title = "Nuke Rendering Manager", tamaño = (600,300), style = wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX) # crea una barra de estado self.CreateStatusBar () # prepara la lista de scripts de Nuke en la pantalla self.NukeScriptsList = wx.TextCtrl (self, style = wx.TE_MULTILINE) self.NukeScriptsList.se ) self.NukeScriptsList.SetBackgroundColour ((120,120,120)) self.NukeScriptsList.SetForegegmentoColour ((50,255,50)) self.NukeScriptsList.SetValue ('Nuke scripts: \ n') # crea el botón de cuenta en el que se encuentra. (self, label = "Render", pos = (8,8)) # layout self.layout = wx.BoxSizer (wx.VERTICAL) self.layout.Add (self.NukeScriptsList, 1, wx.EXPAND) self.layout .Agregue (self.RenderButton, 0, wx.EXPAND) self.SetSizer (self.layout) #variables self.NukeScripts = [] self.dirName = "" self.fileName = "" # crea elementos de menú menuBar = wx. MenuBar () filemenu = wx.Menu () addNukeScript = filemenu.Append (wx.ID_ANY, "Agregar secuencia de comandos de Nuke", " Agregue el script Nuke ") ClearList = filemenu.Append (wx.ID_ANY," Borrar lista "," Borrar lista ") exitEvt = filemenu.Append (wx.ID_EXIT," Exit "," Exit ") menuBar.Append (filemenu," Archivo ") self.SetMenuBar (menuBar) # vincula elementos a eventos self.Bind (wx.EVT_MENU, self.onAdd, addNukeScript) self.Bind (wx.EVT_MENU, self.onClearList, ClearList) self.Bind (wx.EVT_BUTTON , self.onRender, self.RenderButton) self.Bind (wx.EVT_MENU, self.onExit, exitEvt) # muestra la ventana principal self.Show (Verdadero)
Instantánea del editor.

Echemos un vistazo a las funciones. Lo primero que quiero explicar es onAdd que se ejecuta cada vez que el evento de menú addNukeScript se llama. El objetivo de esta función es agregar la información de los scripts de Nuke en una lista:

# agrega scripts Nuke en la lista def onAdd (self, event): wildcard = "Nuke scripts * .nk | * .nk" dlg = wx.FileDialog (self, message = "Add Nuke script", wildcard = wildcard, style = wx.OPEN) si dlg.ShowModal () == wx.ID_OK: self.dirName = dlg.GetDirectory () self.fileName = dlg.GetFilename () self.NukeScripts.append (self.dirName + self.fileName) self .updateList () dlg.Destroy ()

Debido a que esta función se llama cuando ocurre un evento, como la definimos, tenemos que incluir un parámetro adicional que en este caso llamamos evento. Definimos un comodín como una cadena, que es útil para guiar a los usuarios a qué extensión deben buscar:

wildcard = "Scripts de Nuke * .nk | * .nk"

Se crea un cuadro de diálogo para abrir un archivo y, a medida que el usuario hace clic en Aceptar, memorizamos el directorio y el nombre del archivo a nuestras variables y lo llamamos lista de actualizacion para actualizar la pantalla:

if dlg.ShowModal () == wx.ID_OK: self.dirName = dlg.GetDirectory () self.fileName = dlg.GetFilename () self.NukeScripts.append (self.dirName + self.fileName) self.updateList ()

los lista de actualizacion El método borra la pantalla, recorre NukeScripts Lista y escribe de nuevo en la pantalla:

#it ​​actualiza la lista de scripts de Nuke en la pantalla def updateList (self): self.NukeScriptsList.Clear () para i in self.NukeScripts: self.NukeScriptsList.AppendText (i + "\ n") 

los onClearList función borra la pantalla y la NukeScripts lista:

def onClearList (self, event): self.NukeScriptsList.Clear () self.NukeScripts = []

Tenemos onRender () , que se implementará en la siguiente sección, y la onExit Función que cierra la aplicación:

# inicia el proceso de renderizado def onRender (self, event): print "Rendering ..." # cierra el def def del programa (self, event): self.Close (True)

Esta es la definición de la clase mainWindow, ahora necesitamos crear una instancia para poder verla y usarla, pero primero tenemos que crear una wx.App objeto:

app = wx.App (Falso)

Entonces creamos nuestro ventana principal ejemplo:

mainWindow = mainWindow ()

Finalmente necesitamos llamar al Bucle principal Función para iniciar la aplicación:

app.MainLoop ()

Entonces, en este punto tenemos el código NukeRenderingManager.py, excepto el método onRender que vamos a implementar en la siguiente sección.

Para hacer nuestro programa más robusto, agregué un par de líneas para hacer algunas verificaciones. Como cargamos un script Nuke, sería bueno si verificamos si la extensión del archivo es .nk, Incluso si el comodín filtra nuestra elección. Usamos la os.path.splitext función, entonces si la extensión es .nk procedemos de forma normal:

# comprobamos si tenemos una secuencia de comandos de Nuke self.extension = os.path.splitext (self.fileName) si self.extension [1] == ". nk": self.NukeScripts.append (self.dirName + self.fileName ) self.updateList ()

los os.path.splitext devuelve una lista con el nombre y la extensión del archivo en el [0] y [1] posición. Ya que estamos cargando archivos externos, es posible que algunos de ellos estén dañados, por lo que para aumentar la calidad de nuestra aplicación, manejaremos las excepciones:

# agrega scripts Nuke en la lista def onAdd (self, event): wildcard = "Nuke scripts * .nk | * .nk" dlg = wx.FileDialog (self, message = "Add Nuke script", wildcard = wildcard, style = wx.OPEN) intente: si dlg.ShowModal () == wx.ID_OK: self.dirName = dlg.GetDirectory () self.fileName = dlg.GetFilename () # comprobamos si tenemos un script de Nuke self.extension = os.path.splitext (self.fileName) if self.extension [1] == ". nk": self.NukeScripts.append (self.dirName + "\\" + self.fileName) self.updateList () excepto: imprimir "no se puede leer este archivo" dlg.Destroy ()

 Como habrás notado yo he usado self.NukeScripts.append (self.dirName + "\\" + self.fileName), Tuve que agregar "\\" porque me di cuenta de que si alguna secuencia de comandos Nuke se encuentra en do:\ vuelve do:\, tienes que añadir el \ a mano.

Antes de finalizar esta sección, quiero mencionar que para hacer que todo el sistema funcione, deberíamos evitar colocar los scripts de detonador, el exeNuke.bat y el Rendering.py Archivos en una carpeta que tiene una ruta muy larga. He estado probando el programa y, por alguna razón, cuando esta ruta es demasiado larga, no funciona, podría deberse a que el indicador no puede manejar dichas cadenas..

Así que nuestro NukeRenderingManager.py es el siguiente:

importar o importar wx clase mainWindow (wx.Frame): def __init __ (self): #constructor wx.Frame .__ init __ (self, None, title = "Nuke Rendering Manager", tamaño = (600,300), style = wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX) # crea una barra de estado self.CreateStatusBar () # prepara la lista de scripts de Nuke en la pantalla self.NukeScriptsList = wx.TextCtrl (self, style = wx.TE_MULTILINE) self.NukeScriptsList.se ) self.NukeScriptsList.SetBackgroundColour ((120,120,120)) self.NukeScriptsList.SetForegegmentoColour ((50,255,50)) self.NukeScriptsList.SetValue ('Nuke scripts: \ n') # crea el botón de cuenta en el que se encuentra. (self, label = "Render", pos = (8,8)) # layout self.layout = wx.BoxSizer (wx.VERTICAL) self.layout.Add (self.NukeScriptsList, 1, wx.EXPAND) self.layout .Agregue (self.RenderButton, 0, wx.EXPAND) self.SetSizer (self.layout) # variables self.NukeScripts = [] self.dirName = "" self.fileName = "" # crea el menú del menú menuBar = wx. MenuBar () filemenu = wx.Menu () addNukeScript = filemenu.Append (wx.ID_ANY, "Agregar secuencia de comandos de Nuke", "Agregar secuencia de comandos de Nuke") ClearList = filemenu.Append (wx.ID_ANY, "Clear list", "Clear list") exitEvt = filemenu.Append (wx.ID_EXIT, "Exit", "Exit") menuBar.Append (filemenu, "Archivo") self.SetMenuBar (menuBar) # vincula elementos a eventos self.Bind (wx.EVT_MENU, self.onAdd, addNukeScript) self.Bind (wx.EVT_MENU, self.onClearList, ClearList) self.Bind (wx. EVT_BUTTON, self.onRender, self.RenderButton) self.Bind (wx.EVT_MENU, self.onExit, exitEvt) # muestra la ventana principal self.Show (True) #it actualiza la lista de scripts de Nuke en la pantalla def updateList (self) : self.NukeScriptsList.Clear () para i in self.NukeScripts: self.NukeScriptsList.AppendText (i + "\ n") # agrega scripts de Nuke en la lista def onAdd (self, event): wildcard = "Nuke scripts *. nk | * .nk "dlg = wx.FileDialog (self, message =" Add Nuke script ", wildcard = wildcard, style = wx.OPEN) try: if dlg.ShowModal () == wx.ID_OK: self.dirName = dlg.GetDirectory () self.fileName = dlg.GetFilename () # comprobamos si tenemos una secuencia de comandos Nuke self.extension = os.path.splitext (self.fileName) si self.extension [1] == ".nk": self.NukeScripts.append (self.dirName + "\\" + self.fileName) self.updateList () excepto: imprimir "no se puede leer este archivo" dlg.Destroy () def onClearList (self, event) : self.NukeScriptsList.Clear () self.NukeScripts = [] # inicia el proceso de renderizado para cada defensor de comandos Nuke def onRender (self, evento): # para implementar return # cierra el programa def onExit (self, evento): self .Cerrar (Verdadero) app = wx.App (False) mainWindow = mainWindow () app.MainLoop ()
Otra instantánea del editor..

3. Escribiendo el archivo exeNuke.bat

Descripción

El sistema operativo Windows reconoce un archivo bat como una colección de comandos. Puede escribir cualquier tipo de comando que desee, también puede iniciar programas, y esa es una característica que vamos a utilizar. Si está familiarizado con las instrucciones rápidas, encontrará este proceso fácil.

 En primer lugar, debe abrir un archivo de texto vacío (recomiendo el Bloc de notas) y guardarlo como exeNuke.bat. Como mencioné en la sección anterior, debemos evitar colocar estos archivos en una ubicación que tenga una ruta muy larga, ya que debido a que el indicador no puede manejarlo, coloque los tres archivos que estamos escribiendo en su unidad, con solo algunas subcarpetas, como c: \ NukeRenderingManager o c: \ myProjects \ NukeRenderingManager. 

Esa regla también se aplica a los scripts de Nuke, pueden estar ubicados en un lugar diferente, pero asegúrese de que la ruta no sea demasiado larga..

Implementación

Quiero explicar brevemente cómo funciona Nuke. Normalmente trabajamos en Nuke a través de su interfaz gráfica de usuario, pero para algunas tareas específicas es posible que desee ejecutarlo en modo terminal. Eso significa que solo escribimos comandos para realizar cualquier operación habitual, se parece a un indicador de Windows:

La forma en que enviamos instrucciones a Nuke en modo terminal es escribiendo algún código Python. Supongamos que desea crear un nodo de desenfoque, puede escribir nuke.createNode ('Blur') y así. Lo que vamos a hacer es permitir que el archivo bat abra Nuke en modo terminal e inicie el renderizado de un proyecto, haciendo todo mediante el envío de comandos y sin ninguna interfaz gráfica de usuario..

Las primeras instrucciones son:

C: \ C:

 Esto es para asegurarnos de que podemos comenzar a escribir la ruta Nuke para iniciarla:

cd Programmi \ Nuke6.2v6 Nuke6.2 -t

Por supuesto, estas líneas pueden ser diferentes, escriba la ubicación de su máquina. los -t significa modo terminal. Si hace doble clic en su archivo exeNuke.bat, debería ver Nuke en modo terminal. Si quieres salir, solo escribe dejar() y golpear Entrar. Para realizar el renderizado también necesitamos ejecutar el Rendering.py Archivo, para que podamos actualizar nuestro código:

cd \ c: cd Programmi \ Nuke6.2v6 Nuke6.2 -t c: \ NukeRenderingManager \ Rendering.py

Añadiendo la ubicación de la Rendering.py archivo, le pedimos que abra Nuke en modo terminal y ejecute el Rendering.py que contiene todo el código para realizar el renderizado, y como dije antes, el modo terminal requiere el lenguaje Python, así que usamos el Rendering.py código. Pero aún necesitamos una información, el archivo Rendering.py necesita saber dónde se encuentran los scripts de Nuke. 

Recuerda que el exeNuke.bat y Rendering.py se llamará para cada secuencia de comandos Nuke, por lo que si tenemos que renderizar tres proyectos, se ejecutarán tres veces. Pero cada vez que se les llama Rendering.py necesita saber dónde está ubicado el scritp, para lograr esta tarea necesitamos obtener esta información de lo anterior NukeRenderingManager.py.

Instantánea del editor de archivos por lotes .

Complete NukeRenderingManagerFile.py

El único método que necesitamos implementar es onRender (). Lo que hacemos es recorrer NukeScripts y llamar al archivo bat cada vez:

# inicia el proceso de representación de cada def de Nuke script def onRender (self, event): para i in self.NukeScripts: os.system ("C: /exeNuke.bat" + "" + i)

Usamos el os.system Función para ejecutar el archivo. Como habrás notado también pasamos. yo como el argumento después de un espacio. Básicamente enviamos la ruta NukeScript al archivo por lotes. El hecho de que podamos enviar fácilmente esta información al archivo por lotes nos da una gran flexibilidad.

Completa el archivo exeNuke.bat

La forma en que un archivo por lotes obtiene argumentos es mediante el uso del símbolo % seguido de un número, porque pasamos una información que escribiremos % 1. Aquí el código completo:

cd \ c: cd Programmi \ Nuke6.2v6 Nuke6.2 -t c: \ Rendering.py% 1

 Lanzamos Nuke y llamamos al Rendering.py Dándole la ruta del script como argumento..

Antes de concluir esta sección quiero resumir el proceso descrito hasta ahora. NukeRenderingManager.py nos proporciona la interfaz gráfica de usuario y organiza la lista de los scripts de Nuke que se van a representar. Para cada uno de los guiones. exeNuke.bat y Rendering.py sera llamado. El primero está a cargo de ejecutar Nuke en modo terminal, tomando la ruta del script a procesar y pasándolo a la Rendering.py que realizará el render en sí. Ahora necesitamos implementar Rendering.py.

4. Escribiendo el archivo Rendering.py

Implementación

Lo primero que debemos hacer es tomar la ruta del script que pasamos al archivo por lotes. Para lograr esto, simplemente usamos la siguiente declaración sys.argv [1]. Luego transformamos esta información en cadena:

prj = str (sys.argv [1])

La instrucción para abrir un proyecto de Nuke es la siguiente:

nuke.scriptOpen (prj)

Ahora tenemos el script listo para usar. Lo que debemos hacer ahora es buscar el nodo de escritura que deseamos y renderizamos. En mi ejemplo, el nodo de escritura que necesito se llama Escribir1, pero puedes usar el nombre que quieras. Aquí está el código completo:

prj = str (sys.argv [1]) nuke.scriptOpen (prj) para i en nuke.allNodes (): if i.Class () == "Write": if i ['name']. getValue () = = "Write1": first_frame = nuke.Root (). Knob ('first_frame'). Value () last_frame = nuke.Root (). Knob ('last_frame'). Value () nuke.execute (i, first_frame, last_frame )

Lo que hacemos es recorrer todos los nodos del script, verificamos si el nodo está escrito, controlamos que el nombre sea Escribir1, Obtenemos el primer y último fotograma del proyecto y usamos el nuke.execute Función para ejecutar el render..

Instantánea del archivo rendering.py.

Conclusión

Para iniciar el programa simplemente haga doble clic en el NukeRenderingManager.py. Disfrutar!