Create a FeaturePython object part I/de: Difference between revisions

From FreeCAD Documentation
(Created page with "Anfang")
(Updating to match new version of source page)
 
(28 intermediate revisions by 4 users not shown)
Line 1: Line 1:
<languages/>
<languages/>

{{Docnav/de
|
|[[Create_a_FeaturePython_object_part_II/de|Ein FeaturePython-Objekt, erstellen Teil II]]
|IconL=
|IconR=
}}


{{TOCright}}
{{TOCright}}


<span id="Introduction"></span>
==Einführung==
==Einführung==


FunktionPython Objekte (auch als [[Scripted_objects/de|Geskriptete Objekte]] bezeichnet) bieten die Möglichkeit, FreeCAD mit Objekten zu erweitern, die sich nahtlos in das FreeCAD Framework integrieren.
FeaturePython-Objekte (auch als [[Scripted_objects/de|Skriptgenerierte Objekte]] bezeichnet) bieten die Möglichkeit, FreeCAD mit Objekten zu erweitern, die sich nahtlos in die FreeCAD-Struktur integrieren.


Das ermutigt:
Das ermutigt:
*Schnelle Prototypenerstellung von neuen Objekten und Werkzeugen mit benutzerdefinierten Python Klassen.
*Schnelle Prototypenerstellung von neuen Objekten und Werkzeugen mit benutzerdefinierten Python-Klassen.
*Speichern und Wiederherstellen von Daten (auch als Serialisierung bekannt) durch {{incode|App::Property}} Objekte, ohne dass ein Skript in die FreeCAD Dokumentdatei eingebettet wird.
*Speichern und Wiederherstellen von Daten (auch als Serialisierung bekannt) durch {{incode|App::Property}}-Objekte, ohne dass ein Skript in die FreeCAD-Dokumentdatei eingebettet wird.
*Kreative Freiheit, FreeCAD für jede Aufgabe anzupassen.
*Kreative Freiheit, FreeCAD für jede Aufgabe anzupassen.


Auf dieser Seite werden wir ein funktionierendes Beispiel für eine benutzerdefinierte FeaturePython-Klasse konstruieren, wobei wir alle wichtigen Komponenten identifizieren und ein Verständnis dafür gewinnen, wie alles funktioniert, während wir vorankommen.
On this page we are going to construct a working example of a FeaturePython custom class, identifying all the major components and gaining an understanding of how everything works as we go along.


<span id="How_does_it_work?"></span>
==Wie funktioniert es?==
==Wie funktioniert es?==


FreeCAD wird mit einer Reihe von Standard Objekttypen zur Verwaltung verschiedener Arten von Geometrie ausgeliefert. Einige von ihnen haben "FeaturePython" Alternativen, die eine Anpassung mit einer benutzerdefinierten Python Klasse ermöglichen.
FreeCAD wird mit einer Reihe von standard Objekttypen zur Verwaltung verschiedener Arten von Geometrie ausgeliefert. Einige von ihnen haben "FeaturePython"-Alternativen, die eine Anpassung mit einer benutzerdefinierten Python-Klasse ermöglichen.


Diese benutzerdefinierte Python-Klasse nimmt einen Verweis auf eines dieser Objekte und modifiziert es. Zum Beispiel kann die Python-Klasse dem Objekt Eigenschaften hinzufügen oder es mit anderen Objekten verknüpfen. Außerdem kann die Python-Klasse bestimmte Methoden implementieren, um das Objekt in die Lage zu versetzen, auf Dokumentereignisse zu reagieren, wodurch es möglich wird, Änderungen von Objekteigenschaften und Neuberechnungen von Dokumenten abzufangen.
This custom Python class takes a reference to one of these objects and modifies it. For example, the Python class may add properties to the object or link it to other objects. In addition the Python class may implement certain methods to enable the object to respond to document events, making it possible to trap object property changes and document recomputes.


Bei der Arbeit mit benutzerdefinierten Klassen und FeaturePython-Objekten ist es wichtig zu wissen, dass die benutzerdefinierte Klasse und ihr Zustand nicht im Dokument gespeichert werden, da dies die Einbettung eines Skripts in eine FreeCAD-Dokumentdatei erfordern würde, was ein erhebliches Sicherheitsrisiko darstellen würde. Nur das FeaturePython-Objekt selbst wird gespeichert (serialisiert). Da aber der Pfad des Skriptmoduls im Dokument gespeichert ist, muss ein Benutzer nur den Code der benutzerdefinierten Python-Klasse als importierbares Modul installieren und dabei der gleichen Ordnerstruktur folgen, um die verlorene Funktionalität wiederzuerlangen.
When working with custom classes and FeaturePython objects it is important to know that the custom class and its state are not saved in the document as this would require embedding a script in a FreeCAD document file, which would pose a significant security risk. Only the FeaturePython object itself is saved (serialized). But since the script module path is stored in the document, a user need only install the custom Python class code as an importable module, following the same folder structure, to regain the lost functionality.


[[#top|Anfang]]
[[#top|Anfang]]


<span id="Setting_things_up"></span>
==Einrichten der Dinge==
==Einrichten der Dinge==


FeaturePython Object classes need to act as importable modules in FreeCAD. That means you need to place them in a path that exists in your Python environment (or add it specifically). For the purposes of this tutorial, we're going to use the FreeCAD user Macro folder. But if you have another idea in mind, feel free to use that instead.
FeaturePython-Objektklassen müssen in FreeCAD als importierbare Module fungieren. Das bedeutet, dass du sie in einem Pfad platzieren musst, der in deiner Python Umgebung existiert (oder füge ihn speziell hinzu). Für die Zwecke dieses Tutoriums werden wir den FreeCAD-Benutzerordner Macro verwenden. Aber wenn du eine andere Idee im Kopf hast, kannst du diese stattdessen verwenden.


If you don't know where the FreeCAD Macro folder is type {{incode|FreeCAD.getUserMacroDir(True)}} in FreeCAD's [[Python_console|Python console]]:
If you don't know where the FreeCAD Macro folder is type {{incode|FreeCAD.getUserMacroDir(True)}} in FreeCAD's [[Python_console|Python console]]:
* On Linux it is usually {{FileName|/home/<username>/.FreeCAD/Macro/}}.
* On Linux it is usually {{FileName|/home/<username>/.local/share/FreeCAD/Macro/}} ({{VersionPlus|0.20}}) or {{FileName|/home/<username>/.FreeCAD/Macro/}} ({{VersionMinus|0.19}}).
* On Windows it is {{FileName|%APPDATA%\FreeCAD\Macro\}}, which is usually {{FileName|C:\Users\<username>\Appdata\Roaming\FreeCAD\Macro\}}.
* On Windows it is {{FileName|%APPDATA%\FreeCAD\Macro\}}, which is usually {{FileName|C:\Users\<username>\Appdata\Roaming\FreeCAD\Macro\}}.
* On Mac OSX it is usually {{FileName|/Users/<username>/Library/Preferences/FreeCAD/Macro/}}.
* On macOS it is usually {{FileName|/Users/<username>/Library/Application Support/FreeCAD/Macro/}}.


Now we need to create some folders and files:
Now we need to create some folders and files:
Line 57: Line 67:
Finally, navigate to the {{FileName|Macro/fpo/box}} folder and open {{FileName|box.py}} in your favorite code editor. We will only edit that file.
Finally, navigate to the {{FileName|Macro/fpo/box}} folder and open {{FileName|box.py}} in your favorite code editor. We will only edit that file.


[[#top|top]]
[[#top|Anfang]]


<span id="A_FeaturePython_object"></span>
==A FeaturePython object==
==Ein FeaturePython-Objekt==


Let's get started by writing our class and its constructor:
Let's get started by writing our class and its constructor:
Line 124: Line 135:
The {{incode|create()}} method is not required, but it provides a nice way to encapsulate the object creation code.
The {{incode|create()}} method is not required, but it provides a nice way to encapsulate the object creation code.


[[#top|top]]
[[#top|Anfang]]


<span id="Testing_the_code"></span>
===Testing the code===
===Testen des Codes===


Now we can test our new object. Save your code and return to FreeCAD. Make sure you have opened a new document, you can do this by pressing {{KEY|Ctrl}}+{{KEY|N}} or selecting {{MenuCommand|File → New}}.
Now we can test our new object. Save your code and return to FreeCAD. Make sure you have opened a new document, you can do this by pressing {{KEY|Ctrl}}+{{KEY|N}} or selecting {{MenuCommand|File → New}}.
Line 197: Line 209:
[[#top|top]]
[[#top|top]]


<span id="Adding_properties"></span>
===Adding properties===
===Hinzufügen von Eigenschaften===


Properties are the lifeblood of a FeaturePython class. Fortunately, FreeCAD supports [[FeaturePython_Custom_Properties|a number of property types]] for FeaturePython classes. These properties are attached directly to the FeaturePython object and are fully serialized when the file is saved. To avoid having to serialize data yourself, it is advisable to only use these property types.
Properties are the lifeblood of a FeaturePython class. Fortunately, FreeCAD supports [[FeaturePython_Custom_Properties|a number of property types]] for FeaturePython classes. These properties are attached directly to the FeaturePython object and are fully serialized when the file is saved. To avoid having to serialize data yourself, it is advisable to only use these property types.
Line 238: Line 251:
*Select the field and type whatever you like. You'll notice that Python update commands are executed and displayed in the console as you type letters and the property changes.
*Select the field and type whatever you like. You'll notice that Python update commands are executed and displayed in the console as you type letters and the property changes.


[[#top|top]]
[[#top|Anfang]]


Let's add some more properties. Return to your source code and add the following properties to the {{incode|__init__()}} method:
Let's add some more properties. Return to your source code and add the following properties to the {{incode|__init__()}} method:
Line 272: Line 285:
No doubt you noticed that three different values were entered for the dimensions: a floating-point value ({{incode|10.0}}) and two different strings ({{incode|'10 mm'}} and {{incode|'1 cm'}}). The {{incode|App::PropertyLength}} type assumes floating-point values are in millimeters, string values are parsed according to the units specified, and in the GUI all values are converted to the units specified in the user preferences ({{incode|mm}} in the image). This built-in behavior makes the {{incode|App::PropertyLength}} type ideal for dimensions.
No doubt you noticed that three different values were entered for the dimensions: a floating-point value ({{incode|10.0}}) and two different strings ({{incode|'10 mm'}} and {{incode|'1 cm'}}). The {{incode|App::PropertyLength}} type assumes floating-point values are in millimeters, string values are parsed according to the units specified, and in the GUI all values are converted to the units specified in the user preferences ({{incode|mm}} in the image). This built-in behavior makes the {{incode|App::PropertyLength}} type ideal for dimensions.


[[#top|top]]
[[#top|Anfang]]


===Trapping events===
===Trapping events===


The last element required for a basic FeaturePython object is event trapping. A FeaturePython object can react to events with callback functions. In our case we want the object to react whenever it is recomputed. In other words we want to trap recomputes. To accomplish this we need to add a function with a specific name, {{incode|execute()}}, to the object class. There are several other events that can be trapped, both in the FeaturePython object itself and in the [[Viewprovider|ViewProvider]], which we'll cover in the [[Creating_a_FeaturePython_Box,_Part_II|Creating a FeaturePython Box, Part II]].
The last element required for a basic FeaturePython object is event trapping. A FeaturePython object can react to events with callback functions. In our case we want the object to react whenever it is recomputed. In other words we want to trap recomputes. To accomplish this we need to add a function with a specific name, {{incode|execute()}}, to the object class. There are several other events that can be trapped, both in the FeaturePython object itself and in the [[Viewprovider|ViewProvider]], which we'll cover in [[Create_a_FeaturePython_object_part_II|Create a FeaturePython object part II]].


For a complete reference of methods available to implement on FeautrePython classes, see [[FeaturePython_methods|FeaturePython methods]].
For a complete reference of methods available to implement on FeautrePython classes, see [[FeaturePython_methods|FeaturePython methods]].
Line 300: Line 313:
That's it, you now know how to build a basic, functional FeaturePython object!
That's it, you now know how to build a basic, functional FeaturePython object!


[[#top|top]]
[[#top|Anfang]]


<span id="Complete_code"></span>
===Complete code===
===Vollständiger Code===


{{Code|code=
{{Code|code=
Line 344: Line 358:
}}
}}


[[#top|top]]
[[#top|Anfang]]

{{Docnav/de
|
|[[Create_a_FeaturePython_object_part_II/de|Ein FeaturePython-Objekt, erstellen Teil II]]
|IconL=
|IconR=
}}



{{Powerdocnavi{{#translation:}}}}
{{Powerdocnavi{{#translation:}}}}

Latest revision as of 14:11, 17 February 2023

Einführung

FeaturePython-Objekte (auch als Skriptgenerierte Objekte bezeichnet) bieten die Möglichkeit, FreeCAD mit Objekten zu erweitern, die sich nahtlos in die FreeCAD-Struktur integrieren.

Das ermutigt:

  • Schnelle Prototypenerstellung von neuen Objekten und Werkzeugen mit benutzerdefinierten Python-Klassen.
  • Speichern und Wiederherstellen von Daten (auch als Serialisierung bekannt) durch App::Property-Objekte, ohne dass ein Skript in die FreeCAD-Dokumentdatei eingebettet wird.
  • Kreative Freiheit, FreeCAD für jede Aufgabe anzupassen.

Auf dieser Seite werden wir ein funktionierendes Beispiel für eine benutzerdefinierte FeaturePython-Klasse konstruieren, wobei wir alle wichtigen Komponenten identifizieren und ein Verständnis dafür gewinnen, wie alles funktioniert, während wir vorankommen.

Wie funktioniert es?

FreeCAD wird mit einer Reihe von standard Objekttypen zur Verwaltung verschiedener Arten von Geometrie ausgeliefert. Einige von ihnen haben "FeaturePython"-Alternativen, die eine Anpassung mit einer benutzerdefinierten Python-Klasse ermöglichen.

Diese benutzerdefinierte Python-Klasse nimmt einen Verweis auf eines dieser Objekte und modifiziert es. Zum Beispiel kann die Python-Klasse dem Objekt Eigenschaften hinzufügen oder es mit anderen Objekten verknüpfen. Außerdem kann die Python-Klasse bestimmte Methoden implementieren, um das Objekt in die Lage zu versetzen, auf Dokumentereignisse zu reagieren, wodurch es möglich wird, Änderungen von Objekteigenschaften und Neuberechnungen von Dokumenten abzufangen.

Bei der Arbeit mit benutzerdefinierten Klassen und FeaturePython-Objekten ist es wichtig zu wissen, dass die benutzerdefinierte Klasse und ihr Zustand nicht im Dokument gespeichert werden, da dies die Einbettung eines Skripts in eine FreeCAD-Dokumentdatei erfordern würde, was ein erhebliches Sicherheitsrisiko darstellen würde. Nur das FeaturePython-Objekt selbst wird gespeichert (serialisiert). Da aber der Pfad des Skriptmoduls im Dokument gespeichert ist, muss ein Benutzer nur den Code der benutzerdefinierten Python-Klasse als importierbares Modul installieren und dabei der gleichen Ordnerstruktur folgen, um die verlorene Funktionalität wiederzuerlangen.

Anfang

Einrichten der Dinge

FeaturePython-Objektklassen müssen in FreeCAD als importierbare Module fungieren. Das bedeutet, dass du sie in einem Pfad platzieren musst, der in deiner Python Umgebung existiert (oder füge ihn speziell hinzu). Für die Zwecke dieses Tutoriums werden wir den FreeCAD-Benutzerordner Macro verwenden. Aber wenn du eine andere Idee im Kopf hast, kannst du diese stattdessen verwenden.

If you don't know where the FreeCAD Macro folder is type FreeCAD.getUserMacroDir(True) in FreeCAD's Python console:

  • On Linux it is usually /home/<username>/.local/share/FreeCAD/Macro/ (version 0.20 and above) or /home/<username>/.FreeCAD/Macro/ (version 0.19 and below).
  • On Windows it is %APPDATA%\FreeCAD\Macro\, which is usually C:\Users\<username>\Appdata\Roaming\FreeCAD\Macro\.
  • On macOS it is usually /Users/<username>/Library/Application Support/FreeCAD/Macro/.

Now we need to create some folders and files:

  • In the Macro folder create a new folder called fpo.
  • In the fpo folder create an empty file: __init__.py.
  • In the fpo folder, create a new folder called box.
  • In the box folder create two files: __init__.py and box.py (leave both empty for now).

Your folder structure should look like this:

Macro/
    |--> fpo/
        |--> __init__.py
        |--> box/
            |--> __init__.py
            |--> box.py

The fpo folder provides a nice place to play with new FeaturePython objects and the box folder is the module we will be working in. __init__.py tells Python that there is an importable module in the folder, and box.py will be the class file for our new FeaturePython Object.

With our module paths and files created, let's make sure FreeCAD is set up properly:

Finally, navigate to the Macro/fpo/box folder and open box.py in your favorite code editor. We will only edit that file.

Anfang

Ein FeaturePython-Objekt

Let's get started by writing our class and its constructor:

class box():

    def __init__(self, obj):
        """
        Default constructor
        """

        self.Type = 'box'

        obj.Proxy = self

The __init__() method breakdown:

def __init__(self, obj): Parameters refer to the Python class itself and the FeaturePython object that it is attached to.
self.Type = 'box' String definition of the custom Python type.
obj.Proxy = self Stores a reference to the Python instance in the FeaturePython object.

Add the following code at the top of the file:

import FreeCAD as App

def create(obj_name):
    """
    Object creation method
    """

    obj = App.ActiveDocument.addObject('App::FeaturePython', obj_name)

    box(obj)

    return obj

The create() method breakdown:

import FreeCAD as App Standard import for most Python scripts, the App alias is not required.
obj = ... addObject(...) Creates a new FreeCAD FeaturePython object with the name passed to the method. If there is no name clash, this will be the label and the name of the created object. Otherwise, a unique name and label will be created based on 'obj_name'.
box(obj) Creates our custom class instance.
return obj Returns the FeaturePython object.

The create() method is not required, but it provides a nice way to encapsulate the object creation code.

Anfang

Testen des Codes

Now we can test our new object. Save your code and return to FreeCAD. Make sure you have opened a new document, you can do this by pressing Ctrl+N or selecting File → New.

In the Python console type the following:

from fpo.box import box

Now we need to create our object:

mybox = box.create('my_box')

You should see a new object appear in the Tree view labelled "my_box".

Note that the icon is gray. FreeCAD is telling us that the object is not able to display anything in the 3D view. Click on the object and look at its properties in the Property editor. There is not much there, just the name of the object.

Also note that there is a small blue check mark next to the FeaturePython object in the Tree view. That is because when an object is created or changed it is "touched" and needs to be recomputed. Pressing the Std Refresh button will accomplish this. We will add some code to automate this later.

Let's look at our object's attributes:

dir(mybox)

This will return:

['Content', 'Document', 'ExpressionEngine', 'FullName', 'ID', 'InList',
...
'setPropertyStatus', 'supportedProperties', 'touch']

There are a lot of attributes because we're accessing the native FreeCAD FeaturePyton object created in the first line of our create() method. The Proxy property we added in our __init__() method is there too.

Let's inspect it with the dir() method:

dir(mybox.Proxy)

This will return:

['Type', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__',
...
'__str__', '__subclasshook__', '__weakref__']

We can see our Type property. Let's check it:

mybox.Proxy.Type

This will return:

'box'

This is indeed the assigned value, so we know we're accessing the custom class through the FeaturePython object.

Now let's see if we can make our class a little more interesting, and maybe more useful as well.

top

Hinzufügen von Eigenschaften

Properties are the lifeblood of a FeaturePython class. Fortunately, FreeCAD supports a number of property types for FeaturePython classes. These properties are attached directly to the FeaturePython object and are fully serialized when the file is saved. To avoid having to serialize data yourself, it is advisable to only use these property types.

Adding properties is done using the add_property() method. The syntax for the method is:

add_property(type, name, section, description)

You can view the list of supported properties by typing:

mybox.supportedProperties()

Let's try adding a property to our box class. Switch to your code editor, move to the __init__() method, and at the end of the method add:

obj.addProperty('App::PropertyString', 'Description', 'Base', 'Box description').Description = ""

Note how we're using the reference to the (serializable) FeaturePython object obj, and not the (non-serializable) Python class instance self.

Once you're done, save the changes and switch back to FreeCAD. Before we can observe the changes made to our code, we need to reload the module. This can be accomplished by restarting FreeCAD, but restarting FreeCAD every time we edit the code would be inconvenient. To make things easier type the following in the Python console:

from importlib import reload
reload(box)

With the module reloaded, let's see what we get when we create an object:

box.create('box_property_test')

You should see the new box object appear in the Tree view:

  • Select it and look at the Property editor. There, you should see the Description property.
  • Hover over the property name on the left and the tooltip should appear with the description you provided.
  • Select the field and type whatever you like. You'll notice that Python update commands are executed and displayed in the console as you type letters and the property changes.

Anfang

Let's add some more properties. Return to your source code and add the following properties to the __init__() method:

obj.addProperty('App::PropertyLength', 'Length', 'Dimensions', 'Box length').Length = 10.0
obj.addProperty('App::PropertyLength', 'Width', 'Dimensions', 'Box width').Width = '10 mm'
obj.addProperty('App::PropertyLength', 'Height', 'Dimensions', 'Box height').Height = '1 cm'

And let's also add some code to recompute the document automatically. Add the following line above the return() statement in the create() method :

App.ActiveDocument.recompute()

Be careful where you recompute a FeaturePython object. Recomputing should be handled by a method external to its class.

Now, test your changes as follows:

  • Save your changes and reload your module.
  • Delete all objects in the Tree view.
  • Create a new box object from the Python console by calling box.create('myBox').

Once the box is created and you've checked to make sure it has been recomputed, select the object and look at its properties. You should note two things:

  • A new property group: Dimensions.
  • Three new properties: Height, Length and Width.

Note also how the properties have units. More specifically, they have taken on the linear units set in the user preferences (Edit → Preference... → General → Units).

No doubt you noticed that three different values were entered for the dimensions: a floating-point value (10.0) and two different strings ('10 mm' and '1 cm'). The App::PropertyLength type assumes floating-point values are in millimeters, string values are parsed according to the units specified, and in the GUI all values are converted to the units specified in the user preferences (mm in the image). This built-in behavior makes the App::PropertyLength type ideal for dimensions.

Anfang

Trapping events

The last element required for a basic FeaturePython object is event trapping. A FeaturePython object can react to events with callback functions. In our case we want the object to react whenever it is recomputed. In other words we want to trap recomputes. To accomplish this we need to add a function with a specific name, execute(), to the object class. There are several other events that can be trapped, both in the FeaturePython object itself and in the ViewProvider, which we'll cover in Create a FeaturePython object part II.

For a complete reference of methods available to implement on FeautrePython classes, see FeaturePython methods.

Add the following after the __init__() function:

def execute(self, obj):
    """
    Called on document recompute
    """

    print('Recomputing {0:s} ({1:s})'.format(obj.Name, self.Type))

Test the code by again following these steps:

  • Save and reload the module.
  • Delete all objects.
  • Create a new box object.

You should see the printed output in the Python Console, thanks to the recompute() call we added to the create() method. Of course, the execute() method doesn't do anything here, except tell us that it was called, but it is the key to the magic of FeaturePython objects.

That's it, you now know how to build a basic, functional FeaturePython object!

Anfang

Vollständiger Code

import FreeCAD as App

def create(obj_name):
    """
    Object creation method
    """

    obj = App.ActiveDocument.addObject('App::FeaturePython', obj_name)

    box(obj)

    App.ActiveDocument.recompute()

    return obj

class box():

    def __init__(self, obj):
        """
        Default constructor
        """

        self.Type = 'box'

        obj.Proxy = self

        obj.addProperty('App::PropertyString', 'Description', 'Base', 'Box description').Description = ""
        obj.addProperty('App::PropertyLength', 'Length', 'Dimensions', 'Box length').Length = 10.0
        obj.addProperty('App::PropertyLength', 'Width', 'Dimensions', 'Box width').Width = '10 mm'
        obj.addProperty('App::PropertyLength', 'Height', 'Dimensions', 'Box height').Height = '1 cm'

    def execute(self, obj):
        """
        Called on document recompute
        """

        print('Recomputing {0:s} ({1:s})'.format(obj.Name, self.Type))

Anfang