Diese Seite zeigt, wie man einen neuen Arbeitsbereich zur FreeCAD-Oberfläche hinzufügt. Arbeitbereiche sind Behälter für FreeCAD-Befehle. Sie können in Python, in C++ oder in einer Mischung aus beiden programmiert werden, was den Vorteil hat, die Geschwindigkeit von C++ mit der Flexibilität von Python zu verbinden. In jedem Fall wird der neue Arbeitsbereich durch einen Satz von zwei Python-Dateien gestartet. Dies können "interne" Arbeitsbereiche sein, die im FreeCAD-Paket enthalten sind oder "externe" Arbeitsbereiche, die mit dem Addon-Manager erhältlich sind oder sie werden manuell über das Herunterladen einiger Quellen (repositories) aus dem Netz installiert. Interne Arbeitsbereiche können in C++, Python oder einer Kombination der beiden programmiert sein, wogegen externe Arbeitsbereiche nur in Python programmiert sein dürfen.
Du benötigst einen Ordner mit irgendeinem Namen den du magst, platziert im Mod-Verzeichnis des Benutzers, mit einer Init.py
-Datei und wahlweise, einer InitGui.py
-Datei. Die Init-Datei wird immer ausgeführt, wenn FreeCAD startet und die InitGui.py
-Datei wird unmittelbar danach ausgeführt wird, aber nur, wenn FreeCAD im GUI-Modus startet. Das ist alles, was FreeCAD braucht, um deinen Arbeitsbereich beim Start zu finden und ihn in seine Oberfläche aufzunehmen.
Das Mod-Verzeichnis des Benutzers ist ein Unterverzeichnis des Anwendungsdaten-Verzeichnisses des Benutzers (letzteres findet man durch Eingabe von App.getUserAppDataDir()
in der Python-Konsole):
Das Mod-Verzeichnis sollte so aussehen:
/Mod/
+-- MyWorkbench/
+-- Init.py
+-- InitGui.py
Innerhalb dieser Dateien kannst du tun, was immer du willst. Normalerweise werden sie so verwendet:
Die hier beschriebene Struktur und der Dateiinhalt eines Arbeitsbereich sind der klassische Weg beim Anlegen eines Arbeitsbereichs. Man kann eine leichte Abwandlung in der Dateistruktur benutzen, wenn ein neuer Python-Arbeitsbereich erstellt werden soll; dieser alternative Weg kann am besten als "namespaced workbench" (Arbeitsbereich mit eigenem Namensraum) bezeichnet werden, wodurch die Möglichkeit eröffnet wird, pip zur Installation des neuen Arbeitsbereichs zu verwenden. Das Aussehen und die Struktur von hier gezeigten Arbeitsbereichen sind im globalen Namensraum von FreeCAD enthalten, während sich das alternative Aussehen und die Struktur des Arbeitsbereichs in einem dedizierten Namensraum befindet.
Weitere Informationen zu dem Thema findest du unter Verwandtes.
Wenn du deinen Arbeitsbereich in Python programmieren willst, brauchst du keine besondere Vorsicht walten zu lassen und kannst einfach deine anderen Python-Dateien zusammen mit deinen Init.py- und InitGui.py-Dateien unterbringen. Wenn du jedoch mit C++ arbeitest, solltest du größere Sorgfalt walten lassen und eine grundlegende Regel von FreeCAD beachten: die Trennung deines Arbeitsbereichs zwischen einem Anwendungsteil (der ohne jedes GUI-Element im Konsolenmodus laufen kann) und einem GUI-Teil, der nur geladen wird, wenn FreeCAD mit seiner vollständigen GUI-Umgebung läuft. Wenn du also einen C++-Arbeitsbereich erstellst, wirst du höchstwahrscheinlich zwei Module erstellen, eine Anwendung und eine GUI. Diese beiden Module müssen natürlich von Python aus aufrufbar sein. Jedes FreeCAD-Modul (Anwendung oder GUI) besteht mindestens aus einer Modul-Init-Datei. Dies ist eine typische AppMyModuleGui.cpp-Datei:
extern "C" {
void MyModuleGuiExport initMyModuleGui()
{
if (!Gui::Application::Instance) {
PyErr_SetString(PyExc_ImportError, "Cannot load Gui module in console application.");
return;
}
try {
// import other modules this one depends on
Base::Interpreter().runString("import PartGui");
// run some python code in the console
Base::Interpreter().runString("print('welcome to my module!')");
}
catch(const Base::Exception& e) {
PyErr_SetString(PyExc_ImportError, e.what());
return;
}
(void) Py_InitModule("MyModuleGui", MyModuleGui_Import_methods); /* mod name, table ptr */
Base::Console().Log("Loading GUI of MyModule... done\n");
// initializes the FreeCAD commands (in another cpp file)
CreateMyModuleCommands();
// initializes workbench and object definitions
MyModuleGui::Workbench::init();
MyModuleGui::ViewProviderSomeCustomObject::init();
// add resources and reloads the translators
loadMyModuleResource();
}
}
"""FreeCAD init script of XXX module"""
# ***************************************************************************
# * Copyright (c) 2015 John Doe john@doe.com *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENSE text file. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with FreeCAD; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************/
FreeCAD.addImportType("My own format (*.own)", "importOwn")
FreeCAD.addExportType("My own format (*.own)", "exportOwn")
print("I am executing some stuff here when FreeCAD starts!")
Du kannst jede beliebige Lizenz für deinen Arbeitsbereich wählen, aber sei dir bewusst, dass, wenn du deinen Arbeitsbereich irgendwann in den FreeCAD-Quellcode integrierst und mit diesem verteilt sehen möchtest, diese wie im obigen Beispiel unter LGPL2+ stehen muss. Siehe Lizenz.
Die Funktionen FreeCAD.addImportType()
und addEXportType()
ermöglichen es dir, den Namen und die Erweiterung eines Dateityps sowie ein für seinen Import verantwortliches Python-Modul anzugeben. Im obigen Beispiel wird ein Modul importOwn.py
.own
-Dateien verarbeiten.
Siehe Codeschnipsel für weitere Beispiele.
Dies ist die Datei InitGui.py:
class MyWorkbench (Workbench):
MenuText = "My Workbench"
ToolTip = "A description of my workbench"
Icon = """paste here the contents of a 16x16 xpm icon"""
def Initialize(self):
"""This function is executed when the workbench is first activated.
It is executed once in a FreeCAD session followed by the Activated function.
"""
import MyModuleA, MyModuleB # import here all the needed files that create your FreeCAD commands
self.list = ["MyCommand1", "MyCommand2"] # a list of command names created in the line above
self.appendToolbar("My Commands", self.list) # creates a new toolbar with your commands
self.appendMenu("My New Menu", self.list) # creates a new menu
self.appendMenu(["An existing Menu", "My submenu"], self.list) # appends a submenu to an existing menu
def Activated(self):
"""This function is executed whenever the workbench is activated"""
return
def Deactivated(self):
"""This function is executed whenever the workbench is deactivated"""
return
def ContextMenu(self, recipient):
"""This function is executed whenever the user right-clicks on screen"""
# "recipient" will be either "view" or "tree"
self.appendContextMenu("My commands", self.list) # add commands to the context menu
def GetClassName(self):
# This function is mandatory if this is a full Python workbench
# This is not a template, the returned string should be exactly "Gui::PythonWorkbench"
return "Gui::PythonWorkbench"
Gui.addWorkbench(MyWorkbench())
Ansonsten kannst du alles tun, was du willst: Du kannst deinen gesamten Code des Arbeitsbereichs in die InitGui.py speichern, wenn du möchtest, aber es ist normalerweise bequemer, die verschiedenen Funktionen deines Arbeitsbereiches in separate Dateien abzulegen. Dadurch sind diese Dateien kleiner und einfacher zu lesen. Dann importierst du diese Dateien in deine InitGui.py-Datei. Du kannst diese Dateien anordnen wie du willst; ein gutes Beispiel ist eine für jeden FreeCAD-Befehl, den du hinzufügst.
Du kannst eine Einstellungsseite für deinen Python-Arbeitsbereich hinzufügen. Die Einstellungsseiten suchen nach einem Einstellungssymbol mit einem bestimmten Namen im Qt-Ressourcensystem. Wenn sich dein Symbol nicht im Ressourcensystem befindet oder nicht den korrekten Namen hat, wird dein Symbol nicht auf der Einstellungsseite erscheinen.
Hinzufügen deines Arbeitsbereichssymbols:
Du musst du die Schritte erneut ausführen wenn du Symbole hinzufügst/änderst,
@kbwbe hat ein nettes Skript zum Kompilieren von Ressourcen für den Arbeitsbereich A2Plus erstellt. Siehe unten.
Hinzufügen deiner Einstellungsseite(n):
Um einen eigenen Python-Arbeitsbereich zu veröffentlichen (und zu verbreiten), können die Dateien einfach an einem bestimmten Ort bereitgestellt und die Anwender angeleitet werden, wie die Dateien herunterzuladen und manuell in ihrem Mod-Verzeichnis abzulegen sind oder sie können in einem Online-git-Datenspeicher (GitHub, GitLab, Framagit und Debian Salsa werden zurzeit unterstützt) bereitgestellt und für die Installation mit dem Addon-Manager konfiguriert werden. Anweisungen zum Einbinden in FreeCADs offizieller Addon-Liste befinden sich unter FreeCAD Addons GitHub repository (engl.). Um den Addon-Manager zu verwenden, sollte eine Metadaten-Datei package.xml enthalten sein, die dem Addon-Manager zeigt, wo das Symbol des Arbeitsbereiches zu finden ist und die die Anzeige von Beschreibung, Versionsnummer und anderem ermöglicht. Sie kann auch verwendet werden, um andere Arbeitsbereiche oder Python-Pakete anzugeben, von denen der eigene Arbeitsbereich abhängt, die ihn blockieren oder die er ersetzen soll.
Für eine Kurzanleitung wie man eine einfache package.xml-Datei erstellt und einen Arbeitsbereich zum Addon-Manager hinzufügt, siehe: Arbeitsbereich zum Addon-Manager hinzufügen.
Wahlweise kann eine separate Metadaten-Datei enthalten sein, die die eigenen Python-Abhängigkeiten beschreibt. Dies kann eine Datei metadata.txt sein, die die externen Abhängigkeiten des eigenen Arbeitsbereichs beschreibt (von entweder anderen Addons, Arbeitsbereichen oder Python-Modulen) oder eine Datei requirements.txt, die die Python-Abhängigkeiten beschreibt. Man beachte, dass wenn eine requirements.txt-Datei verwendet wird, nur die Namen der angegebenen Pakete zum Auflösen der Abhängigkeiten eingesetzt werden: pip-Befehlsoptionen, Include-Optionen und Versionsinformationen werden vom Addon-Manager nicht unterstützt. Anwender können die requirements-Datei manuell mit pip ausführen, wenn diese Elemente erforderlich sind.
Das Format der Datei metadata.txt ist einfacher Text und enthält drei optionalen Zeilen:
workbenches=
pylibs=
optionalpylibs=
Jede Zeile sollte aus einer durch Komma getrennten Liste von Elementen bestehen, von der der eigene Arbeitsbereich abhängt. Arbeitsbereiche können entweder ein interner FreeCAD-Arbeitsbereich, z.B. "FEM", oder ein externes Addon, z.B. "Curves" sein. Die benötigten oder wählbaren Python-Bibliotheken sollten mit ihren regelkonformen Python-Namen benannt werden, wie sie auch mit pip install
verwendet werden. Zum Beispiel:
workbenches=FEM,Curves
pylibs=ezdxf
optionalpylibs=metadata,git
Du kannst auch ein Skript einfügen, das gestartet wird, wenn dein Paket deinstalliert wird. Das wäre die Datei "uninstall.py", die ganz oben in deinem Addon zu finden wäre. Es wird ausgeführt, wenn ein Benutzer dein Addon mit dem Addon-Manager deinstalliert. Es wird verwendet, um alles, was das Addon auf dem Benutzersystem gemacht hat und nicht dort bleiben soll, zu entfernen, wenn das Addon deinstalliert ist, z.B. Cachedateien entfernen etc.
Um zu gewährleisten, dass dein Addon vom Addon-Manager richtig gelesen werden kann, kann der "developer mode" aktiviert werden, in dem der Addon-Manager alle verfügbaren Addons prüft und gewährleistet, dass deren Metadaten die erforderlichen Elemente enthält. Diesen Modus aktiviert man durch Auswahl von Bearbeiten → Einstellungen... → Addon-Manager → Addon-Manager-Optionen → Addon-Entwickler-Modus, siehe Voreinstellungseditor.
Wenn du deinen Arbeitsbereich in C++ programmierst, wirst du wahrscheinlich die Arbeitsbereichsdefinition selbst auch in C++ programmieren (obwohl es nicht notwendig ist: du könntest auch nur die Werkzeuge in C++ programmieren und die Arbeitsbereichsdefinition in Python belassen). In diesem Fall, wird die Datei InitGui.py sehr einfach: Sie könnte nur eine Zeile enthalten:
import MyModuleGui
wobei MyModule dein vollständiger C++ Arbeitsbereich ist, der die Befehle und Arbeitsbereichsdefinition einschließt.
Die Programmierung von C++ Arbeitsbereichen funktioniert auf ziemlich ähnliche Weise. Dies ist eine typische Workbench.cpp-Datei, die du in den Gui-Teil deines Moduls aufnehmen kannst:
namespace MyModuleGui {
class MyModuleGuiExport Workbench : public Gui::StdWorkbench
{
TYPESYSTEM_HEADER();
public:
Workbench();
virtual ~Workbench();
virtual void activated();
virtual void deactivated();
protected:
Gui::ToolBarItem* setupToolBars() const;
Gui::MenuItem* setupMenuBar() const;
};
}
Du kannst auch eine Voreinstellungsseite für C++ Arbeitsbereiche hinzufügen. Die Schritte sind ähnlich wie die für Python.
Es gibt zwei Optionen zum Veröffentlichen (und verbreiten) eines C++ -Arbeitsbereiches; es können eigene vorkompilierte Versionen für die unterschiedlichen Betriebssysteme bereitgestellt werden oder man fragt das Einbinden in den FreeCADs Quellcode an. Wie oben erwähnt, erfordert dies eine LGPL2+ -Lizenz und man muss den Arbeitsbereich der Gemeinschaft im FreeCAD-Forum zur Bewertung vorstellen.
FreeCAD-Befehle sind die Grundbausteine der FreeCAD-Oberfläche. Sie können als Schaltflächen in Symbolleisten und als Einträge in Menüs erscheinen. Es handelt sich dabei immer um denselben Befehl. Ein Befehl ist einfach eine Python-Klasse, die eine Reihe von vordefinierten Attributen und Funktionen enthält, wie der Befehlsname, das Symbol und der Code, der ausgeführt wird, wenn der Befehl aktiviert wird.
class My_Command_Class():
"""My new command"""
def GetResources(self):
return {"Pixmap" : "My_Command_Icon", # the name of a svg file available in the resources
"Accel" : "Shift+S", # a default shortcut (optional)
"MenuText": "My New Command",
"ToolTip" : "What my new command does"}
def Activated(self):
"""Do something here"""
return
def IsActive(self):
"""Here you can define if the command must be active or not (greyed) if certain conditions
are met or not. This function is optional."""
return True
FreeCADGui.addCommand("My_Command", My_Command_Class())
In ähnlicher Weise können Befehle in C++ programmiert werden, normalerweise in einer Commands.cpp-Datei in deinem GUI-Modul. Dies ist eine typische Commands.cpp-Datei:
DEF_STD_CMD_A(CmdMyCommand);
CmdMyCommand::CmdMyCommand()
:Command("My_Command")
{
sAppModule = "MyModule";
sGroup = QT_TR_NOOP("MyModule");
sMenuText = QT_TR_NOOP("Runs my command...");
sToolTipText = QT_TR_NOOP("Describes what my command does");
sWhatsThis = QT_TR_NOOP("Describes what my command does");
sStatusTip = QT_TR_NOOP("Describes what my command does");
sPixmap = "some_svg_icon_from_my_resource";
}
void CmdMyCommand::activated(int iMsg)
{
openCommand("My Command");
doCommand(Doc,"print('Hello, world!')");
commitCommand();
updateActive();
}
bool CmdMyCommand::isActive(void)
{
if( getActiveGuiDocument() )
return true;
else
return false;
}
void CreateMyModuleCommands(void)
{
Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
rcCmdMgr.addCommand(new CmdMyCommand());
}
compileA2pResources.py aus dem Arbeitsbereich A2Plus:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#***************************************************************************
#* *
#* Copyright (c) 2019 kbwbe *
#* *
#* Portions of code based on hamish's assembly 2 *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENSE text file. *
#* *
#* This program is distributed in the hope that it will be useful, *
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
#* GNU Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
# This script compiles the A2plus icons for py2 and py3
# For Linux only
# Start this file in A2plus main directory
# Make sure pyside-rcc is installed
import os, glob
qrc_filename = 'temp.qrc'
if os.path.exists(qrc_filename):
os.remove(qrc_filename)
qrc = '''<RCC>
\t<qresource prefix="/">'''
for fn in glob.glob('./icons/*.svg'):
qrc = qrc + '\n\t\t<file>%s</file>' % fn
qrc = qrc + '''\n\t</qresource>
</RCC>'''
print(qrc)
f = open(qrc_filename,'w')
f.write(qrc)
f.close()
os.system(
'pyside-rcc -o a2p_Resources2.py {}'.format(qrc_filename))
os.system(
'pyside-rcc -py3 -o a2p_Resources3.py {}'.format(qrc_filename))
os.remove(qrc_filename)