Embedding FreeCADGui/fr: Difference between revisions
(Updating to match new version of source page) |
(Updating to match new version of source page) |
||
(14 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
<languages/> |
<languages/> |
||
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. |
|||
== Introduction == |
|||
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. |
|||
Il est possible d'[[Embedding FreeCAD/fr|importer le module FreeCAD]] dans une application [[Python/fr|Python]] et d'utiliser tous les outils de l'application hôte. Mais l'interface graphique pour 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 est une construction complexe où plusieurs composants invisibles (tels que le système de sélection, etc.) sont nécessaires pour que la [[3D view/fr|vue 3D]] puisse 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. |
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 14: | ||
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. |
||
Tout d'abord, obtenir la fenêtre principale via |
Tout d'abord, obtenir la fenêtre principale via [[PySide/fr|PySide]]: |
||
{{Code|code= |
|||
<syntaxhighlight> |
|||
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 26: | ||
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|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]]''' : |
|||
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: |
|||
== Conversion de notre boîte de dialogue en code Python avec "pyuic" == |
|||
{{Code|code= |
|||
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 /> |
|||
<syntaxhighlight> |
|||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||
Line 99: | Line 85: | ||
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> |
|||
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 une fenêtre principale qui devrait être la fenêtre principale de votre application. Appliquez-lui la configuration de l'interface utilisateur ci-dessus afin d'ajouter une zone MDI et "déplacez" notre [[3D view/fr|vue 3D]] dessus. |
|||
<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"== |
||
Alternativement, vous pouvez également utiliser le module |
Alternativement, vous pouvez également utiliser le module FreeCADGui pour extraire une représentation OpenInventor (Coin) des objets de votre scène puis utiliser ces données dans une visionneuse externe (votre application). Voici un moyen simple d'obtenir la représentation 3D d'un objet. |
||
{{Code|code= |
|||
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/fr|Pivy]]: |
|||
Voici un moyen facile d'obtenir la représentation 3D d'un objet: |
|||
{{Code|code= |
|||
<syntaxhighlight> |
|||
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 |
|||
</syntaxhighlight> |
|||
Ensuite, créez un visualiseur autonome avec '''pivy''' : |
|||
<syntaxhighlight> |
|||
from pivy.sogui import * |
from pivy.sogui import * |
||
from pivy.coin import * |
from pivy.coin import * |
||
Line 130: | Line 120: | ||
# If unsuccessful, exit. |
# If unsuccessful, exit. |
||
myWindow = SoGui.init(sys.argv[0]) |
myWindow = SoGui.init(sys.argv[0]) |
||
if myWindow == None: |
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 146: | Line 137: | ||
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 |
|||
### |
|||
# 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() |
|||
}} |
|||
</syntaxhighlight> |
|||
== Sans exécuter le Gui de FreeCAD == |
== Sans exécuter le Gui de FreeCAD == |
||
A partir de FreeCAD rev2760, il est maintenant possible d'obtenir la représentation |
A partir de FreeCAD rev2760, (2010, [https://forum.freecadweb.org/viewtopic.php?f=8&t=203&start=20#p1226 1]), il est maintenant possible d'obtenir la représentation des coin de n'importe quel objet FreeCAD sans ouvrir la fenêtre principale. Il est donc extrêmement facile de mettre en œuvre son propre visualiseur et de rester transparent pour les mises à jour de FreeCAD. Après avoir importé {{incode|FreeCADGui}}, vous devez le mettre à niveau avec la méthode {{incode|setupWithoutGUI()}}, après quoi, vous pouvez utiliser tous les fournisseurs de vue de FreeCAD, pour obtenir les nœuds OpenInventor (Coin). |
||
{{Code|code= |
|||
Il est donc extrêmement facile de mettre en œuvre votre propre visualiseur, et, rester transparent pour les mises à jour de FreeCAD.<br /> |
|||
import os, sys, FreeCAD, FreeCADGui |
|||
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'''. |
|||
from PyQt4 import QtCore, QtGui |
|||
<syntaxhighlight> |
|||
from PyQt4.QtGui import QMainWindow, QWorkspace, QAction, QFileDialog, QApplication |
|||
import os, sys, FreeCAD, FreeCADGui |
|||
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): |
|||
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() |
|||
}} |
|||
</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 |
|||
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() |
|||
}} |
|||
== Informations supplémentaires == |
|||
* [https://forum.freecadweb.org/viewtopic.php?f=8&t=203 Embedding a view to another (QT) application?] |
|||
</syntaxhighlight> |
|||
* [https://forum.freecadweb.org/viewtopic.php?t=12575 Using Gui functions without Gui.showMainWindow() in python script] |
|||
* [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] |
|||
Dans le code source, il existe des exemples d'intégration de FreeCAD avec diverses boîtes à outils graphiques: |
|||
[[Category:Poweruser Documentation/fr]] |
|||
* [https://github.com/FreeCAD/FreeCAD/tree/master/src/Tools/embedded src/Tools/embedded] |
|||
[[Category:Python Code/fr]] |
|||
{{Powerdocnavi{{#translation:}}}} |
|||
[[Category:Developer Documentation{{#translation:}}]] |
|||
[[Category:Python Code{{#translation:}}]] |
|||
{{clear}} |
{{clear}} |
Latest revision as of 20:59, 23 August 2020
Introduction
Il est possible d'importer le module FreeCAD dans une application Python et d'utiliser tous les outils de l'application hôte. Mais l'interface graphique pour 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 est une construction complexe où plusieurs composants invisibles (tels que le système de sélection, etc.) sont nécessaires pour que la vue 3D puisse 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 PySide:
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 une fenêtre principale qui devrait être la fenêtre principale de votre application. Appliquez-lui la configuration de l'interface utilisateur ci-dessus afin d'ajouter une zone MDI et "déplacez" notre vue 3D dessus.
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 OpenInventor (Coin) des objets de votre scène puis utiliser ces données dans une visionneuse externe (votre application). Voici un moyen simple 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, (2010, 1), il est maintenant possible d'obtenir la représentation des coin de n'importe quel objet FreeCAD sans ouvrir la fenêtre principale. Il est donc extrêmement facile de mettre en œuvre son propre visualiseur et de rester transparent pour les mises à jour de FreeCAD. Après avoir importé 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 OpenInventor (Coin).
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()
Informations supplémentaires
- Embedding a view to another (QT) application?
- Using Gui functions without Gui.showMainWindow() in python script
- Acess Scenegraph through python API in without Gui mode
- FreeCAD Hangs When Called from Python
- Problem with FreeCADGui
Dans le code source, il existe des exemples d'intégration de FreeCAD avec diverses boîtes à outils graphiques:
- Scripts FreeCAD : Python, Introduction à Python, Tutoriel sur les scripts Python, Débuter avec les scripts
- Modules : Modules intégrés, Unités, Quantity
- Ateliers : Création d'atelier, Commands Gui, Les commandes, Installer des ateliers supplémentaires
- Maillages et objets Parts : Scripts Mesh, Script de données topologiques, Conversion objet Mesh en Part, PythonOCC
- Objets paramétriques : Objets créés par script, Viewproviders (Icône personnalisée dans l'arborescence)
- Scénographie : Graphe de scène Coin (Inventor), Pivy
- Interface graphique : Création d'interface, Création d'une boite de dialogue (1, 2, 3, 4, 5), PySide, Exemples PySide débutant, intermédiaire, expérimenté
- Macros : Macros, Comment installer des macros
- Intégration : Intégrer FreeCAD, Intégration de FreeCADGui
- Autre : Expressions, Extraits de codes, Fonction - tracer une ligne, Bibliothèque mathématique vectorielle de FreeCAD (déprécié)