python - español - Usar bibliotecas de GUI externas para crear interfaces de usuario en Autodesk Maya
manual de maya 2018 (3)
Desarrollo herramientas en Autodesk Maya. Muchas de las herramientas que construyo tienen GUI simples en ventana para que los animadores y modeladores las usen. Estas GUI a menudo contienen lo que normalmente esperarías ver en cualquier ventana básica; Etiquetas, listas, menús, botones, campos de texto, etc. Sin embargo, existen limitaciones en cuanto a la complejidad de las UI que puede compilar con las herramientas disponibles, específicamente en los tipos de widgets disponibles.
Estoy interesado en usar algunos de los wxPython widgets más avanzados como ListView (grid), Tree, etc. Esto implicaría usar un wxFrame (ventana) completo para mostrar toda la UI, lo que esencialmente significaría que la ventana ya no estar atado a Maya. No es un factor decisivo, pero significa que cuando Maya se minimiza, la ventana no seguirá el mismo camino.
Intenté algo así antes con tkinter como prueba, pero descubrí que necesitaba un MainLoop para ejecutarse en su propio hilo. Esto es lógico, pero en mi caso, entra en conflicto con el propio hilo de Maya, esencialmente haciendo que Maya cuelgue hasta que se cierre la ventana. Esto se debe al hecho de que Maya ejecuta todos los scripts, ya sean MEL o Python, en un único hilo que comparte la GUI principal de Maya. Esto es para evitar que una secuencia de comandos, por ejemplo, eliminar un objeto, mientras que otra secuencia de comandos está tratando de hacer el trabajo en el mismo objeto.
wxPython tiene esta misma metodología "mainloop". Me pregunto si hay alguna forma de evitarlo para que pueda funcionar dentro de Maya.
No sé si hay una forma de evitar un mainloop para la interfaz gráfica de usuario, ya que es necesario para manejar todas las cadenas de eventos y volver a dibujar colas.
Pero hay varios medios de comunicación entre procesos, como tuberías o semáforos. Tal vez sea una opción dividir tu extensión Maya en el plugin real, estar ajustado a maya y una aplicación separada para la GUI. Estos dos podrían usar dichos medios para comunicarse e intercambiar información del modelo entre el complemento y la interfaz gráfica de usuario. No estoy seguro, sin embargo, si realmente puedo recomendar este enfoque porque complica mucho la aplicación.
Podrías echar un vistazo a IPython, un shell interactivo de Python, cuyo equipo de desarrollo se ha esforzado por integrarlo con wxPython. Tienen alguna forma de interrumpir el ciclo de eventos y conectarse para hacer sus propias cosas.
No estoy seguro de si esto es pertinente, pero se descubrió que PyQt es bastante popular dentro de Maya. Puedes probar la técnica aquí o aquí (explicado aquí con el código fuente) para crear un nuevo threadloop a través de Maya y ejecutar dentro de eso. Parece que Maya tiene un módulo incluido que configura un nuevo objeto de subproceso, con una aplicación Q dentro de él:
def initializePumpThread():
global pumpedThread
global app
if pumpedThread == None:
app = QtGui.QApplication(sys.argv)
pumpedThread = threading.Thread(target = pumpQt, args = ())
pumpedThread.start()
y luego configura una función para procesar los eventos de Qt:
def pumpQt():
global app
def processor():
app.processEvents()
while 1:
time.sleep(0.01)
utils.executeDeferred( processor )
Probablemente puedas hacer algo similar con wxPython también. (utils.executeDeferred es una función Maya.) Asegúrese de ver cómo crear una GUI sin bloqueo en la wiki de wxPython. En lugar de processEvents (), querrá configurar un bucle de evento y comprobar si hay eventos "Pendientes" dentro de la función pumpQt (¿se le cambia el nombre?) Más arriba. (La fuente de wxPython tiene una implementación de Python de MainLoop.) Probablemente esto debería hacerse a través de la función app.Yield (), pero no estoy seguro.
def pumpWx():
global app
def processor():
app.Yield(True)
while 1:
time.sleep(0.01)
utils.executeDeferred( processor )
def initializePumpThread():
global pumpedThread
global app
if pumpedThread == None:
app = wx.App(False)
pumpedThread = threading.Thread(target = pumpWx, args = ())
pumpedThread.start()
Los documentos de wxPython indican que se prefiere SafeYield () . De nuevo, parece que podría ser un primer paso, pero no estoy seguro si funcionará y no solo se bloqueará horriblemente. (Hay alguna discusión sobre lo que quiere hacer en la lista de correo de wxPython, pero es de unas pocas versiones menores de wx atrás.) También hay alguna indicación en varios foros de que esta técnica causa problemas con la entrada del teclado. También puedes intentar hacer:
def processor():
while app.Pending(): app.Dispatch()
para tratar con la lista actual de eventos.
¡Buena suerte!
La mejor forma de hacerlo es crear un QWidget con lo que necesita y usarlo desde un MPxCommand a través de la API de C ++. De esta forma, también tienes la posibilidad de insertar editores personalizados completos en Maya a través de scriptedPanels.
Pero si estás obligado a Python, pyQt es el camino a seguir.