Iterate Through Shapes in FreeCAD with Python
This guide explains how to programmatically access and iterate through all geometric shapes within an open FreeCAD document using Python. By accessing the document's object tree and filtering for elements that contain shape representations, you can inspect geometries, extract sub-elements such as edges or faces, and automate CAD analysis directly from FreeCAD's built-in Python console or external macro scripts.
Accessing Document Objects
In FreeCAD, 3D geometric entities are typically stored as features
containing a Shape property (most commonly derived from
Part::Feature). To access these items, you first reference
the active document and loop through its Objects list.
Because a document can contain non-geometric objects—such as groups,
spreadsheets, or technical drawings—you should check whether each object
possesses a Shape attribute before attempting to interact
with its geometry.
import FreeCAD
# Reference the active document
doc = FreeCAD.ActiveDocument
if doc is None:
print("No active document found.")
else:
# Iterate through all objects in the document
for obj in doc.Objects:
if hasattr(obj, "Shape") and not obj.Shape.isNull():
print(f"Object: {obj.Label} | Shape Type: {obj.Shape.ShapeType}")Filtering with
isDerivedFrom
Alternatively, you can filter objects using the
isDerivedFrom method. This approach ensures you are
specifically targeting elements derived from the
Part::Feature class, which standardizes geometry handling
across the Part and PartDesign workbenches.
import FreeCAD
doc = FreeCAD.ActiveDocument
for obj in doc.Objects:
if obj.isDerivedFrom("Part::Feature"):
shape = obj.Shape
print(f"Found Part Feature: {obj.Name} (Label: {obj.Label})")Iterating Over Sub-Shapes (Faces, Edges, Vertices)
Once you have isolated an object's Shape (a
TopoShape instance), you can access its topological
sub-components using built-in list properties such as
Faces, Edges, Vertexes,
Wires, and Solids.
import FreeCAD
doc = FreeCAD.ActiveDocument
for obj in doc.Objects:
if hasattr(obj, "Shape") and not obj.Shape.isNull():
shape = obj.Shape
print(f"\nProcessing {obj.Label}:")
print(f" - Total Solids: {len(shape.Solids)}")
print(f" - Total Faces: {len(shape.Faces)}")
print(f" - Total Edges: {len(shape.Edges)}")
print(f" - Total Vertices: {len(shape.Vertexes)}")
# Example: Loop over all edges to find total length
total_length = sum(edge.Length for edge in shape.Edges)
print(f" - Total Edge Length: {total_length:.2f} mm")Complete Macro Example
To run a complete script that gathers all shapes and exports basic boundary box metrics to the FreeCAD report view:
import FreeCAD
def analyze_document_shapes():
doc = FreeCAD.ActiveDocument
if not doc:
FreeCAD.Console.PrintError("Error: No document currently open.\n")
return
valid_shapes = []
for obj in doc.Objects:
if hasattr(obj, "Shape") and not obj.Shape.isNull():
valid_shapes.append(obj)
bbox = obj.Shape.BoundBox
FreeCAD.Console.PrintMessage(
f"Shape '{obj.Label}' Dimensions: "
f"X={bbox.XLength:.2f}, Y={bbox.YLength:.2f}, Z={bbox.ZLength:.2f}\n"
)
FreeCAD.Console.PrintMessage(f"Total valid shapes found: {len(valid_shapes)}\n")
analyze_document_shapes()