Embedding FreeCADGui/fr: Difference between revisions

From FreeCAD Documentation
(ajout de complément d'info sur "pyuic")
(Updating to match new version of source page)
Line 1: Line 1:
You already know that you can [[Embedding FreeCAD|import the FreeCAD module]] into a python application, and use all its tools from the host application. But the FreeCAD User Interface (GUI) can also be imported as a python module. Normally you can only import the complete interface as a whole, not pieces of it. That is because the FreeCAD interface system is not only made of independent widgets and toolbars, but is a complex construction where several invisible components (such as the selection system, etc) are needed for the main 3D view to be able to function.
=Embedding_FreeCADGui/fr=
__TOC__


But, with a bit of hacking, it is possible to import the whole FreeCAD interface, then move the 3D view from it to your own Qt application. We show here 3 different methods.
Vous savez déjà que vous pouvez [[Embedding FreeCAD/fr|importer le module FreeCAD]] dans une application Python, et d'utiliser tous les outils de l'application hôte. Mais l'interface utilisateur FreeCAD (GUI) peut également être importée en tant que module Python.<br>
Normalement, vous pouvez importer l'interface complète dans son ensemble, et non pas des portions de celui-ci. C'est parce que le système d'interface FreeCAD n'est pas seulement faite de widgets indépendants, et, de barres d'outils, mais, cette interface est une construction complexe, où plusieurs composantes invisibles (tels que le système de sélection, etc) sont nécessaires à la vue 3D, pour pouvoir fonctionner.<br>
Mais, avec un peu de "hacking", il est possible d'importer toute l'interface de FreeCAD, alors déplacez la vue 3D de FreeCAD dans votre propre application Qt.


==Using the FreeCAD 3D view widget directly==
Nous vous montrons ici, 3 méthodes différentes.


Be aware that there are a lot of problems with this approach. The Qt event handling doesn't seem to work (no idea why) and if you use the 3d view's context-menu the application crashes. A better way could be to create your own 3d view SoQtExaminerViewer or SoQtViewer and "push" the content of FreeCAD's 3d view to your view, as shown in the other sections below.
==En utilisant directement le widget vue 3D de FreeCAD==

Soyez conscient qu'il y a d''''énormes problèmes''' dans cette approche. La gestion des événements '''Qt''' ne semble pas fonctionner (pourquoi ?? aucune idée !), et, si vous utilisez la vue 3D, du menu contextuel, l'application se bloque. Une meilleure façon serait de créer votre propre vue 3D '''SoQtExaminerViewer''' ou '''SoQtViewer''' et "pousser" le contenu de la vue 3d de FreeCAD dans votre code, comme indiqué dans les sections ci-dessous.

Tout d'abord, obtenir la fenêtre principale via '''PyQt''' :


First, get the main window via PyQt:
<syntaxhighlight>
from PyQt4 import QtGui
from PyQt4 import QtGui
from PyQt4 import QtCore
from PyQt4 import QtCore
Line 25: Line 20:
mw=getMainWindow()
mw=getMainWindow()
</syntaxhighlight>

Ensuite, récupérez les '''View3DInventor''' de la même façon :
Then get the View3DInventor view the same way:
<syntaxhighlight>

def get3dview(mw):
def get3dview(mw):
childs=mw.findChildren(QtGui.QMainWindow)
childs=mw.findChildren(QtGui.QMainWindow)
Line 36: Line 31:
v=get3dview(mw)
v=get3dview(mw)
</syntaxhighlight>

The following code is generated automatically, by [[Dialog_creation|creating a Ui-file with QtDesigner]], and converting it to python code with the pyuic tool:
Le code suivant est généré automatiquement, par la création d'un fichier [[Dialog_creation/fr|QtDesigner]], et de le convertir en code Python avec l'outil '''[[Dialog_creation/fr#Conversion_de_notre_bo.C3.AEte_de_dialogue_en_code_Python_avec_.22pyuic.22|pyuic]]''' :
<syntaxhighlight>

== Conversion de notre boîte de dialogue en code Python avec "pyuic" ==

Maintenant, nous allons sauver notre widget quelque part. Il sera sauvegardé dans un fichier '''.Ui''', que nous allons facilement convertir en script Python avec '''pyuic'''.<br>
Dans windows, le programme est livré avec '''pyuic pyqt''' (à vérifier), sur Linux, vous aurez probablement besoin de l'installer séparément à partir de votre gestionnaire de paquets (sur debian-systèmes basés sur, il fait partie du paquet pyqt4-dev-tools).<br>
Pour faire la conversion, vous aurez besoin d'ouvrir une fenêtre de terminal (ou une fenêtre d'invite de commandes), accédez à l'endroit où vous avez enregistré votre fichier '''ui''' :
* '''pyuic.py''' est l'outil qui convertit les fichiers qt-designer '''.ui''' (Interface Utilisateur) en fichier '''.py''' (code Python), la ligne de commande dans la console DOS est :<br>
pyuic -x fichier.ui > fichier.py

* vous pouvez créer un fichier '''.bat''' pour automatiser la commande:
* copiez cette ligne dans un fichier texte et sauvez le sous le nom '''"compile.bat"'''

@"C:\Python27\python" "C:\Python27\Lib\site-packages\PyQt4\uic\pyuic.py" -x %1.ui > %1.py

* puis tapez à la ligne de commande " '''compile fichier''' " sans extension, le nom "'''fichier'''" entré '''.ui''', sera le nom sortant avec extension '''.py'''
* '''ATTENTION: il faut que les fichiers soient présents et accessibles, vérifiez que les fichiers sont présents et que les chemins sont justes !'''
* pour cet exemple entièrement automatique et simplifié, '''"compile.bat"''' est au même endroit que le '''fichier.ui''' à convertir en '''fichier.py'''<br>
Autres liens de documentation [http://www.qtrac.eu/pyqtbook.html "Python and Qt"] , sur [http://ogirardot.developpez.com/introduction-pyqt/ Développez.com] et bien d'autres.<br>
Sur certains systèmes, le programme est appelé '''pyuic4''' au lieu de '''pyuic''' (attention à la compatibilité). Il sert simplement de convertisseur de fichier '''.Ui''' en un script python '''.py'''.<br><br>

# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
Line 96: Line 72:
def retranslateUi(self, MainWindow):
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
</syntaxhighlight>

Then, create a main window that should be your application's main window, apply the UI setup above to it in order to add an MDI area and "move" our 3d view to it
Ensuite, créez la fenêtre principale, qui devrait être la fenêtre principale de votre application, et appliquez la configuration UI (UI setup) ci-dessus, dans le but d'ajouter une zone MDI et "bouger" notre point de vue 3d pour elle.
<syntaxhighlight>

ui=Ui_MainWindow()
ui=Ui_MainWindow()
my_mw=QtGui.QMainWindow()
my_mw=QtGui.QMainWindow()
Line 104: Line 80:
ui.mdiArea.addSubWindow(v)
ui.mdiArea.addSubWindow(v)
my_mw.show()
my_mw.show()
</syntaxhighlight>
==Creating a soGui Examiner Viewer==


Alternatively, you can also use the FreeCADGui module to extract a coin/openInventor representation of the objects of your scene, then use that coin data in an external viewer (your application). Here is an easy way to get the 3d representation of an object:
==Création d'un examinateur "soGui Examiner Viewer"==
<syntaxhighlight>

Alternativement, vous pouvez également utiliser le module '''FreeCADGui''' pour extraire une représentation '''coin/openInventor''' des objets de votre scène, puis utilisez des données '''coin''', dans une visionneuse externe (votre application).

Voici un moyen facile d'obtenir la représentation 3D d'un objet:

FreeCAD.activeDocument().addObject("Part::Box","myBox")
FreeCAD.activeDocument().addObject("Part::Box","myBox")
s=FreeCADGui.activeDocument().getObject("myBox").toString() # store as string
s=FreeCADGui.activeDocument().getObject("myBox").toString() # store as string
Line 116: Line 90:
inp.setBuffer(s)
inp.setBuffer(s)
myNode=coin.SoDB.readAll(inp) # restore from string
myNode=coin.SoDB.readAll(inp) # restore from string
</syntaxhighlight>

Ensuite, créez un visualiseur autonome avec '''pivy''' :
Then, create a standalone viewer with pivy:
<syntaxhighlight>

from pivy.sogui import *
from pivy.sogui import *
from pivy.coin import *
from pivy.coin import *
Line 143: Line 117:
SoGui.show(myWindow) # Display main window
SoGui.show(myWindow) # Display main window
SoGui.mainLoop() # Main Coin event loop
SoGui.mainLoop() # Main Coin event loop
</syntaxhighlight>

Then you just need to run your viewer:
Ensuite, il vous suffit de lancer votre visualiseur :
<syntaxhighlight>

myViewer()
myViewer()
</syntaxhighlight>
==Using the quarter module==


Instead of using the sogui viewer, you can also use the more modern quarter module. This is probably the best solution of the 3.
==Utilisation d'un module tiers==
<syntaxhighlight>

Au lieu d'utiliser le visualiseur '''Sogui''', vous pouvez aussi utiliser un module tiers plus moderne. C'est probablement la meilleure des 3 solutions.

#!/usr/bin/env python
#!/usr/bin/env python
Line 321: Line 295:
if __name__ == '__main__':
if __name__ == '__main__':
main()
main()
</syntaxhighlight>
== Without even firing up the FreeCAD Gui ==


Starting from FreeCAD rev2760, it is now possible to obtain the coin representation of any FreeCAD object without opening the main window. This makes it extremely easy to implement your own viewer and transparently have FreeCAD updating it. After importing the FreeCADGui module, you need to fire it up with the setupWithoutGUI() method, after which you can use all of FreeCAD's view providers to obtain coin/openInventor nodes.
== Sans exécuter le Gui de FreeCAD ==
<syntaxhighlight>

A partir de FreeCAD rev2760, il est maintenant possible d'obtenir la représentation d'un objet quelconque '''coin''' FreeCAD sans devoir ouvrir la fenêtre principale.<br>
Il est donc extrêmement facile de mettre en œuvre votre propre visualiseur, et, rester transparent pour les mises à jour de FreeCAD.<br>
Après avoir importé le module '''FreeCADGui''', vous devez le mettre à niveau avec la méthode '''setupWithoutGUI()''', après quoi, vous pouvez utiliser tous les fournisseurs de vue de FreeCAD, pour obtenir les nœuds '''coin/openInventor'''.

import os, sys, FreeCAD, FreeCADGui
import os, sys, FreeCAD, FreeCADGui
from PyQt4 import QtCore, QtGui
from PyQt4 import QtCore, QtGui
Line 390: Line 362:
if __name__ == '__main__':
if __name__ == '__main__':
main()
main()
</syntaxhighlight>

Or, if using pivy's sogui module doesn't work for you (the sogui module is becoming obsoleted and the coin developers are now favoring the new quarter library, which has much better interaction with qt), this is the same script but with using quarter:
Ou, si vous utilisez le module '''pivy Sogui''' et qu'il ne fonctionne pas chez vous, (le module '''Sogui''' devient obsolète, et, les développeurs, désormais favorisent la bibliothèque '''coin''', qui a une interaction bien meilleure avec qt), c'est le même scénario, mais avec une aide tierce :
<syntaxhighlight>

#!/usr/bin/env python
#!/usr/bin/env python
Line 477: Line 449:
main()
main()


</syntaxhighlight>
<br>


[[Category:Poweruser Documentation/fr]]
[[Category:Poweruser Documentation]]
[[Category:Python Code/fr]]
[[Category:Python Code]]


{{clear}}
{{languages/fr | {{en|Embedding_FreeCADGui}} {{es|Embedding_FreeCADGui/es}} }}
<languages/>

Revision as of 20:19, 23 September 2014

You already know that you can import the FreeCAD module into a python application, and use all its tools from the host application. But the FreeCAD User Interface (GUI) can also be imported as a python module. Normally you can only import the complete interface as a whole, not pieces of it. That is because the FreeCAD interface system is not only made of independent widgets and toolbars, but is a complex construction where several invisible components (such as the selection system, etc) are needed for the main 3D view to be able to function.

But, with a bit of hacking, it is possible to import the whole FreeCAD interface, then move the 3D view from it to your own Qt application. We show here 3 different methods.

Using the FreeCAD 3D view widget directly

Be aware that there are a lot of problems with this approach. The Qt event handling doesn't seem to work (no idea why) and if you use the 3d view's context-menu the application crashes. A better way could be to create your own 3d view SoQtExaminerViewer or SoQtViewer and "push" the content of FreeCAD's 3d view to your view, as shown in the other sections below.

First, get the main window via PyQt:

    from PyQt4 import QtGui
    from PyQt4 import QtCore
       
    def getMainWindow():
       toplevel = QtGui.qApp.topLevelWidgets()
       for i in toplevel:
          if i.metaObject().className() == "Gui::MainWindow":
             return i
       raise Exception("No main window found")
 
    mw=getMainWindow()

Then get the View3DInventor view the same way:

    def get3dview(mw):
          childs=mw.findChildren(QtGui.QMainWindow)
          for i in childs:
             if i.metaObject().className()=="Gui::View3DInventor":
                return i
          return None
 
    v=get3dview(mw)

The following code is generated automatically, by creating a Ui-file with QtDesigner, and converting it to python code with the pyuic tool:

    # -*- coding: utf-8 -*-
 
    # Form implementation generated from reading ui file 'mainwindow.ui'
    #
    # Created: Sun Dec 27 11:18:56 2009
    #      by: PyQt4 UI code generator 4.6
    #
    # WARNING! All changes made in this file will be lost!
 
    from PyQt4 import QtCore, QtGui
 
    class Ui_MainWindow(object):
        def setupUi(self, MainWindow):
            MainWindow.setObjectName("MainWindow")
            MainWindow.resize(508, 436)
            self.centralwidget = QtGui.QWidget(MainWindow)
            self.centralwidget.setObjectName("centralwidget")
            self.gridLayout = QtGui.QGridLayout(self.centralwidget)
            self.gridLayout.setObjectName("gridLayout")
            self.mdiArea = QtGui.QMdiArea(self.centralwidget)
            self.mdiArea.setViewMode(QtGui.QMdiArea.TabbedView)
            self.mdiArea.setTabPosition(QtGui.QTabWidget.South)
            self.mdiArea.setObjectName("mdiArea")
            self.gridLayout.addWidget(self.mdiArea, 0, 0, 1, 1)
            MainWindow.setCentralWidget(self.centralwidget)
            self.menubar = QtGui.QMenuBar(MainWindow)
            self.menubar.setGeometry(QtCore.QRect(0, 0, 508, 27))
            self.menubar.setObjectName("menubar")
            MainWindow.setMenuBar(self.menubar)
            self.statusbar = QtGui.QStatusBar(MainWindow)
            self.statusbar.setObjectName("statusbar")
            MainWindow.setStatusBar(self.statusbar)
 
            self.retranslateUi(MainWindow)
            QtCore.QMetaObject.connectSlotsByName(MainWindow)
 
        def retranslateUi(self, MainWindow):
            MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))

Then, create a main window that should be your application's main window, apply the UI setup above to it in order to add an MDI area and "move" our 3d view to it

    ui=Ui_MainWindow()
    my_mw=QtGui.QMainWindow()
    ui.setupUi(my_mw)
    ui.mdiArea.addSubWindow(v)
    my_mw.show()

Creating a soGui Examiner Viewer

Alternatively, you can also use the FreeCADGui module to extract a coin/openInventor representation of the objects of your scene, then use that coin data in an external viewer (your application). Here is an easy way to get the 3d representation of an object:

    FreeCAD.activeDocument().addObject("Part::Box","myBox")
    s=FreeCADGui.activeDocument().getObject("myBox").toString() # store as string
    from pivy import coin
    inp.setBuffer(s)
    myNode=coin.SoDB.readAll(inp) # restore from string

Then, create a standalone viewer with pivy:

  from pivy.sogui import *
  from pivy.coin import *
  import sys
 
  def myViewer():
      # Initialize Coin. This returns a main window to use.
      # If unsuccessful, exit.
      myWindow = SoGui.init(sys.argv[0])
      if myWindow == None: sys.exit(1)
  
      # Make an empty scene and add our node to it
      scene = SoSeparator()
      scene.addChild(myNode)
 
      # Create a viewer in which to see our scene graph.
      viewer = SoGuiExaminerViewer(myWindow)
 
      # Put our scene into viewer, change the title
      viewer.setSceneGraph(scene)
      viewer.setTitle("FreeCAD Object Viewer")
      viewer.show()
 
      SoGui.show(myWindow) # Display main window
      SoGui.mainLoop()     # Main Coin event loop

Then you just need to run your viewer:

      myViewer()

Using the quarter module

Instead of using the sogui viewer, you can also use the more modern quarter module. This is probably the best solution of the 3.

    #!/usr/bin/env python
 
    ###
    # Copyright (c) 2002-2008 Kongsberg SIM
    #
    # Permission to use, copy, modify, and distribute this software for any
    # purpose with or without fee is hereby granted, provided that the above
    # copyright notice and this permission notice appear in all copies.
    #
    # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    #
 
    import os
    import sys
 
    from PyQt4 import QtCore, QtGui
    from PyQt4.QtGui import QMainWindow, QWorkspace, QAction, QFileDialog, QApplication
 
    from pivy.coin import SoInput, SoDB
    from pivy.quarter import QuarterWidget
 
    import FreeCAD, FreeCADGui
 
    def getMainWindow():
       toplevel = QtGui.qApp.topLevelWidgets()
       for i in toplevel:
          if i.metaObject().className() == "Gui::MainWindow":
             return i
       raise Exception("No main window found")
 
    class MdiQuarterWidget(QuarterWidget):
        def __init__(self, parent, sharewidget):
            QuarterWidget.__init__(self, parent=parent, sharewidget=sharewidget)
 
        def loadFile(self, filename):
            in_ = SoInput()
            if (in_.openFile(str(filename.toLatin1()))):
                root = SoDB.readAll(in_)
            if (root):
                self.setSceneGraph(root)
                self.currentfile = filename
                self.setWindowTitle(filename)
                return True
            return False
 
        def currentFile(self):
            return self.currentfile
 
        def minimumSizeHint(self):
            return QtCore.QSize(640, 480)
 
    class MdiMainWindow(QMainWindow):
        def __init__(self, qApp):
            QMainWindow.__init__(self)
            self._firstwidget = None
            self._workspace = QWorkspace()
            self.setCentralWidget(self._workspace)
            self.setAcceptDrops(True)
            self.setWindowTitle("Pivy Quarter MDI example")
 
            filemenu = self.menuBar().addMenu("&File")
            windowmenu = self.menuBar().addMenu("&Windows")
 
            fileopenaction = QAction("&Create Box", self)
            fileexitaction = QAction("E&xit", self)
            tileaction = QAction("Tile", self)
            cascadeaction = QAction("Cascade", self)
 
            filemenu.addAction(fileopenaction)
            filemenu.addAction(fileexitaction)
            windowmenu.addAction(tileaction)
            windowmenu.addAction(cascadeaction)
 
            self.connect(fileopenaction, QtCore.SIGNAL("triggered()"), self.createBoxInFreeCAD)
            self.connect(fileexitaction, QtCore.SIGNAL("triggered()"), QtGui.qApp.closeAllWindows)
            self.connect(tileaction, QtCore.SIGNAL("triggered()"), self._workspace.tile)
            self.connect(cascadeaction, QtCore.SIGNAL("triggered()"), self._workspace.cascade)
 
            windowmapper = QtCore.QSignalMapper(self)
            self.connect(windowmapper, QtCore.SIGNAL("mapped(QWidget *)"), self._workspace.setActiveWindow)
 
            self.dirname = os.curdir       
 
        def dragEnterEvent(self, event):
            # just accept anything...
            event.acceptProposedAction()
 
        def dropEvent(self, event):
            mimedata = event.mimeData()
            if mimedata.hasUrls():
                path = mimedata.urls().takeFirst().path()
                self.open_path(path)
 
        def closeEvent(self, event):
            self._workspace.closeAllWindows()
 
        def open(self):
            self.open_path(QFileDialog.getOpenFileName(self, "", self.dirname))
 
        def open_path(self, filename):
            self.dirname = os.path.dirname(str(filename.toLatin1()))
            if not filename.isEmpty():
                existing = self.findMdiChild(filename)
                if existing:
                    self._workspace.setActiveWindow(existing)
                    return
            child = self.createMdiChild()
            if (child.loadFile(filename)):
                self.statusBar().showMessage("File loaded", 2000)
                child.show()
            else:
                child.close()
 
        def findMdiChild(self, filename):
            canonicalpath = QtCore.QFileInfo(filename).canonicalFilePath()
            for window in self._workspace.windowList():
                mdiwidget = window
                if mdiwidget.currentFile() == canonicalpath:
                    return mdiwidget
            return 0;
 
        def createMdiChild(self):
            widget = MdiQuarterWidget(None, self._firstwidget)
            self._workspace.addWindow(widget)
            if not self._firstwidget:
                self._firstwidget = widget
            return widget
 
        def createBoxInFreeCAD(self):
            widget = MdiQuarterWidget(None, self._firstwidget)
            self._workspace.addWindow(widget)
            if not self._firstwidget:
                self._firstwidget = widget
            widget.show()
            doc = FreeCAD.newDocument()
            doc.addObject("Part::Box","myBox")
            iv_=FreeCADGui.getDocument(doc.Name).getObject("myBox").toString()
            in_ = SoInput()
            in_.setBuffer(iv_)
            root = SoDB.readAll(in_)
            if (root):
                widget.setSceneGraph(root)
 
    def main():
        app = QApplication(sys.argv)
 
        mdi = MdiMainWindow(app)   
        mdi.show()
        FreeCADGui.showMainWindow() # setup the GUI stuff of FreeCAD
        mw=getMainWindow()
        mw.hide() # hide all
        if len(sys.argv)==2:
            mdi.open_path(QtCore.QString(sys.argv[1]))
        sys.exit(app.exec_())
 
    def show():
        mdi = MdiMainWindow(QtGui.qApp)   
        mdi.show()
        mw=getMainWindow()
        #mw.hide() # hide all
 
    if __name__ == '__main__':
        main()

Without even firing up the FreeCAD Gui

Starting from FreeCAD rev2760, it is now possible to obtain the coin representation of any FreeCAD object without opening the main window. This makes it extremely easy to implement your own viewer and transparently have FreeCAD updating it. After importing the FreeCADGui module, you need to fire it up with the setupWithoutGUI() method, after which you can use all of FreeCAD's view providers to obtain coin/openInventor nodes.

    import os, sys, FreeCAD, FreeCADGui
    from PyQt4 import QtCore, QtGui
    from PyQt4.QtGui import QMainWindow, QWorkspace, QAction, QFileDialog, QApplication
    from pivy.coin import SoInput, SoDB, sogui
 
    class MdiMainWindow(QMainWindow):
        def __init__(self, qApp):
            QMainWindow.__init__(self)
            self._firstwidget = None
            self._workspace = QWorkspace()
            self.setCentralWidget(self._workspace)
            self.setAcceptDrops(True)
            self.setWindowTitle("Pivy Quarter MDI example")
            self.viewers=[]
 
            filemenu = self.menuBar().addMenu("&File")
            windowmenu = self.menuBar().addMenu("&Windows")
 
            fileopenaction = QAction("&Create Box", self)
            fileexitaction = QAction("E&xit", self)
            tileaction = QAction("Tile", self)
            cascadeaction = QAction("Cascade", self)
 
            filemenu.addAction(fileopenaction)
            filemenu.addAction(fileexitaction)
            windowmenu.addAction(tileaction)
            windowmenu.addAction(cascadeaction)
 
            self.connect(fileopenaction, QtCore.SIGNAL("triggered()"), self.createBoxInFreeCAD)
            self.connect(fileexitaction, QtCore.SIGNAL("triggered()"), QtGui.qApp.closeAllWindows)
            self.connect(tileaction, QtCore.SIGNAL("triggered()"), self._workspace.tile)
            self.connect(cascadeaction, QtCore.SIGNAL("triggered()"), self._workspace.cascade)
 
            windowmapper = QtCore.QSignalMapper(self)
            self.connect(windowmapper, QtCore.SIGNAL("mapped(QWidget *)"), self._workspace.setActiveWindow)
 
        def closeEvent(self, event):
            self._workspace.closeAllWindows()
 
        def createBoxInFreeCAD(self):
            widget = QtGui.QWidget(self._firstwidget)
            viewer = sogui.SoGuiExaminerViewer(widget)
            self._workspace.addWindow(widget)
            if not self._firstwidget:
                self._firstwidget = widget
            widget.show()
            self.viewers.append(viewer)
            doc = FreeCAD.newDocument()
            obj=doc.addObject("Part::Box","myBox")
            doc.recompute()
            root=FreeCADGui.subgraphFromObject(obj)
            viewer.setSceneGraph(root)
 
    def main():
        app = QApplication(sys.argv)
        mdi = MdiMainWindow(app)   
        mdi.show()
        FreeCADGui.setupWithoutGUI()
        sys.exit(app.exec_())
 
    if __name__ == '__main__':
        main()

Or, if using pivy's sogui module doesn't work for you (the sogui module is becoming obsoleted and the coin developers are now favoring the new quarter library, which has much better interaction with qt), this is the same script but with using quarter:

    #!/usr/bin/env python
 
    import os
    import sys
 
    from PyQt4 import QtCore, QtGui
    from PyQt4.QtGui import QMainWindow, QWorkspace, QAction, QApplication
 
    from pivy.coin import SoInput, SoDB
    from pivy.quarter import QuarterWidget
    import FreeCADGui
 
 
    class MdiQuarterWidget(QuarterWidget):
        def __init__(self, parent, sharewidget):
            QuarterWidget.__init__(self, parent=parent, sharewidget=sharewidget)
 
        def minimumSizeHint(self):
            return QtCore.QSize(640, 480)
 
 
    class MdiMainWindow(QMainWindow):
        def __init__(self, qApp):
            QMainWindow.__init__(self)
            self._firstwidget = None
            self._workspace = QWorkspace()
            self.setCentralWidget(self._workspace)
            self.setAcceptDrops(True)
            self.setWindowTitle("Pivy Quarter MDI example")
 
            filemenu = self.menuBar().addMenu("&File")
            windowmenu = self.menuBar().addMenu("&Windows")
 
            fileopenaction = QAction("&Create Box", self)
            fileexitaction = QAction("E&xit", self)
            tileaction = QAction("Tile", self)
            cascadeaction = QAction("Cascade", self)
 
            filemenu.addAction(fileopenaction)
            filemenu.addAction(fileexitaction)
            windowmenu.addAction(tileaction)
            windowmenu.addAction(cascadeaction)
 
            self.connect(fileopenaction, QtCore.SIGNAL("triggered()"), self.createBoxInFreeCAD)
            self.connect(fileexitaction, QtCore.SIGNAL("triggered()"), QtGui.qApp.closeAllWindows)
            self.connect(tileaction, QtCore.SIGNAL("triggered()"), self._workspace.tile)
            self.connect(cascadeaction, QtCore.SIGNAL("triggered()"), self._workspace.cascade)
 
            windowmapper = QtCore.QSignalMapper(self)
            self.connect(windowmapper, QtCore.SIGNAL("mapped(QWidget *)"), self._workspace.setActiveWindow)
 
            self.dirname = os.curdir       
 
        def closeEvent(self, event):
            self._workspace.closeAllWindows()
 
        def createBoxInFreeCAD(self):
            d=FreeCAD.newDocument()
            o=d.addObject("Part::Box")
            d.recompute()
            s=FreeCADGui.subgraphFromObject(o)
            child = self.createMdiChild()
            child.show()
            child.setSceneGraph(s)
 
        def createMdiChild(self):
            widget = MdiQuarterWidget(None, self._firstwidget)
            self._workspace.addWindow(widget)
            if not self._firstwidget:
                self._firstwidget = widget
            return widget
 
 
    def main():
        FreeCADGui.setupWithoutGUI()
        app = QApplication(sys.argv)
        mdi = MdiMainWindow(app)   
        mdi.show()
        sys.exit(app.exec_())
 
 
    if __name__ == '__main__':
        main()