I develop tools in Autodesk Maya. Many of the tools I build have simple windowed GUIs for the animators and modellers to use. These GUIs often contain what you'd normally expect to see in any basic window; labels, lists, menus, buttons, textfields, etc. However, there are limitations to the complexity of the UIs you can build with the available tools, specifically in the types of available widgets.
I'm interested in using some of the more advanced wxPython widgets such as the ListView (grid), Tree, etc. This would involve using a complete wxFrame (window) to display the whole UI, which would essentially mean that window would no longer be tied to Maya. Not a deal breaker, but it means when Maya is minimized, the window won't follow suit.
I've tried something like this before with tkinter as a test, but found that it needed a MainLoop to run in its own thread. This is logical, but in my case, it conflicts with Maya's own thread, essentially making Maya hang until the window is closed. This is due to the fact that Maya runs all scripts, be they MEL or Python, in a single thread that the main Maya GUI shares. This is to prevent one script from, say, deleting an object while another script is trying to do work on the same object.
wxPython has this same "mainloop" methodolgy. I'm wondering if there's any way around it so that it can work within Maya?
I'm not sure if this is germane, but some googling turns up that PyQt is pretty popular inside of Maya. You could try the technique here or here (explained here with source code) of creating a new threadloop via Maya and executing inside of that. It seems Maya has a module included that sets up a new thread object, with a QApplication inside it:
def initializePumpThread(): global pumpedThread global app if pumpedThread == None: app = QtGui.QApplication(sys.argv) pumpedThread = threading.Thread(target = pumpQt, args = ()) pumpedThread.start()
and then sets up a function to process the Qt events:
def pumpQt(): global app def processor(): app.processEvents() while 1: time.sleep(0.01) utils.executeDeferred( processor )
You can probably do something similar with wxPython as well. (utils.executeDeferred is a Maya function.) Be sure to check out how to create a non-blocking GUI on the wxPython wiki. Instead of processEvents(), you'll want to set up an event loop and check for "Pending" events inside the (hopefully renamed?) pumpQt function above. (The wxPython source has a Python implementation of MainLoop.) Likely this should be done through the app.Yield() function, but I'm not sure.
你也可以用wxPython做類似的事情。 (utils.executeDeferred是一個Maya函數。)請務必查看如何在wxPython wiki上創建非阻塞GUI。您需要設置一個事件循環並檢查上面(希望重命名的?)pumpQt函數中的“Pending”事件,而不是processEvents()。 (wxPython源代碼有一個MainLoop的Python實現。)可能這應該通過app.Yield()函數完成,但我不確定。
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()
The wxPython docs indicate SafeYield() is preferred. Again, this seems like it could be a first step, but I'm not sure it will work and not just crash horribly. (There's some discussion about what you want to do on the wxPython mailing list but it's from a few minor versions of wx ago.) There is also some indication in various forums that this technique causes problems with keyboard input. You might also try doing:
def processor(): while app.Pending(): app.Dispatch()
to deal with the current list of events.
I don't know if there is a way around a mainloop for the gui, since it is needed to handle all event chains and redraw queues.
But there are several means of inter-process communication, like pipes or semaphores. Maybe it is an option to split your Maya extension into the actual plugin, being tight into maya, and a separate application for the gui. These two could use such means to communicate and exchange model information between plugin and gui. I'm not sure, however, if I can really recommend this approach because it very much complicates the application.
You could have a look at IPython, an interactive Python shell, whose dev team has put some effort into integrating it with wxPython. They have some way of interrupting the event loop and hooking into it to do their own stuff.
The best way to go is creating a QWidget with what you need, and using it from within a MPxCommand thru the C++ API. That way you also have the chance to inject complete custom editors into Maya via scriptedPanels.
最好的方法是使用您需要的東西創建一個QWidget,並通過C ++ API在MPxCommand中使用它。這樣你也有機會通過scriptedPanels向Maya注入完整的自定義編輯器。
But if you're bound to Python, pyQt is the way to go.