Inside Sabertooth
Learn how Sabertooth uses 3ds Max to create 3D interactive projects, including HBO Go’s Game of Thrones interactive experience
  • 1/3
You are here: Forum Home / Autodesk® MotionBuilder® / Python / PyQt integration in MoBu210 tutorial
  RSS 2.0 ATOM  

PyQt integration in MoBu210 tutorial
Rate this thread
 
34466
 
Permlink of this thread  
avatar
  • sphaneuf
  • Posted: 15 September 2009 07:44 AM
  • Total Posts: 10
  • Joined: 14 August 2009 04:08 PM

Hi guys,

Some people have noticed that under the hood, Mobu is built with Qt. This means you can develop ORSDK plugin with Qt and this also means that you can use PyQt to create UI. The following will explain how to integrate PyQt in MoBu 2010.

DISCLAIMER: This is totally unsupported! MoBu 2010 wasn’t meant to work WELL with PyQt. In fact, I didn’t think it would work (but it does)! It has a big limitation though: in Qt (and in most UI toolkit) every window/widget must be parented to another window with only the Main Application Window being “parentless”. Currently with Mobu 2010 there is no way to access the QWidget hierarchy from Python. So if you create a tool with PyQt, this QtTool won’t be parented to any window in MoBu. So your QtTool will be its “own” main window: it will have a window button in the Windows Bar. Also, your QtTool will work “funkily” with Motionbuilder focus (since it is a main window in itself): if you click inside MoBu, your QtTool will lose focus and it will “hide” behind MoBu mainWindow. If you work with 2 screens and have MoBu on a screen and your QtTool on another screen it should be fine.

Here are the steps on how to integrate PyQt with MoBu2010:

1- Install the official distribution for Python 2.6.
2- Download and install PyQt4 for Windows: http://www.riverbankcomputing.com/software/pyqt/download. Take the PyQt-Py2.6-gpl-4.5.4-1.exe package.
3- Install PyQt. This will install in c:\Python26\Lib\site-packages which is the “official” directory for all Python third party libraries.
4- Now you must add the site-packages path to MoBu python search path. To do so you will need to modify the Python initialization file: <MoBuInstallPath>\bin\config\Python\pyfbsdk_init.py. BEFORE DOING ANYTHING ELSE BACKUP THIS FILE! Are your sure you did a backup? I’m waiting…

In that file, find the lines:
path.extend(sys.path)
sys.path = path

Add the following 2 lines below these lines:
import site
site.main()

I included as an attachment a zip file containing a modified pyfbsdk_init.py that contains these lines.

5- Start MoBu. Start the Python Editor.
6- Type: import PyQt4
I added a test script to the zip file. You can try it. It creates a QPushButton and hook a callback creating a cube in it.

7- Rejoice!

Here is the official documentation for PyQt4:
http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/pyqt4ref.html

Here a few good reference links for PyQt4 documentation:
http://zetcode.com/tutorials/pyqt4/
http://diotavelli.net/PyQtWiki
http://wiki.python.org/moin/PyQt4

Good luck and don’t forget that this is totally unsupported!

Sebastien



Replies: 1
/img/forum/dark/default_avatar.png

The sample seems to have been lost… here is one I finaly made to work.

Bruno.

from pyfbsdk import *
from PyQt4 import *

class 
QuitButton(QtGui.QWidget):
    
def __init__(selfparent=None):
        
QtGui.QWidget.__init__(selfparent)

        self
.setGeometry(300300250150)
        self
.setWindowTitle('Patate')

        cube 
QtGui.QPushButton('Cube'self)
        cube
.setGeometry(10105030)

        cube
.clicked.connect(self.CreateCube)
                
    def CreateCube(self)
:
        
lCube FBModelCube("Cube")
        lCube
.Show True


app 
QtGui.qApp
qb 
QuitButton()
qb
.show()
Author: BROY

Replied: 25 March 2010 02:32 AM  
avatar

Hi Sebastien, thanks for the info. I have used pyqt in the past to develop a gui for MB7.5, but, I have to ask why would you need pyqt with MB2010? Are there controls missing from the new built in gui tools? or is it for a legacy pyqt application?

just curious,
brian



MotionBuilder 7.5 thru ...
3dsMax 2.5 thru ...

Replies: 0
avatar
  • sphaneuf
  • Posted: 23 September 2009 06:12 AM

Currently with MoBu 2010 you can use MoBu native UI from Python to create Gui. But some users prefers to use Qt since it is well documented and more complete and powerful than MoBu’s GUI.

The difference with the implementation you did is that PyQt runs INSIDE MotionBuilder. So from within your Qt tool you can access and modify Mb objects without “pushing” command to a terminal/ip connection.



Replies: 0
avatar
  • csa3d
  • Posted: 17 October 2009 03:56 AM

Thanks for the tutorial, got it up and running.  A few questions:

1.) Is there any advantage to modifying the ..\bin\config\Python\pyfbsdk_init.py file that comes with motionbuilder as opposed to placing the following lines of code in a startup.py file placed in ..\bin\config\PythonStartup\

import sys
# path to location where PyQt4 installs, which by default goes here
sys.path.append('C:/Python26/Lib/site-packages')

2.) Qt Tutorials seem to all have a common line in them that reads

app QtGui.QApplication(sys.argv)
#
# .. window code ..
#
sys.exit(app.exec_())

MoBu doesn’t appear to have a sys.argv, how do you typically deal with this?  Take for instance this block of code:

import sys
from PyQt4 import QtGui

app 
QtGui.QApplication(sys.argv)

widget 
QtGui.QWidget()
widget
.resize(250150)
widget
.setWindowTitle('simple')
widget
.show()

sys
.exit(app.exec_())

If you comment out the “app = “ line and the “sys.exit” line the program runs fine, and displays a simple window.  I’ve read that Qt expects 1 application instance, and MoBu is built off Qt, so am I to expect that those two lines are unnecessary inside MoBu because the main application window is all ready created, and is the hook for any other Qt interface built?

Thanks in advance
-csa



Replies: 0
avatar
  • csa3d
  • Posted: 20 October 2009 12:40 AM

I have found part of my answer to this one:

csa3d 17 October 2009 10:56 AM

2.) Qt Tutorials seem to all have a common line in them that reads

app QtGui.QApplication(sys.argv)
#
# .. window code ..
#
sys.exit(app.exec_())


MoBu doesn’t appear to have a sys.argv, how do you typically deal with this?

From the docs at http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qtgui.html#qApp-var

QApplication qApp

This member should be treated as a constant.

A global pointer referring to the unique application object. It is equivalent to the pointer returned by the QCoreApplication.instance() function except that, in GUI applications, it is a pointer to a QApplication instance.

Only one application object can be created.

So you need to use the following line to get a handle to the existing application instance:

app=QtGui.qApp

as for the last line which was erroring out, if I understand things right, you only need to attach your GUI to the application, which can be done so like so:

app.connect(appQtCore.SIGNAL("lastWindowClosed()"),
                            
appQtCore.SLOT("quit()"))

The final code for the example posted above would now read:

import sys
from PyQt4 import QtGui
QtCore

app 
QtGui.qApp

widget 
QtGui.QWidget()
widget
.resize(250150)
widget
.setWindowTitle('simple')
widget
.show()

app
.connect(appQtCore.SIGNAL("lastWindowClosed()")appQtCore.SLOT("quit()"))

Does anyone have any links which marry PyQt and Motionbuilder and/or Maya specifically?

-csa



Replies: 0
avatar
  • csa3d
  • Posted: 22 October 2009 03:01 AM

On a side note, if you are running 64 bit MotionBuilder, you will need to install 64 bit PyQt libraries.  By default, these are not available for direct download from riverbankcomputing.  You will have to compile you own or you can try to download a pre-compiled version and give that a go. The pre-compiled version happened to work on my setup.

-csa



Replies: 0
avatar
  • Neill3d
  • Posted: 15 February 2010 03:08 AM

As I see, this is not a real Qt integration in Mobu… this is just python code of using Qt without any attachment to MoBu… Anyway It could be useful.
But real Qt integration is supported by MoBu 2010 only in ORSDK I guess by new special Visual Component of the MoBu’s UI - FBWidgetHolder. In this case Qt widget will be parented to the MoBu’s component.



Replies: 0
avatar
  • BE
  • Posted: 19 July 2010 08:08 PM

There are some more interesting possibilities along these lines that people may be interested in trying out.

Interesting functions to explore

QtGui.qApp.allWidgets()

Returns all qt widgets of the motionbuilder interface, bit messy to work with thought so better to use.

QtGui.qApp.topLevelWidgets()

This returns all windows...like viewer window for example, the menu bar or more interestingly any FBTool window that has been shown.

So its possible to get a pointer to the qt widget representing the window of a FBTool and since any QWidget that you create can be parented into any other widget you can now after you have found your tool window parent in your qt gui into the FBTool that you created.

Seems that the pointers to the widgets are destroyed once the parent is hidden so unlike normal tools it becomes necessary to recreate the gui each time you show the window however that is easy enough to do with a callback to the tools OnShow event.

So i make this subclass of FBTool that i use for my qt based tools

from pyfbsdk import FBTool
from pyfbsdk_additions import AddTool
from PyQt4 import QtGui
,QtCore 

class QTool(FBTool):
    
def __init__(selfnameui_classregister=True):
        
FBTool.__init__(self,name)
        
self.widget=None
        self
.__ui_=ui_class
        
if register:
            
AddTool(self)
        
self.OnShow.Add(self.build)
            
    
def build(self,inst,evt):
        
self.widget=None
        
for widget in QtGui.qApp.topLevelWidgets():
            if 
widget.windowTitle()==self.Name:
                
self.widget=widget
                
break
        if 
not self.widget:
            
raise RuntimeError('Unable to locate the QWidget representing this FBTool')
        
self.__ui_self.widget )

Then i use designer to make my interfaces, signals etc and use the uic module to source the .ui files into python classes and then i create a interface class for that which will take care of running any pyfbsdk code based on existing or custom signals ive setup..could look something like this:

from YOUR_PACKAGE_HERE import QTool
from PyQt4 import uic
QtGuiQtCore

#-------------------------------------------------------------------------------
# Locate the .ui file to load as the qt interface
#-------------------------------------------------------------------------------
ui_filename=setYourUiFilename()
#-------------------------------------------------------------------------------
# Use the uic module to compile it and generate the ui class and its baseclass
#-------------------------------------------------------------------------------
UI_CLASSQT_BASE=uic.loadUiType(ui_filename)

class 
InterfaceClass(QT_BASE):
    
def __init__(self,parent):
        
QT_BASE.__init__(self,parent)
        
#-----------------------------------------------------------------------
        # Create the controls
        #-----------------------------------------------------------------------
        
self.ui UI_CLASS( )
        
self.ui.setupUiself )
        
#-----------------------------------------------------------------------
        # The parent of this widget will be a widget representing a FBTool window
        # and for this widget to resize into that window we need to add a new
        # layout owned by the parent.
        #-----------------------------------------------------------------------
        
layout=QtGui.QBoxLayout(0,parent)
        
layout.addWidget(self)

        
# your own initializer here, probably hooking up your custom signals
        # self.emit(QtCore.SIGNAL("myCustomSignal(QString)"),self.returnAString)

So that creates a interface class that takes care of initializing the qt and running the actual mobu code. The constructor takes a parent argument and that is where the QTool comes in. The QTool will take this interface class, create it and pass its own qt widget representation to the interface class initializer which is then used as its parent. The interface class adds a layout to the QTool which sizes itself to fit within the tool window so then the interface is drawn on top of the FBTool window. Code to create the tool could look something like this:

DestroyToolByName("MyToolName")
QTool("MyToolName",InterfaceClass)

And thats all there is to it. The new qt interface is not really part of the FBTool, rather its just drawn on top of it so potentially if one finds a way to access widget representations to FBLayouts it would be possible to mix pyfbsdk controls with pyqt controls

...lots of interesting things are accessible now. Its possible to add your own menu bars and menu items and so on.

So far my own tools created this way seem pretty stable

Oh and the above works only if the base of your qt interface is QWidget. If you are trying to integrate a qt tool based on QMainWindow and not FBTool a slightly different approach is needed since the QMainWindow will not parent itself happily into another window. In this case you need to find the qApp.topLevelWidgets() with the .name ‘Viewer’ which is the viewer window and then pass that QWidget to as the parent to your main window. Then if you want it to look like other FBTools you can set the window decorators on it with window.setWindowFlags(Qt.Tool)



Replies: 0
avatar
  • Nenox
  • Posted: 11 July 2011 11:12 PM

I’m having trouble importing PyQt4 into MotionBuilder (& Maya) 2011/2012 versions.

While this works:
import PyQt4

This breaks:
from PyQt4 import QtGui

With the following error:
ImportError: DLL load failed: The specified procedure could not be found.

PyQt4 works a treat outside these applications :-)

I’m using PyQt-Py2.6-x86-gpl-4.8.4-1.exe from PyQt’s download page and I don’t think it’s built against QT 4.5.3., that MotionBuilder uses. I have been unable to find the installer referenced in the tutorial. Could that be the issue?

Any help would be great!



Replies: 2
/img/forum/dark/default_avatar.png

Found a versions of PyQwt4 compiled against 4.5.3 and things are now working :-)

http://pyqwt.sourceforge.net/download.html

Author: Nenox

Replied: 11 July 2011 11:33 PM  
/img/forum/dark/default_avatar.png

Hi Nenox,

the only PyQt release that seems to works with Mobu is the one describe at the beginning of the page

“Take the PyQt-Py2.6-gpl-4.5.4-1.exe package.” we have also tried other release but none of them work..!

note:
apparently this doesn’t work with Mobu 2011, but is working with 2010/2012

Fred

Author: nouknouk

Replied: 05 September 2011 02:16 AM