Creating a FeaturePython Box, Part II

App::FeaturePython vs. Part::FeaturePython
Up to this pint we've focused on the internal aspects of a Python class built around a FeaturePython object - specifically, an App::FeaturePython object. We've created the object, defined some properties, and added some document-level event callbacks that allow our object to respond to a document recompute with the  method.

But we still don't have a box.

We can create one easily just by adding two lines. First, add a new import to the top of the file:

Then, in, add the following line (you can delete the   statement):

def execute: '''    Called on document recompute '''    Part.show(Part.makeBox(obj.Length, obj.Width, obj.Height)

These commands execute python scripts that come with FreeCAD as a default. The  method generates a new box Shape. The enclosing call to  adds the Shape to the document tree and makes it visible.

If you have FreeCAD open, reload the box module and create a new box object using  (delete any existing box objects, just to keep things clean).

Notice how a box immediately appears on the screen.

That's because the execute method is called immediately after the box is created, because we force the document to recompute at the end of

But there's a problem.

It should be pretty obvious. The box itself is represented by an entirely different object than our FeaturePython object. The reason is because  creates a separate Part::Shape object for the document and assigns the shape created by   to it.

In fact, if you go to your FeaturePython object and change the dimensions, you'll see another box shape gets created and the old one is left in place. That's not good!

What we want, of course, is a single object that represents a box and resizes the existing box as we change it's properties. We could delete the previous Shape every time we change a property, but we still have two objects - our FeaturePython object and the Shape it generates.

So how do we solve this problem?

First, we need to use the right type of object. To this pint we've been using  objects. They're great, but they're not intended for use as geometry objects. They're document objects which are meant to do things that don't require a visual in the 3D view. We'll talk more about that later.

For now, we just need to make the following change in.

Change:

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

to:

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

Then, in the execute method, change the line:

Part.show(Part.makeBox(obj.Length, obj.Width, obj.Height))

to:

obj.Shape = Part.makeBox(obj.Length, obj.Width, obj.Height)

Your code should look like this:

import FreeCAD as App import Part def create(obj_name): """  Object creation method   """ obj = App.ActiveDocument.addObject('Part::FeaturePython', obj_name) fpo = box(obj) App.ActiveDocument.recompute return fpo class box: def __init__(self, obj): """      Default Constructor       """ self.Type = 'box' 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' obj.Proxy = self self.Object = obj def execute(self, obj): """      Called on document recompute       """ obj.Shape = Part.makeBox(obj.Length, obj.Width, obj.Height)

Now, save your changes and switch back to FreeCAD. Delete any existing objects, reload the box module, and create a new box object.

Now take a look at the object in the document tree. See any differences?

The first thing you should see is that the icon changed. It's a cube now.

The second thing you should see (or rather, not see) is the cube. It's not there. And the icon is still gray.

What happened?

The answer is, while we've properly created our box shape and assigned it to a Part::FeaturePython object, before we can make it show up in our 3D view, we need to assign a ViewProvider.

Writing a ViewProvider
A View Provider is the component of an object which allows it to have visual representation in the GUI - specifically in the 3D view. FreeCAD maintains a model-view structure internally - the data is separate from it's visual representation. If you've spent any time working with FreeCAD in Python, you'll likely already be aware of this through the two Python modules: FreeCAD and FreeCADGui (often aliased as 'App' and 'Gui' repectively).