Embedding FreeCADGui/it: Difference between revisions
Renatorivo (talk | contribs) (Created page with "== Senza avviare la GUI di FreeCAD ==") |
(Created page with "==Introduzione==") |
||
(19 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
<languages/> |
|||
Sapete già che è possibile [[Embedding FreeCAD/it| importare il modulo FreeCAD]] in una applicazione Python e utilizzare tutti i suoi strumenti dall'applicazione che lo ospita, ma l'interfaccia utente (GUI) di FreeCAD può anche essere importata come modulo Python. Normalmente è possibile importare solo, in blocco, l'interfaccia completa, e non le sue singole parti. Questo perché il sistema dell'interfaccia di FreeCAD non è fatto soltanto di widget e di barre degli strumenti indipendenti, ma è una costruzione complessa in cui sono necessari diversi componenti invisibili (come ad esempio il sistema di selezione, ecc) affinchè la vista 3D principale possa funzionare. |
|||
==Introduzione== |
|||
Ma, con un po' di hacking, è possibile importare prima l'intera interfaccia di FreeCAD e poi spostare la vista 3D nella propria applicazione Qt. Qui descriviamo 3 metodi diversi. |
|||
È possibile [[Embedding FreeCAD/it|importare il modulo FreeCAD]] in una applicazione [[Python/it|Python]] e utilizzare tutti i suoi strumenti dall'applicazione che lo ospita, ma l'interfaccia utente (GUI) di FreeCAD può anche essere importata come modulo Python. Normalmente è possibile importare solo, in blocco, l'interfaccia completa, e non le sue singole parti. Questo perché il sistema dell'interfaccia di FreeCAD non è fatto soltanto di widget e di barre degli strumenti indipendenti, ma è una costruzione complessa in cui sono necessari diversi componenti invisibili (come ad esempio il sistema di selezione, ecc) affinchè la vista 3D principale possa funzionare. |
|||
Ma, con un po' di hacking, è possibile importare prima l'intera interfaccia di FreeCAD e poi spostare la vista 3D nella propria applicazione Qt. Qui sono descritti 3 metodi diversi. |
|||
==Utilizzando direttamente il componente aggiuntivo (Widget) di FreeCAD per la vista 3D== |
==Utilizzando direttamente il componente aggiuntivo (Widget) di FreeCAD per la vista 3D== |
||
Line 7: | Line 11: | ||
Tenete presente che con questo approccio ci sono molti problemi. L'elaborazione di Qt sembra non funzionare (nessuna idea sul perché) e quando si utilizza il menu contestuale della vista 3D l'applicazione si blocca. Un approccio migliore potrebbe essere quello di creare il proprio SoQtExaminerViewer o SoQtViewer della vista 3D e "spingere" il contenuto della vista FreeCAD 3D, come mostrato più avanti, nelle altre sezioni. |
Tenete presente che con questo approccio ci sono molti problemi. L'elaborazione di Qt sembra non funzionare (nessuna idea sul perché) e quando si utilizza il menu contestuale della vista 3D l'applicazione si blocca. Un approccio migliore potrebbe essere quello di creare il proprio SoQtExaminerViewer o SoQtViewer della vista 3D e "spingere" il contenuto della vista FreeCAD 3D, come mostrato più avanti, nelle altre sezioni. |
||
In primo luogo, ottenere la finestra principale via |
In primo luogo, ottenere la finestra principale via [[PySide/it|PySide]]: |
||
{{Code|code= |
|||
<syntaxhighlight> |
|||
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() |
|||
}} |
|||
</syntaxhighlight> |
|||
Poi ottenere la vista View3DInventor nello stesso modo: |
Poi ottenere la vista View3DInventor nello stesso modo: |
||
{{Code|code= |
|||
<syntaxhighlight> |
|||
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) |
|||
}} |
|||
</syntaxhighlight> |
|||
Il seguente codice viene generato automaticamente, tramite la [[Dialog_creation/it|creazione di una interfaccia grafica di QtDesigner]], e convertito in codice python con lo strumento pyuic: |
Il seguente codice viene generato automaticamente, tramite la [[Dialog_creation/it|creazione di una interfaccia grafica di QtDesigner]], e convertito in codice python con lo strumento pyuic: |
||
{{Code|code= |
|||
<syntaxhighlight> |
|||
# -*- 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 |
|||
# WARNING! All changes made in this file will be lost! |
|||
# Python version: 2.7.8 |
|||
# Qt version: 4.8.6 |
|||
# |
|||
# WARNING! All changes made in this file will be lost! |
|||
class Ui_MainWindow(object): |
|||
def setupUi(self, MainWindow): |
|||
from PySide import QtCore, QtGui |
|||
MainWindow.setObjectName("MainWindow") |
|||
MainWindow.resize(508, 436) |
|||
class Ui_MainWindow(object): |
|||
self.centralwidget = QtGui.QWidget(MainWindow) |
|||
def setupUi(self, MainWindow): |
|||
self.centralwidget.setObjectName("centralwidget") |
|||
MainWindow.setObjectName("MainWindow") |
|||
self.gridLayout = QtGui.QGridLayout(self.centralwidget) |
|||
MainWindow.resize(508, 436) |
|||
self.gridLayout.setObjectName("gridLayout") |
|||
self.centralwidget = QtGui.QWidget(MainWindow) |
|||
self.centralwidget.setObjectName("centralwidget") |
|||
self.gridLayout = QtGui.QGridLayout(self.centralwidget) |
|||
self.mdiArea.setTabPosition(QtGui.QTabWidget.South) |
|||
self.gridLayout.setObjectName("gridLayout") |
|||
self.mdiArea = QtGui.QMdiArea(self.centralwidget) |
|||
self.mdiArea.setViewMode(QtGui.QMdiArea.TabbedView) |
|||
MainWindow.setCentralWidget(self.centralwidget) |
|||
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.setStatusBar(self.statusbar) |
|||
MainWindow.setMenuBar(self.menubar) |
|||
self.statusbar = QtGui.QStatusBar(MainWindow) |
|||
self.statusbar.setObjectName("statusbar") |
|||
QtCore.QMetaObject.connectSlotsByName(MainWindow) |
|||
MainWindow.setStatusBar(self.statusbar) |
|||
def retranslateUi(self, MainWindow): |
|||
self.retranslateUi(MainWindow) |
|||
MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8)) |
|||
QtCore.QMetaObject.connectSlotsByName(MainWindow) |
|||
</syntaxhighlight> |
|||
Quindi, creare una finestra principale che dovrebbe essere la finestra principale dell'applicazione, applicarvi sopra la configurazione dell'interfaccia utente per aggiungere una zona MDI e "spostare" in essa la vista 3D |
|||
def retranslateUi(self, MainWindow): |
|||
<syntaxhighlight> |
|||
MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8)) |
|||
ui=Ui_MainWindow() |
|||
}} |
|||
my_mw=QtGui.QMainWindow() |
|||
ui.setupUi(my_mw) |
|||
Quindi, creare una finestra principale che dovrebbe essere la finestra principale dell'applicazione, applicarvi sopra la configurazione dell'interfaccia utente per aggiungere una zona MDI e "spostare" in essa la vista 3D. |
|||
ui.mdiArea.addSubWindow(v) |
|||
{{Code|code= |
|||
my_mw.show() |
|||
ui = Ui_MainWindow() |
|||
</syntaxhighlight> |
|||
my_mw = QtGui.QMainWindow() |
|||
ui.setupUi(my_mw) |
|||
ui.mdiArea.addSubWindow(v) |
|||
my_mw.show() |
|||
}} |
|||
Creazione di un esaminatore "soGui Viewer" |
Creazione di un esaminatore "soGui Viewer" |
||
In alternativa, è possibile utilizzare anche il modulo FreeCADGui per estrarre una rappresentazione |
In alternativa, è possibile utilizzare anche il modulo FreeCADGui per estrarre una rappresentazione OpenInventor (Coin) degli oggetti di scena, quindi utilizzare tali dati coin in un visualizzatore esterno (la propria applicazione). Ecco un modo facile per ottenere la rappresentazione 3D di un oggetto. |
||
{{Code|code= |
|||
<syntaxhighlight> |
|||
from pivy import coin |
|||
FreeCAD.activeDocument().addObject("Part::Box","myBox") |
|||
import FreeCAD as App |
|||
s=FreeCADGui.activeDocument().getObject("myBox").toString() # store as string |
|||
from pivy import coin |
|||
box = App.activeDocument().addObject("Part::Box", "myBox") |
|||
inp.setBuffer(s) |
|||
s = box.ViewObject.toString() # store as string |
|||
inp = coin.SoInput() |
|||
</syntaxhighlight> |
|||
inp.setBuffer(s) |
|||
Quindi, creare un visualizzatore autonomo con pivy: |
|||
myNode = coin.SoDB.readAll(inp) # restore from string |
|||
<syntaxhighlight> |
|||
}} |
|||
from pivy.sogui import * |
|||
from pivy.coin import * |
|||
Quindi, creare un visualizzatore autonomo con [[Pivy/it|Pivy]]: |
|||
import sys |
|||
{{Code|code= |
|||
from pivy.sogui import * |
|||
def myViewer(): |
|||
from pivy.coin import * |
|||
# Initialize Coin. This returns a main window to use. |
|||
import sys |
|||
# If unsuccessful, exit. |
|||
myWindow = SoGui.init(sys.argv[0]) |
|||
def myViewer(): |
|||
if myWindow == None: sys.exit(1) |
|||
# Initialize Coin. This returns a main window to use. |
|||
# If unsuccessful, exit. |
|||
# Make an empty scene and add our node to it |
|||
myWindow = SoGui.init(sys.argv[0]) |
|||
if myWindow == None: |
|||
scene.addChild(myNode) |
|||
sys.exit(1) |
|||
# Create a viewer in which to see our scene graph. |
|||
# Make an empty scene and add our node to it |
|||
viewer = SoGuiExaminerViewer(myWindow) |
|||
scene = SoSeparator() |
|||
scene.addChild(myNode) |
|||
# Put our scene into viewer, change the title |
|||
viewer.setSceneGraph(scene) |
|||
# Create a viewer in which to see our scene graph. |
|||
viewer.setTitle("FreeCAD Object Viewer") |
|||
viewer = SoGuiExaminerViewer(myWindow) |
|||
viewer.show() |
|||
# Put our scene into viewer, change the title |
|||
SoGui.show(myWindow) # Display main window |
|||
viewer.setSceneGraph(scene) |
|||
SoGui.mainLoop() # Main Coin event loop |
|||
viewer.setTitle("FreeCAD Object Viewer") |
|||
</syntaxhighlight> |
|||
viewer.show() |
|||
SoGui.show(myWindow) # Display main window |
|||
SoGui.mainLoop() # Main Coin event loop |
|||
}} |
|||
Poi basta eseguire il proprio visualizzatore: |
Poi basta eseguire il proprio visualizzatore: |
||
{{Code|code= |
|||
<syntaxhighlight> |
|||
myViewer() |
|||
}} |
|||
</syntaxhighlight> |
|||
==Utlizzando il modulo Quarter== |
==Utlizzando il modulo Quarter== |
||
Invece di utilizzare il visualizzatore soGui, è possibile utilizzare il più moderno modulo Quarter. Delle tre soluzioni questa è probabilmente la migliore. |
Invece di utilizzare il visualizzatore soGui, è possibile utilizzare il più moderno modulo Quarter. Delle tre soluzioni questa è probabilmente la migliore. |
||
{{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(app) |
|||
mdi = MdiMainWindow(QtGui.qApp) |
|||
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_()) |
|||
if __name__ == '__main__': |
|||
def show(): |
|||
main() |
|||
mdi = MdiMainWindow(QtGui.qApp) |
|||
}} |
|||
mdi.show() |
|||
mw=getMainWindow() |
|||
#mw.hide() # hide all |
|||
if __name__ == '__main__': |
|||
main() |
|||
</syntaxhighlight> |
|||
== Senza avviare la GUI di FreeCAD == |
== Senza avviare la GUI di FreeCAD == |
||
A partire da FreeCAD rev2760 (2010, [https://forum.freecadweb.org/viewtopic.php?f=8&t=203&start=20#p1226 1]), è possibile avere la rappresentazione coin di qualsiasi oggetto di FreeCAD senza aprire la finestra principale. Questo rende estremamente facile implementare il proprio visualizzatore e avere FreeCAD aggiornato. Dopo aver importato il modulo FreeCADGui, è necessario attivarlo con il metodo {{incode|setupWithoutGUI()}}, dopo di che è possibile utilizzare tutti i fornitori di viste di FreeCAD per ottenere i nodi OpenInventor (Coin). |
|||
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. |
|||
{{Code|code= |
|||
<syntaxhighlight> |
|||
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() |
|||
}} |
|||
</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: |
|||
Oppure, se utilizzando il modulo sogui di pivy per voi non funziona (il modulo sogui sta diventando obsoleto e gli sviluppatori di coin ora stanno favorendo la nuova libreria Quarter che ha una migliore interazione con qt), questo è lo stesso script, ma utilizzando Quarter: |
|||
<syntaxhighlight> |
|||
{{Code|code= |
|||
#!/usr/bin/env python |
|||
#!/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() |
|||
FreeCADGui.setupWithoutGUI() |
|||
app = QApplication(sys.argv) |
|||
mdi = MdiMainWindow(app) |
|||
mdi.show() |
|||
sys.exit(app.exec_()) |
|||
if __name__ == '__main__': |
|||
main() |
|||
}} |
|||
== Informazioni aggiuntive == |
|||
</syntaxhighlight> |
|||
* [https://forum.freecadweb.org/viewtopic.php?f=8&t=203 Embedding a view to another (QT) application?] |
|||
[[Category:Poweruser Documentation/it]] |
|||
* [https://forum.freecadweb.org/viewtopic.php?t=12575 Using Gui functions without Gui.showMainWindow() in python script] |
|||
[[Category:Python Code/it]] |
|||
* [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] |
|||
Nel codice sorgente ci sono esempi di incorporamento di FreeCAD con vari toolkit grafici: |
|||
* [https://github.com/FreeCAD/FreeCAD/tree/master/src/Tools/embedded src/Tools/embedded] |
|||
{{Powerdocnavi{{#translation:}}}} |
|||
[[Category:Developer Documentation{{#translation:}}]] |
|||
[[Category:Python Code{{#translation:}}]] |
|||
{{clear}} |
{{clear}} |
||
<languages/> |
Latest revision as of 21:21, 15 September 2022
Introduzione
È possibile importare il modulo FreeCAD in una applicazione Python e utilizzare tutti i suoi strumenti dall'applicazione che lo ospita, ma l'interfaccia utente (GUI) di FreeCAD può anche essere importata come modulo Python. Normalmente è possibile importare solo, in blocco, l'interfaccia completa, e non le sue singole parti. Questo perché il sistema dell'interfaccia di FreeCAD non è fatto soltanto di widget e di barre degli strumenti indipendenti, ma è una costruzione complessa in cui sono necessari diversi componenti invisibili (come ad esempio il sistema di selezione, ecc) affinchè la vista 3D principale possa funzionare.
Ma, con un po' di hacking, è possibile importare prima l'intera interfaccia di FreeCAD e poi spostare la vista 3D nella propria applicazione Qt. Qui sono descritti 3 metodi diversi.
Utilizzando direttamente il componente aggiuntivo (Widget) di FreeCAD per la vista 3D
Tenete presente che con questo approccio ci sono molti problemi. L'elaborazione di Qt sembra non funzionare (nessuna idea sul perché) e quando si utilizza il menu contestuale della vista 3D l'applicazione si blocca. Un approccio migliore potrebbe essere quello di creare il proprio SoQtExaminerViewer o SoQtViewer della vista 3D e "spingere" il contenuto della vista FreeCAD 3D, come mostrato più avanti, nelle altre sezioni.
In primo luogo, ottenere la finestra 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()
Poi ottenere la vista View3DInventor nello stesso modo:
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)
Il seguente codice viene generato automaticamente, tramite la creazione di una interfaccia grafica di QtDesigner, e convertito in codice python con lo strumento 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))
Quindi, creare una finestra principale che dovrebbe essere la finestra principale dell'applicazione, applicarvi sopra la configurazione dell'interfaccia utente per aggiungere una zona MDI e "spostare" in essa la vista 3D.
ui = Ui_MainWindow()
my_mw = QtGui.QMainWindow()
ui.setupUi(my_mw)
ui.mdiArea.addSubWindow(v)
my_mw.show()
Creazione di un esaminatore "soGui Viewer"
In alternativa, è possibile utilizzare anche il modulo FreeCADGui per estrarre una rappresentazione OpenInventor (Coin) degli oggetti di scena, quindi utilizzare tali dati coin in un visualizzatore esterno (la propria applicazione). Ecco un modo facile per ottenere la rappresentazione 3D di un oggetto.
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
Quindi, creare un visualizzatore autonomo con 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
Poi basta eseguire il proprio visualizzatore:
myViewer()
Utlizzando il modulo Quarter
Invece di utilizzare il visualizzatore soGui, è possibile utilizzare il più moderno modulo Quarter. Delle tre soluzioni questa è probabilmente la migliore.
#!/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()
Senza avviare la GUI di FreeCAD
A partire da FreeCAD rev2760 (2010, 1), è possibile avere la rappresentazione coin di qualsiasi oggetto di FreeCAD senza aprire la finestra principale. Questo rende estremamente facile implementare il proprio visualizzatore e avere FreeCAD aggiornato. Dopo aver importato il modulo FreeCADGui, è necessario attivarlo con il metodo setupWithoutGUI()
, dopo di che è possibile utilizzare tutti i fornitori di viste di FreeCAD per ottenere i nodi 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()
Oppure, se utilizzando il modulo sogui di pivy per voi non funziona (il modulo sogui sta diventando obsoleto e gli sviluppatori di coin ora stanno favorendo la nuova libreria Quarter che ha una migliore interazione con qt), questo è lo stesso script, ma utilizzando 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():
app = QApplication(sys.argv)
FreeCADGui.setupWithoutGUI()
mdi = MdiMainWindow(app)
mdi.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Informazioni aggiuntive
- 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
Nel codice sorgente ci sono esempi di incorporamento di FreeCAD con vari toolkit grafici:
- FreeCAD scripting: Python, Introduction to Python, Python scripting tutorial, FreeCAD Scripting Basics
- Modules: Builtin modules, Units, Quantity
- Workbenches: Workbench creation, Gui Commands, Commands, Installing more workbenches
- Meshes and Parts: Mesh Scripting, Topological data scripting, Mesh to Part, PythonOCC
- Parametric objects: Scripted objects, Viewproviders (Custom icon in tree view)
- Scenegraph: Coin (Inventor) scenegraph, Pivy
- Graphical interface: Interface creation, Interface creation completely in Python (1, 2, 3, 4, 5), PySide, PySide examples beginner, intermediate, advanced
- Macros: Macros, How to install macros
- Embedding: Embedding FreeCAD, Embedding FreeCADGui
- Other: Expressions, Code snippets, Line drawing function, FreeCAD vector math library (deprecated)
- Hubs: User hub, Power users hub, Developer hub