Create Custom Parametric Features in FreeCAD Python
This guide provides a practical walkthrough for building custom
parametric features in FreeCAD using its Python Feature framework
(FeaturePython). You will learn how to create a custom
document object, attach a Python proxy class to define dynamic
properties, implement recompute algorithms using the Part
module, and bind view providers to handle 3D visualization inside
FreeCAD.
Understanding the FeaturePython Architecture
FreeCAD separates an object's functional data from its graphical representation. A custom parametric feature relies on two core components:
- Document Object (
Part::FeaturePython): The C++ object that lives inside the FreeCAD document tree, holds data properties, and serializes to the file. - Proxy Class: A standard Python class attached to
the document object's
Proxyattribute. It handles property definitions, state changes, and geometry calculation during document recomputations.
To support the graphical interface, a corresponding
ViewProvider Proxy attaches to the object's graphical
node (ViewObject) to define icons, display modes, and color
properties.
Step 1: Define the Feature Proxy Class
The proxy class requires an initialization method to define
user-editable properties and an execute method that FreeCAD
runs whenever a property changes.
import FreeCAD as App
import Part
class ParametricBox:
def __init__(self, obj):
"""Initialize the custom object and attach properties."""
obj.Proxy = self
# Add custom parametric properties: (Type, Name, Group, Tooltip)
obj.addProperty("App::PropertyLength", "Length", "Dimensions", "Length of the box").Length = 20.0
obj.addProperty("App::PropertyLength", "Width", "Dimensions", "Width of the box").Width = 15.0
obj.addProperty("App::PropertyLength", "Height", "Dimensions", "Height of the box").Height = 10.0
def execute(self, obj):
"""Called by FreeCAD whenever the object or its dependencies recompute."""
# Validate values to prevent invalid geometry
if obj.Length <= 0 or obj.Width <= 0 or obj.Height <= 0:
return
# Generate geometry using the Part kernel
obj.Shape = Part.makeBox(obj.Length, obj.Width, obj.Height)Step 2: Define the ViewProvider Proxy
To integrate with the 3D scene and the tree view, define a view provider class:
class ViewProviderParametricBox:
def __init__(self, vobj):
"""Initialize the view provider."""
vobj.Proxy = self
def getIcon(self):
"""Return the XPM or path to an SVG icon (optional)."""
return """
/* XPM */
static const char *box_icon[] = {
"16 16 2 1",
" c None",
". c #0000FF",
" .......... ",
" . .. ",
" . . . ",
".......... . ",
". . . ",
". . . ",
". . .. ",
". .. . ",
".......... . ",
" . . . ",
" . .. ",
" .......... ",
" ",
" ",
" ",
" "
};
"""
def attach(self, vobj):
"""Setup default display modes."""
self.ViewObject = vobj
return
def updateData(self, fp, prop):
"""Called when a property of the data object changes."""
return
def getDisplayModes(self, vobj):
"""Return available display modes."""
return ["Shaded", "Wireframe", "Points"]
def getDefaultDisplayMode(self):
return "Shaded"
def setDisplayMode(self, mode):
return modeStep 3: Instantiate and Register the Feature
Combine the data object and view provider inside an execution script or workbench tool:
def create_parametric_box(doc=None):
if doc is None:
doc = App.ActiveDocument or App.newDocument()
# Create the core geometry object
obj = doc.addObject("Part::FeaturePython", "ParametricBox")
ParametricBox(obj)
# Attach the GUI view provider if running with graphical interface
if App.GuiUp:
import FreeCADGui as Gui
ViewProviderParametricBox(obj.ViewObject)
doc.recompute()
return obj
# Run the creation
if __name__ == "__main__":
create_parametric_box()Key Properties Reference
FreeCAD provides standard property types that determine how values appear in the Data tab:
App::PropertyLength: Floating-point value rendered with unit suffixes (e.g.,mm,in).App::PropertyAngle: Value representing an angle, automatically converted to degrees/radians.App::PropertyInteger: Standard integer property.App::PropertyBool: Boolean checkbox.App::PropertyString: Text input field.App::PropertyLink: Allows the feature to reference another document object as an input.App::PropertyEnumeration: Dropdown list of string choices.
Serialization and Persistence
When saving and reloading documents, Python objects must handle serialization:
- Ensure the Python module containing your class is in FreeCAD's Python path (e.g., standard macros folder or an active Workbench directory).
- Avoid storing non-serializable objects (such as open file handles or
complex transient states) directly in
self. Always read from and write to theobjproperties, as FreeCAD automatically serializesobj.PropertiesList.