Embedding FreeCADGui/fr: Difference between revisions

From FreeCAD Documentation
(Updating to match new version of source page)
(Updating to match new version of source page)
Line 1: Line 1:
<languages/>
<languages/>

== Introduction ==

<div class="mw-translate-fuzzy">
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.
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.
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.
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.
</div>


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.
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.
Line 11: Line 16:
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.
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.


<div class="mw-translate-fuzzy">
Tout d'abord, obtenir la fenêtre principale via '''PyQt''' :
Tout d'abord, obtenir la fenêtre principale via '''PyQt''' :
</div>
<syntaxhighlight>
{{Code|code=
from PySide import QtGui
from PySide import QtGui
from PySide import QtCore
from PySide import QtCore

def getMainWindow():
def getMainWindow():
toplevel = QtGui.qApp.topLevelWidgets()
toplevel = QtGui.qApp.topLevelWidgets()
Line 23: Line 30:
raise Exception("No main window found")
raise Exception("No main window found")


mw=getMainWindow()
mw = getMainWindow()
}}
</syntaxhighlight>

Ensuite, récupérez les '''View3DInventor''' de la même façon :
Ensuite, récupérez les '''View3DInventor''' de la même façon :
{{Code|code=
<syntaxhighlight>
def get3dview(mw):
def get3dview(mw):
childs=mw.findChildren(QtGui.QMainWindow)
childs=mw.findChildren(QtGui.QMainWindow)
for i in childs:
for i in childs:
if i.metaObject().className()=="Gui::View3DInventor":
if i.metaObject().className() == "Gui::View3DInventor":
return i
return i
return None
return None


v=get3dview(mw)
v = get3dview(mw)
}}
</syntaxhighlight>

Le code suivant est généré automatiquement, par la création d'un fichier [[Dialog_creation/fr|création d'un fichier UI avec QtDesigner]], et de le convertir en code Python avec l'outil pyuic:
Le code suivant est généré automatiquement, par la création d'un fichier [[Dialog_creation/fr|création d'un fichier UI avec QtDesigner]], et de le convertir en code Python avec l'outil pyuic:
{{Code|code=
<syntaxhighlight>
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-


Line 80: Line 89:
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>

<div class="mw-translate-fuzzy">
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.
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.
</div>
<syntaxhighlight>
{{Code|code=
ui=Ui_MainWindow()
ui = Ui_MainWindow()
my_mw=QtGui.QMainWindow()
my_mw = QtGui.QMainWindow()
ui.setupUi(my_mw)
ui.setupUi(my_mw)
ui.mdiArea.addSubWindow(v)
ui.mdiArea.addSubWindow(v)
my_mw.show()
my_mw.show()
}}
</syntaxhighlight>

==Création d'un examinateur "soGui Examiner Viewer"==
==Création d'un examinateur "soGui Examiner Viewer"==


<div class="mw-translate-fuzzy">
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).
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:
Voici un moyen facile d'obtenir la représentation 3D d'un objet:
</div>
<syntaxhighlight>
{{Code|code=
FreeCAD.activeDocument().addObject("Part::Box","myBox")
from pivy import coin
s=FreeCADGui.activeDocument().getObject("myBox").toString() # store as string
import FreeCAD as App
from pivy import coin

inp.setBuffer(s)
box = App.activeDocument().addObject("Part::Box", "myBox")
myNode=coin.SoDB.readAll(inp) # restore from string
s = box.ViewObject.toString() # store as string
</syntaxhighlight>
inp = coin.SoInput()
inp.setBuffer(s)
myNode = coin.SoDB.readAll(inp) # restore from string
}}

<div class="mw-translate-fuzzy">
Ensuite, créez un visualiseur autonome avec '''pivy''' :
Ensuite, créez un visualiseur autonome avec '''pivy''' :
</div>
<syntaxhighlight>
{{Code|code=
from pivy.sogui import *
from pivy.sogui import *
from pivy.coin import *
from pivy.coin import *
Line 111: Line 132:
# If unsuccessful, exit.
# If unsuccessful, exit.
myWindow = SoGui.init(sys.argv[0])
myWindow = SoGui.init(sys.argv[0])
if myWindow == None: sys.exit(1)
if myWindow == None:
sys.exit(1)


# Make an empty scene and add our node to it
# Make an empty scene and add our node to it
Line 127: Line 149:
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>

Ensuite, il vous suffit de lancer votre visualiseur :
Ensuite, il vous suffit de lancer votre visualiseur :
{{Code|code=
<syntaxhighlight>
myViewer()
myViewer()
}}
</syntaxhighlight>

==Utilisation d'un module tiers==
==Utilisation d'un module tiers==


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


== Sans exécuter le Gui de FreeCAD ==
== Sans exécuter le Gui de FreeCAD ==


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

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


== Additional information ==
</syntaxhighlight>


* [https://forum.freecadweb.org/viewtopic.php?f=8&t=203 Embedding a view to another (QT) application?]
[[Category:Poweruser Documentation{{#translation:}}]]
* [https://forum.freecadweb.org/viewtopic.php?t=12575 Using Gui functions without Gui.showMainWindow() in python script]
[[Category:Python Code{{#translation:}}]]
* [https://forum.freecadweb.org/viewtopic.php?t=14912 Acess Scenegraph through python API in without Gui mode]
* [https://forum.freecadweb.org/viewtopic.php?p=190353 FreeCAD Hangs When Called from Python]
* [https://forum.freecadweb.org/viewtopic.php?t=41697 Problem with FreeCADGui]


In the source code there are examples of embedding FreeCAD with various graphical toolkits:
* [https://github.com/FreeCAD/FreeCAD/tree/master/src/Tools/embedded src/Tools/embedded]

{{Powerdocnavi{{#translation:}}}}
[[Category:Python Code{{#translation:}}]]
{{clear}}
{{clear}}

Revision as of 19:48, 15 May 2020

Introduction

Vous savez déjà que vous pouvez 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. 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.

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.

Nous vous montrons ici, 3 méthodes différentes.

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 :

from PySide import QtGui
from PySide 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()

Ensuite, récupérez les View3DInventor de la même façon :

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)

Le code suivant est généré automatiquement, par la création d'un fichier création d'un fichier UI avec QtDesigner, et de le convertir en code Python avec l'outil pyuic:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'mainwindow.ui'
#
# Created: Sun Dec 27 11:18:56 2009
#      by: PySide UI code generator 4.6
#
# Modify for PySide 11/02/2015
#      Python version: 2.7.8
#      Qt version: 4.8.6
#
# WARNING! All changes made in this file will be lost!

from PySide 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))

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.

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

Création d'un examinateur "soGui Examiner Viewer"

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:

from pivy import coin
import FreeCAD as App

box = App.activeDocument().addObject("Part::Box", "myBox")
s = box.ViewObject.toString()  # store as string
inp = coin.SoInput()
inp.setBuffer(s)
myNode = coin.SoDB.readAll(inp)  # restore from string

Ensuite, créez un visualiseur autonome avec 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

Ensuite, il vous suffit de lancer votre visualiseur :

myViewer()

Utilisation d'un module tiers

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
 
###
# 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 PySide import QtCore, QtGui
from PySide.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()

Sans exécuter le Gui de FreeCAD

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.
Il est donc extrêmement facile de mettre en œuvre votre propre visualiseur, et, rester transparent pour les mises à jour de FreeCAD.
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
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()

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 :

#!/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():
    app = QApplication(sys.argv)
    FreeCADGui.setupWithoutGUI()        
    mdi = MdiMainWindow(app)   
    mdi.show()
    sys.exit(app.exec_())
 
 
if __name__ == '__main__':
    main()

Additional information

In the source code there are examples of embedding FreeCAD with various graphical toolkits: