Create a FeaturePython object part II

Introduction
On the FeaturePython Objects page we've focused on the internal aspects of a Python class built around a FeaturePython object, specifically an object. We've created the object, defined some properties, and added a document-level event callback that allows our object to respond to a document recompute with the method. But our object still lacks a presence in the 3D view. Let's remedy that by adding a box.

Add a box
First at the top of the file, below the existing import, add:

Then in delete the  statement and add the following line in its place:



These commands execute Python methods that come with FreeCAD by default:
 * The method generates a box shape.
 * The enclosing call to adds the shape to the document and makes it visible.

Delete any existing objects, reload the box module and create a new box object using. Notice how a box immediately appears on the screen. That is because we force the document to recompute at the end of which in turn triggers the  method of our  class.

At first sight the result may look fine but there are some problems. The most obvious one is that the box is represented by an entirely different object than our FeaturePython object. simply creates a separate box object and adds it to the document. Worse, if you go to your FeaturePython object and change the dimensions, another box shape gets created and the old one is left in place. And if you have the Report view open, you may have noticed an error stating "Recursive calling of recompute for document Unnamed". This has to do with using the method inside a FeaturePython object.

Fix the code
To solve these problems we have to make a number of changes. Until now we've been using a object which is actually not intended to have a visual representation in the 3D view. We have to use a object instead. In change the following line:

to:

To get rid of the separate box object we need to assigns the result of the method to the  property of our  object. Change this line in :

to:



Save your changes, switch back to FreeCAD, delete any existing objects, reload the box module, and create a new box object. The new result is slightly disappointing. There no longer is an extra object in the Tree view, and the icon in the Tree view has changed, but our box in the 3D view is also gone (which is why the icon is gray). What happened? Although we've properly created our box shape and assigned it to a object, before we can make it show up in the 3D view, we need to assign a ViewProvider.

Write a ViewProvider
A View Provider is the component of an object which allows it to have a visual representation in the 3D view. FreeCAD uses an application structure which is designed to separate the data (the "model") from it's visual representation (the "view"). If you've spent any time working with FreeCAD in Python, you are likely already aware of this through the use of the two core Python modules: and  (often aliased as  and  repectively).

Our FeaturePython object also requires these elements. Thus far we've focused purely on the "model" portion of the code, now it's time to write the "view" portion. Fortunately most ViewProviders are simple and require little effort to write, at least to get started. Here's an example ViewProvider borrowed and slightly modified from :

In the code above, we define an XMP icon for this object. Icon design is beyond the scope of this tutorial, but basic design can be managed using open source tools like GIMP, Krita, and Inkscape. The getIcon method is optional, FreeCAD will use a default icon if this method is not provided.

Add the ViewProvider code at the end of and in the  method insert the following line above the  statement:

This instances the custom ViewProvider class and passes the FeaturePython's built-in ViewObject to it. The ViewObject won't do anything without our custom class implementation, so when the ViewProvider class initializes, it saves a reference to itself in the FeaturePython's ViewObject.Proxy attribute. This way, when FreeCAD needs to render our box visually, it can find the ViewProvider class to do that.

Now, save the changes and return to FreeCAD. Import or reload the box module and call. You should now see two things:
 * The icon for the box object has changed.
 * And, more importantly, there is a box in the 3D view. If you do not see it press the button.

You can even alter the dimensions of the box by changing the values in the Property editor. Give it a try!

Trapping events
Until now we haven't explicitly addressed event trapping. Nearly every method of a FeaturePython class serves as a callback accessible to the FeaturePython object (which gets access to our class instance through the attribute, if you recall).

Below is a list of the callbacks that may be implemented in the basic FeaturePython object:

In addition, there are two callbacks in the ViewProvider class that may occasionally prove useful:

It is not uncommon to encounter a situation where the Python callbacks are not being triggered as they should. Beginners in this area can rest assured that the FeaturePython callback system is not fragile or broken. Invariably, when callbacks fail to run, it is because a reference is lost or undefined in the underlying code. If, however, callbacks appear to be breaking with no explanation, providing object/proxy references in the callback (as noted in the first table above) may alleviate these problems. Until you are comfortable with the callback system, it may be useful to add print statements in each callback to print messages to the console during development.