Create a FeaturePython object part I

Introduction
FeaturePython objects (also often referred to as Scripted objects) provide users the ability to extend FreeCAD with objects that integrate seamlessly into the FreeCAD framework.

This encourages:
 * Rapid prototyping of new objects and tools with custom Python classes.
 * Serialization through objects, without embedding any script in the FreeCAD document file.
 * Creative freedom to adapt FreeCAD for any task.

This wiki will provide you with a complete understanding of how to use FeaturePython objects and custom Python classes in FreeCAD. We're going to construct a complete, working example of a FeaturePython custom class, identifying all of the major components and gaining an intimate understanding of how everything works as we go.

How does it work?
FreeCAD comes with a number of default object types for managing different kinds of geometry. Some of them have 'FeaturePython' alternatives that allow for user customization with a custom python class.

The custom python class simply takes a reference to one of these objects and modifies it in any number of ways. For example, the python class may add properties to the object, modify other properties when it's recomputed, 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.

When working with custom classes and FeaturePython objects it is important to know that, when it comes time to save the document, only the FeaturePython object itself is serialized. The custom class and it's 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.

A FeaturePython object ultimately exists entirely apart from it's script. The inconvenience posed by not packing the script with the object in the document file is far less than the risk posed by running a file embedded with an unknown script. However, the script module path is stored in the document file. Therefore, a user need only install the custom python class code as an importable module following the same directory structure to regain the lost functionality.

top

Setting things up
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.

If you don't know where the FreeCAD Macro folder is type in FreeCAD's Python console:
 * On Linux it is usually.
 * On Windows it is, which is usually.
 * On Mac OSX it is usually.

Now we need to create some files:
 * In the folder create a new folder called.
 * In the folder create an empty file:.
 * In the folder, create a  new folder called.
 * In the folder create two files:  and  (leave both empty for now).

Your directory structure should look like this:

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

The folder provides a nice place to play with new FeaturePython objects and the  folder is the module we will be working in. tells Python that there is an importable module in the folder, and 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:
 * Start FreeCAD (if it isn't already open).
 * Enable Report view.
 * Enable Python console see FreeCAD Scripting Basics.

Finally, navigate to the folder and open  in your favorite code editor.

top

A FeaturePython object
Let's get started by writing our class and it's constructor:

The method breakdown:

Still in the file, add the following code at the top:

The method breakdown:

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

top

Testing the code
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 + or selecting.

In the Python console type the following:

Now we need to create our object:

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, for now at least, is not able to display anything in the 3D view. Click on the object and note what appears in the Property editor. There is not very much, just the name of the object. We'll add some properties later.

Let's make referencing our new object a little more convenient:

And then we should take a look at our object's attributes:

This will return:

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

Let's inspect that with the method:

This will return:

We can see our property. This means we're accessing the custom Python object defined in.

Let's check that property:

This will return:

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

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

top

Adding Properties
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 itself and fully serialized when the file is saved. That means, unless you want to serialize the data yourself, you'll need to find some way to wrangle it into a supported property type.

Adding properties is done quite simply using the method. The syntax for the method is:

Let's try adding a property to our box class. Switch to your code editor and move to the method.

Then, at the end of the method, add:

Note how we're using the reference to the (serializable) FeaturePython object,  and not the (non-serializable) Python class instance, . Anyway, once you're done, save the changes and switch back to FreeCAD. Before we can observe the changes we made to our code, we need to reload the module. This can be accomplished by restarting FreeCAD, but restarting FreeCAD everytime we make a change to the python class code can get a bit inconvenient. To make it easier, try the following in the Python console:

This will reload the box module, incorporating changes you made to the file, just as if you'd restarted FreeCAD. With the module reloaded, now let's see what we get when we create an object:



You should see the new box object appear in the Tree view on the left.


 * Select it and look at the Property editor. There, you should see the 'Description' property.
 * Hover over the property name at left and see the tooltip appear with the description text 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.

But before we leave the topic of properties for the moment, let's go back and add some properties that would make a custom box object *really* useful: namely, length, width, and height. Return to your source code and add the following properties:



One last thing: Did you notice how the blue checkmark appears next to the FeaturePython object in the tree view ? That's because when an object is created or changed, it's "touched" and needs to be recomputed. Clicking the "recycle" arrows (the two arrows forming a circle) will accomplish this. But, we can accomplish that automatically by adding the following line to the end of the method:

Now, test your changes as follows:
 * Save your changes and return to FreeCAD.
 * Delete any existing objects and reload your module.
 * Finally, create another box object from the command line by calling

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


 * Three new properties (length, width, and height)
 * A new property group, Dimensions.

Note also how the properties have dimensions. Specifically, they take on the linear dimension of the units set in the user preferences.

In fact, if you were paying attention when you were entering the code, you will have noticed that three separate values were entered for each dimension. The length was a floating-point value (10.0), the width was a string, specifying millimeters ('10 mm') and the height was a string specifying centimeters ('1 cm'). Yet, the property rendered all three values the same way: 10 mm. Specifically, a floating-point value is assumed to be in the current document units, and the string values are parsed according to the units specified, then converted to document units.

The nice thing about the type is that it's a 'unit' type - values are understood as having specific units. Therefore, whenever you create a property that uses linear dimensions, use as the property type.

-

Event Trapping
The last element required for a basic FeaturePython object is event trapping. Specifically, we need to trap the event, which is called when the object is recomputed. There's several other document-level events that can be trapped in our object as well, both in the FeaturePython object itself and in the ViewProvider, which we'll cover in another section.

Add the following after the function:

Test the code as follows:
 * Save changes and reload the box module in the FreeCAD Python console.
 * Delete any objects in the Tree view
 * Re-create the box object.

You should see the printed output in the Python Console, thanks to the call we added to the  method.

Of course, the  method doesn't do anything here (except tell us that it was called), but it is the key to the magic of FeaturePython objects.

So that's it!

You now know how to build a basic, functional FeaturePython object!

-