freecad-scripts
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFreeCAD Scripts
FreeCAD 脚本
Expert skill for generating production-quality Python scripts for the FreeCAD CAD application. Interprets shorthand, quasi-code, and natural language descriptions of 3D modeling tasks and translates them into correct FreeCAD Python API calls.
用于为FreeCAD CAD应用生成生产级Python脚本的专业技能。可解析3D建模任务的简写、伪代码和自然语言描述,并将其转换为正确的FreeCAD Python API调用。
When to Use This Skill
何时使用该技能
- Writing Python scripts for FreeCAD's built-in console or macro system
- Creating or manipulating 3D geometry (Part, Mesh, Sketcher, Path, FEM)
- Building parametric FeaturePython objects with custom properties
- Developing GUI tools using PySide/Qt within FreeCAD
- Manipulating the Coin3D scenegraph via Pivy
- Creating custom workbenches or Gui Commands
- Automating repetitive CAD operations with macros
- Converting between mesh and solid representations
- Scripting FEM analyses, raytracing, or drawing exports
- 为FreeCAD内置控制台或宏系统编写Python脚本
- 创建或操作3D几何图形(Part、Mesh、Sketcher、Path、FEM)
- 构建带有自定义属性的参数化FeaturePython对象
- 在FreeCAD中使用PySide/Qt开发GUI工具
- 通过Pivy操作Coin3D场景图
- 创建自定义工作台或Gui命令
- 使用宏自动化重复的CAD操作
- 在网格和实体表示形式之间转换
- 编写FEM分析、光线追踪或绘图导出的脚本
Prerequisites
前置要求
- FreeCAD installed (0.19+ recommended; 0.21+/1.0+ for latest API)
- Python 3.x (bundled with FreeCAD)
- For GUI work: PySide2 (bundled with FreeCAD)
- For scenegraph: Pivy (bundled with FreeCAD)
- 已安装FreeCAD(推荐0.19+版本;最新API需0.21+/1.0+版本)
- Python 3.x(FreeCAD已捆绑)
- 用于GUI开发:PySide2(FreeCAD已捆绑)
- 用于场景图开发:Pivy(FreeCAD已捆绑)
FreeCAD Python Environment
FreeCAD Python环境
FreeCAD embeds a Python interpreter. Scripts run in an environment where these key modules are available:
python
import FreeCAD # Core module (also aliased as 'App')
import FreeCADGui # GUI module (also aliased as 'Gui') — only in GUI mode
import Part # Part workbench — BRep/OpenCASCADE shapes
import Mesh # Mesh workbench — triangulated meshes
import Sketcher # Sketcher workbench — 2D constrained sketches
import Draft # Draft workbench — 2D drawing tools
import Arch # Arch/BIM workbench
import Path # Path/CAM workbench
import FEM # FEM workbench
import TechDraw # TechDraw workbench (replaces Drawing)
import BOPTools # Boolean operations
import CompoundTools # Compound shape utilitiesFreeCAD嵌入了Python解释器。脚本运行环境中可使用以下核心模块:
python
import FreeCAD # Core module (also aliased as 'App')
import FreeCADGui # GUI module (also aliased as 'Gui') — only in GUI mode
import Part # Part workbench — BRep/OpenCASCADE shapes
import Mesh # Mesh workbench — triangulated meshes
import Sketcher # Sketcher workbench — 2D constrained sketches
import Draft # Draft workbench — 2D drawing tools
import Arch # Arch/BIM workbench
import Path # Path/CAM workbench
import FEM # FEM workbench
import TechDraw # TechDraw workbench (replaces Drawing)
import BOPTools # Boolean operations
import CompoundTools # Compound shape utilitiesThe FreeCAD Document Model
FreeCAD文档模型
python
undefinedpython
undefinedCreate or access a document
Create or access a document
doc = FreeCAD.newDocument("MyDoc")
doc = FreeCAD.ActiveDocument
doc = FreeCAD.newDocument("MyDoc")
doc = FreeCAD.ActiveDocument
Add objects
Add objects
box = doc.addObject("Part::Box", "MyBox")
box.Length = 10.0
box.Width = 10.0
box.Height = 10.0
box = doc.addObject("Part::Box", "MyBox")
box.Length = 10.0
box.Width = 10.0
box.Height = 10.0
Recompute
Recompute
doc.recompute()
doc.recompute()
Access objects
Access objects
obj = doc.getObject("MyBox")
obj = doc.MyBox # Attribute access also works
obj = doc.getObject("MyBox")
obj = doc.MyBox # Attribute access also works
Remove objects
Remove objects
doc.removeObject("MyBox")
undefineddoc.removeObject("MyBox")
undefinedCore Concepts
核心概念
Vectors and Placements
向量与位置
python
import FreeCADpython
import FreeCADVectors
Vectors
v1 = FreeCAD.Vector(1, 0, 0)
v2 = FreeCAD.Vector(0, 1, 0)
v3 = v1.cross(v2) # Cross product
d = v1.dot(v2) # Dot product
v4 = v1 + v2 # Addition
length = v1.Length # Magnitude
v_norm = FreeCAD.Vector(v1)
v_norm.normalize() # In-place normalize
v1 = FreeCAD.Vector(1, 0, 0)
v2 = FreeCAD.Vector(0, 1, 0)
v3 = v1.cross(v2) # Cross product
d = v1.dot(v2) # Dot product
v4 = v1 + v2 # Addition
length = v1.Length # Magnitude
v_norm = FreeCAD.Vector(v1)
v_norm.normalize() # In-place normalize
Rotations
Rotations
rot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 45) # axis, angle(deg)
rot = FreeCAD.Rotation(0, 0, 45) # Euler angles (yaw, pitch, roll)
rot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 45) # axis, angle(deg)
rot = FreeCAD.Rotation(0, 0, 45) # Euler angles (yaw, pitch, roll)
Placements (position + orientation)
Placements (position + orientation)
placement = FreeCAD.Placement(
FreeCAD.Vector(10, 20, 0), # translation
FreeCAD.Rotation(0, 0, 45), # rotation
FreeCAD.Vector(0, 0, 0) # center of rotation
)
obj.Placement = placement
placement = FreeCAD.Placement(
FreeCAD.Vector(10, 20, 0), # translation
FreeCAD.Rotation(0, 0, 45), # rotation
FreeCAD.Vector(0, 0, 0) # center of rotation
)
obj.Placement = placement
Matrix (4x4 transformation)
Matrix (4x4 transformation)
import math
mat = FreeCAD.Matrix()
mat.move(FreeCAD.Vector(10, 0, 0))
mat.rotateZ(math.radians(45))
undefinedimport math
mat = FreeCAD.Matrix()
mat.move(FreeCAD.Vector(10, 0, 0))
mat.rotateZ(math.radians(45))
undefinedCreating and Manipulating Geometry (Part Module)
几何创建与操作(Part模块)
The Part module wraps OpenCASCADE and provides BRep solid modeling:
python
import FreeCAD
import PartPart模块封装了OpenCASCADE,提供BRep实体建模能力:
python
import FreeCAD
import Part--- Primitive Shapes ---
--- Primitive Shapes ---
box = Part.makeBox(10, 10, 10) # length, width, height
cyl = Part.makeCylinder(5, 20) # radius, height
sphere = Part.makeSphere(10) # radius
cone = Part.makeCone(5, 2, 10) # r1, r2, height
torus = Part.makeTorus(10, 2) # major_r, minor_r
box = Part.makeBox(10, 10, 10) # length, width, height
cyl = Part.makeCylinder(5, 20) # radius, height
sphere = Part.makeSphere(10) # radius
cone = Part.makeCone(5, 2, 10) # r1, r2, height
torus = Part.makeTorus(10, 2) # major_r, minor_r
--- Wires and Edges ---
--- Wires and Edges ---
edge1 = Part.makeLine((0, 0, 0), (10, 0, 0))
edge2 = Part.makeLine((10, 0, 0), (10, 10, 0))
edge3 = Part.makeLine((10, 10, 0), (0, 0, 0))
wire = Part.Wire([edge1, edge2, edge3])
edge1 = Part.makeLine((0, 0, 0), (10, 0, 0))
edge2 = Part.makeLine((10, 0, 0), (10, 10, 0))
edge3 = Part.makeLine((10, 10, 0), (0, 0, 0))
wire = Part.Wire([edge1, edge2, edge3])
Circles and arcs
Circles and arcs
circle = Part.makeCircle(5) # radius
arc = Part.makeCircle(5, FreeCAD.Vector(0, 0, 0),
FreeCAD.Vector(0, 0, 1), 0, 180) # start/end angle
circle = Part.makeCircle(5) # radius
arc = Part.makeCircle(5, FreeCAD.Vector(0, 0, 0),
FreeCAD.Vector(0, 0, 1), 0, 180) # start/end angle
--- Faces ---
--- Faces ---
face = Part.Face(wire) # From a closed wire
face = Part.Face(wire) # From a closed wire
--- Solids from Faces/Wires ---
--- Solids from Faces/Wires ---
extrusion = face.extrude(FreeCAD.Vector(0, 0, 10)) # Extrude
revolved = face.revolve(FreeCAD.Vector(0, 0, 0),
FreeCAD.Vector(0, 0, 1), 360) # Revolve
extrusion = face.extrude(FreeCAD.Vector(0, 0, 10)) # Extrude
revolved = face.revolve(FreeCAD.Vector(0, 0, 0),
FreeCAD.Vector(0, 0, 1), 360) # Revolve
--- Boolean Operations ---
--- Boolean Operations ---
fused = box.fuse(cyl) # Union
cut = box.cut(cyl) # Subtraction
common = box.common(cyl) # Intersection
fused_clean = fused.removeSplitter() # Clean up seams
fused = box.fuse(cyl) # Union
cut = box.cut(cyl) # Subtraction
common = box.common(cyl) # Intersection
fused_clean = fused.removeSplitter() # Clean up seams
--- Fillets and Chamfers ---
--- Fillets and Chamfers ---
filleted = box.makeFillet(1.0, box.Edges) # radius, edges
chamfered = box.makeChamfer(1.0, box.Edges) # dist, edges
filleted = box.makeFillet(1.0, box.Edges) # radius, edges
chamfered = box.makeChamfer(1.0, box.Edges) # dist, edges
--- Loft and Sweep ---
--- Loft and Sweep ---
loft = Part.makeLoft([wire1, wire2], True) # wires, solid
swept = Part.Wire([path_edge]).makePipeShell([profile_wire],
True, False) # solid, frenet
loft = Part.makeLoft([wire1, wire2], True) # wires, solid
swept = Part.Wire([path_edge]).makePipeShell([profile_wire],
True, False) # solid, frenet
--- BSpline Curves ---
--- BSpline Curves ---
from FreeCAD import Vector
points = [Vector(0,0,0), Vector(1,2,0), Vector(3,1,0), Vector(4,3,0)]
bspline = Part.BSplineCurve()
bspline.interpolate(points)
edge = bspline.toShape()
from FreeCAD import Vector
points = [Vector(0,0,0), Vector(1,2,0), Vector(3,1,0), Vector(4,3,0)]
bspline = Part.BSplineCurve()
bspline.interpolate(points)
edge = bspline.toShape()
--- Show in document ---
--- Show in document ---
Part.show(box, "MyBox") # Quick display (adds to active doc)
Part.show(box, "MyBox") # Quick display (adds to active doc)
Or explicitly:
Or explicitly:
doc = FreeCAD.ActiveDocument or FreeCAD.newDocument()
obj = doc.addObject("Part::Feature", "MyShape")
obj.Shape = box
doc.recompute()
undefineddoc = FreeCAD.ActiveDocument or FreeCAD.newDocument()
obj = doc.addObject("Part::Feature", "MyShape")
obj.Shape = box
doc.recompute()
undefinedTopological Exploration
拓扑结构探索
python
shape = obj.Shapepython
shape = obj.ShapeAccess sub-elements
Access sub-elements
shape.Vertexes # List of Vertex objects
shape.Edges # List of Edge objects
shape.Wires # List of Wire objects
shape.Faces # List of Face objects
shape.Shells # List of Shell objects
shape.Solids # List of Solid objects
shape.Vertexes # List of Vertex objects
shape.Edges # List of Edge objects
shape.Wires # List of Wire objects
shape.Faces # List of Face objects
shape.Shells # List of Shell objects
shape.Solids # List of Solid objects
Bounding box
Bounding box
bb = shape.BoundBox
print(bb.XMin, bb.XMax, bb.YMin, bb.YMax, bb.ZMin, bb.ZMax)
print(bb.Center)
bb = shape.BoundBox
print(bb.XMin, bb.XMax, bb.YMin, bb.YMax, bb.ZMin, bb.ZMax)
print(bb.Center)
Properties
Properties
shape.Volume
shape.Area
shape.Length # For edges/wires
face.Surface # Underlying geometric surface
edge.Curve # Underlying geometric curve
shape.Volume
shape.Area
shape.Length # For edges/wires
face.Surface # Underlying geometric surface
edge.Curve # Underlying geometric curve
Shape type
Shape type
shape.ShapeType # "Solid", "Shell", "Face", "Wire", "Edge", "Vertex", "Compound"
undefinedshape.ShapeType # "Solid", "Shell", "Face", "Wire", "Edge", "Vertex", "Compound"
undefinedMesh Module
Mesh模块
python
import Meshpython
import MeshCreate mesh from vertices and facets
Create mesh from vertices and facets
mesh = Mesh.Mesh()
mesh.addFacet(
0.0, 0.0, 0.0, # vertex 1
1.0, 0.0, 0.0, # vertex 2
0.0, 1.0, 0.0 # vertex 3
)
mesh = Mesh.Mesh()
mesh.addFacet(
0.0, 0.0, 0.0, # vertex 1
1.0, 0.0, 0.0, # vertex 2
0.0, 1.0, 0.0 # vertex 3
)
Import/Export
Import/Export
mesh = Mesh.Mesh("/path/to/file.stl")
mesh.write("/path/to/output.stl")
mesh = Mesh.Mesh("/path/to/file.stl")
mesh.write("/path/to/output.stl")
Convert Part shape to Mesh
Convert Part shape to Mesh
import Part
import MeshPart
shape = Part.makeBox(1, 1, 1)
mesh = MeshPart.meshFromShape(Shape=shape, LinearDeflection=0.1,
AngularDeflection=0.5)
import Part
import MeshPart
shape = Part.makeBox(1, 1, 1)
mesh = MeshPart.meshFromShape(Shape=shape, LinearDeflection=0.1,
AngularDeflection=0.5)
Convert Mesh to Part shape
Convert Mesh to Part shape
shape = Part.Shape()
shape.makeShapeFromMesh(mesh.Topology, 0.05) # tolerance
solid = Part.makeSolid(shape)
undefinedshape = Part.Shape()
shape.makeShapeFromMesh(mesh.Topology, 0.05) # tolerance
solid = Part.makeSolid(shape)
undefinedSketcher Module
Sketcher模块
Create a sketch on XY plane
—
sketch = doc.addObject("Sketcher::SketchObject", "MySketch")
sketch.Placement = FreeCAD.Placement(
FreeCAD.Vector(0, 0, 0),
FreeCAD.Rotation(0, 0, 0, 1)
)
python
undefinedAdd geometry (returns geometry index)
Create a sketch on XY plane
idx_line = sketch.addGeometry(Part.LineSegment(
FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(10, 0, 0)))
idx_circle = sketch.addGeometry(Part.Circle(
FreeCAD.Vector(5, 5, 0), FreeCAD.Vector(0, 0, 1), 3))
sketch = doc.addObject("Sketcher::SketchObject", "MySketch")
sketch.Placement = FreeCAD.Placement(
FreeCAD.Vector(0, 0, 0),
FreeCAD.Rotation(0, 0, 0, 1)
)
Add constraints
Add geometry (returns geometry index)
sketch.addConstraint(Sketcher.Constraint("Coincident", 0, 2, 1, 1))
sketch.addConstraint(Sketcher.Constraint("Horizontal", 0))
sketch.addConstraint(Sketcher.Constraint("DistanceX", 0, 1, 0, 2, 10.0))
sketch.addConstraint(Sketcher.Constraint("Radius", 1, 3.0))
sketch.addConstraint(Sketcher.Constraint("Fixed", 0, 1))
idx_line = sketch.addGeometry(Part.LineSegment(
FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(10, 0, 0)))
idx_circle = sketch.addGeometry(Part.Circle(
FreeCAD.Vector(5, 5, 0), FreeCAD.Vector(0, 0, 1), 3))
Constraint types: Coincident, Horizontal, Vertical, Parallel, Perpendicular,
Add constraints
Tangent, Equal, Symmetric, Distance, DistanceX, DistanceY, Radius, Angle,
—
Fixed (Block), InternalAlignment
—
doc.recompute()
undefinedsketch.addConstraint(Sketcher.Constraint("Coincident", 0, 2, 1, 1))
sketch.addConstraint(Sketcher.Constraint("Horizontal", 0))
sketch.addConstraint(Sketcher.Constraint("DistanceX", 0, 1, 0, 2, 10.0))
sketch.addConstraint(Sketcher.Constraint("Radius", 1, 3.0))
sketch.addConstraint(Sketcher.Constraint("Fixed", 0, 1))
Draft Module
Constraint types: Coincident, Horizontal, Vertical, Parallel, Perpendicular,
—
Tangent, Equal, Symmetric, Distance, DistanceX, DistanceY, Radius, Angle,
—
Fixed (Block), InternalAlignment
python
import Draft
import FreeCADdoc.recompute()
undefined2D shapes
Draft模块
line = Draft.makeLine(FreeCAD.Vector(0,0,0), FreeCAD.Vector(10,0,0))
circle = Draft.makeCircle(5)
rect = Draft.makeRectangle(10, 5)
poly = Draft.makePolygon(6, radius=5) # hexagon
python
import Draft
import FreeCADOperations
2D shapes
moved = Draft.move(obj, FreeCAD.Vector(10, 0, 0), copy=True)
rotated = Draft.rotate(obj, 45, FreeCAD.Vector(0,0,0),
axis=FreeCAD.Vector(0,0,1), copy=True)
scaled = Draft.scale(obj, FreeCAD.Vector(2,2,2), center=FreeCAD.Vector(0,0,0),
copy=True)
offset = Draft.offset(obj, FreeCAD.Vector(1,0,0))
array = Draft.makeArray(obj, FreeCAD.Vector(15,0,0),
FreeCAD.Vector(0,15,0), 3, 3)
undefinedline = Draft.makeLine(FreeCAD.Vector(0,0,0), FreeCAD.Vector(10,0,0))
circle = Draft.makeCircle(5)
rect = Draft.makeRectangle(10, 5)
poly = Draft.makePolygon(6, radius=5) # hexagon
Creating Parametric Objects (FeaturePython)
Operations
FeaturePython objects are custom parametric objects with properties that trigger recomputation:
python
import FreeCAD
import Part
class MyBox:
"""A custom parametric box."""
def __init__(self, obj):
obj.Proxy = self
obj.addProperty("App::PropertyLength", "Length", "Dimensions",
"Box length").Length = 10.0
obj.addProperty("App::PropertyLength", "Width", "Dimensions",
"Box width").Width = 10.0
obj.addProperty("App::PropertyLength", "Height", "Dimensions",
"Box height").Height = 10.0
def execute(self, obj):
"""Called on document recompute."""
obj.Shape = Part.makeBox(obj.Length, obj.Width, obj.Height)
def onChanged(self, obj, prop):
"""Called when a property changes."""
pass
def __getstate__(self):
return None
def __setstate__(self, state):
return None
class ViewProviderMyBox:
"""View provider for custom icon and display settings."""
def __init__(self, vobj):
vobj.Proxy = self
def getIcon(self):
return ":/icons/Part_Box.svg"
def attach(self, vobj):
self.Object = vobj.Object
def updateData(self, obj, prop):
pass
def onChanged(self, vobj, prop):
pass
def __getstate__(self):
return None
def __setstate__(self, state):
return Nonemoved = Draft.move(obj, FreeCAD.Vector(10, 0, 0), copy=True)
rotated = Draft.rotate(obj, 45, FreeCAD.Vector(0,0,0),
axis=FreeCAD.Vector(0,0,1), copy=True)
scaled = Draft.scale(obj, FreeCAD.Vector(2,2,2), center=FreeCAD.Vector(0,0,0),
copy=True)
offset = Draft.offset(obj, FreeCAD.Vector(1,0,0))
array = Draft.makeArray(obj, FreeCAD.Vector(15,0,0),
FreeCAD.Vector(0,15,0), 3, 3)
undefined--- Usage ---
创建参数化对象(FeaturePython)
doc = FreeCAD.ActiveDocument or FreeCAD.newDocument("Test")
obj = doc.addObject("Part::FeaturePython", "CustomBox")
MyBox(obj)
ViewProviderMyBox(obj.ViewObject)
doc.recompute()
undefinedFeaturePython对象是带有自定义属性的自定义参数化对象,属性变更会触发重新计算:
python
import FreeCAD
import Part
class MyBox:
"""A custom parametric box."""
def __init__(self, obj):
obj.Proxy = self
obj.addProperty("App::PropertyLength", "Length", "Dimensions",
"Box length").Length = 10.0
obj.addProperty("App::PropertyLength", "Width", "Dimensions",
"Box width").Width = 10.0
obj.addProperty("App::PropertyLength", "Height", "Dimensions",
"Box height").Height = 10.0
def execute(self, obj):
"""Called on document recompute."""
obj.Shape = Part.makeBox(obj.Length, obj.Width, obj.Height)
def onChanged(self, obj, prop):
"""Called when a property changes."""
pass
def __getstate__(self):
return None
def __setstate__(self, state):
return None
class ViewProviderMyBox:
"""View provider for custom icon and display settings."""
def __init__(self, vobj):
vobj.Proxy = self
def getIcon(self):
return ":/icons/Part_Box.svg"
def attach(self, vobj):
self.Object = vobj.Object
def updateData(self, obj, prop):
pass
def onChanged(self, vobj, prop):
pass
def __getstate__(self):
return None
def __setstate__(self, state):
return NoneCommon Property Types
--- Usage ---
| Property Type | Python Type | Description |
|---|---|---|
| | Boolean |
| | Integer |
| | Float |
| | String |
| | Length with units |
| | Angle in degrees |
| | 3D vector |
| | Position + rotation |
| object ref | Link to another object |
| list of refs | Links to multiple objects |
| | Dropdown selection |
| | File path |
| | RGB color (0.0-1.0) |
| any | Serializable Python object |
doc = FreeCAD.ActiveDocument or FreeCAD.newDocument("Test")
obj = doc.addObject("Part::FeaturePython", "CustomBox")
MyBox(obj)
ViewProviderMyBox(obj.ViewObject)
doc.recompute()
undefinedCreating GUI Tools
常用属性类型
Gui Commands
—
python
import FreeCAD
import FreeCADGui
class MyCommand:
"""A custom toolbar/menu command."""
def GetResources(self):
return {
"Pixmap": ":/icons/Part_Box.svg",
"MenuText": "My Custom Command",
"ToolTip": "Creates a custom box",
"Accel": "Ctrl+Shift+B"
}
def IsActive(self):
return FreeCAD.ActiveDocument is not None
def Activated(self):
# Command logic here
FreeCAD.Console.PrintMessage("Command activated\n")
FreeCADGui.addCommand("My_CustomCommand", MyCommand())| 属性类型 | Python类型 | 描述 |
|---|---|---|
| | 布尔值 |
| | 整数 |
| | 浮点数 |
| | 字符串 |
| | 带单位的长度 |
| | 角度(单位为度) |
| | 3D向量 |
| | 位置+旋转 |
| object ref | 指向其他对象的链接 |
| list of refs | 指向多个对象的链接 |
| | 下拉选择项 |
| | 文件路径 |
| | RGB颜色(取值范围0.0-1.0) |
| any | 可序列化的Python对象 |
PySide Dialogs
创建GUI工具
—
Gui命令
python
from PySide2 import QtWidgets, QtCore, QtGui
class MyDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super().__init__(parent or FreeCADGui.getMainWindow())
self.setWindowTitle("My Tool")
self.setMinimumWidth(300)
layout = QtWidgets.QVBoxLayout(self)
# Input fields
self.label = QtWidgets.QLabel("Length:")
self.spinbox = QtWidgets.QDoubleSpinBox()
self.spinbox.setRange(0.1, 1000.0)
self.spinbox.setValue(10.0)
self.spinbox.setSuffix(" mm")
form = QtWidgets.QFormLayout()
form.addRow(self.label, self.spinbox)
layout.addLayout(form)
# Buttons
btn_layout = QtWidgets.QHBoxLayout()
self.btn_ok = QtWidgets.QPushButton("OK")
self.btn_cancel = QtWidgets.QPushButton("Cancel")
btn_layout.addWidget(self.btn_ok)
btn_layout.addWidget(self.btn_cancel)
layout.addLayout(btn_layout)
self.btn_ok.clicked.connect(self.accept)
self.btn_cancel.clicked.connect(self.reject)python
import FreeCAD
import FreeCADGui
class MyCommand:
"""A custom toolbar/menu command."""
def GetResources(self):
return {
"Pixmap": ":/icons/Part_Box.svg",
"MenuText": "My Custom Command",
"ToolTip": "Creates a custom box",
"Accel": "Ctrl+Shift+B"
}
def IsActive(self):
return FreeCAD.ActiveDocument is not None
def Activated(self):
# Command logic here
FreeCAD.Console.PrintMessage("Command activated\n")
FreeCADGui.addCommand("My_CustomCommand", MyCommand())Usage
PySide对话框
dialog = MyDialog()
if dialog.exec_() == QtWidgets.QDialog.Accepted:
length = dialog.spinbox.value()
FreeCAD.Console.PrintMessage(f"Length: {length}\n")
undefinedpython
from PySide2 import QtWidgets, QtCore, QtGui
class MyDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super().__init__(parent or FreeCADGui.getMainWindow())
self.setWindowTitle("My Tool")
self.setMinimumWidth(300)
layout = QtWidgets.QVBoxLayout(self)
# Input fields
self.label = QtWidgets.QLabel("Length:")
self.spinbox = QtWidgets.QDoubleSpinBox()
self.spinbox.setRange(0.1, 1000.0)
self.spinbox.setValue(10.0)
self.spinbox.setSuffix(" mm")
form = QtWidgets.QFormLayout()
form.addRow(self.label, self.spinbox)
layout.addLayout(form)
# Buttons
btn_layout = QtWidgets.QHBoxLayout()
self.btn_ok = QtWidgets.QPushButton("OK")
self.btn_cancel = QtWidgets.QPushButton("Cancel")
btn_layout.addWidget(self.btn_ok)
btn_layout.addWidget(self.btn_cancel)
layout.addLayout(btn_layout)
self.btn_ok.clicked.connect(self.accept)
self.btn_cancel.clicked.connect(self.reject)Task Panel (Recommended for FreeCAD integration)
Usage
python
class MyTaskPanel:
"""Task panel shown in the left sidebar."""
def __init__(self):
self.form = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout(self.form)
self.spinbox = QtWidgets.QDoubleSpinBox()
self.spinbox.setValue(10.0)
layout.addWidget(QtWidgets.QLabel("Length:"))
layout.addWidget(self.spinbox)
def accept(self):
# Called when user clicks OK
length = self.spinbox.value()
FreeCAD.Console.PrintMessage(f"Accepted: {length}\n")
FreeCADGui.Control.closeDialog()
return True
def reject(self):
FreeCADGui.Control.closeDialog()
return True
def getStandardButtons(self):
return int(QtWidgets.QDialogButtonBox.Ok |
QtWidgets.QDialogButtonBox.Cancel)dialog = MyDialog()
if dialog.exec_() == QtWidgets.QDialog.Accepted:
length = dialog.spinbox.value()
FreeCAD.Console.PrintMessage(f"Length: {length}\n")
undefinedShow the panel
任务面板(推荐用于FreeCAD集成)
panel = MyTaskPanel()
FreeCADGui.Control.showDialog(panel)
undefinedpython
class MyTaskPanel:
"""Task panel shown in the left sidebar."""
def __init__(self):
self.form = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout(self.form)
self.spinbox = QtWidgets.QDoubleSpinBox()
self.spinbox.setValue(10.0)
layout.addWidget(QtWidgets.QLabel("Length:"))
layout.addWidget(self.spinbox)
def accept(self):
# Called when user clicks OK
length = self.spinbox.value()
FreeCAD.Console.PrintMessage(f"Accepted: {length}\n")
FreeCADGui.Control.closeDialog()
return True
def reject(self):
FreeCADGui.Control.closeDialog()
return True
def getStandardButtons(self):
return int(QtWidgets.QDialogButtonBox.Ok |
QtWidgets.QDialogButtonBox.Cancel)Coin3D Scenegraph (Pivy)
Show the panel
python
from pivy import coin
import FreeCADGuipanel = MyTaskPanel()
FreeCADGui.Control.showDialog(panel)
undefinedAccess the scenegraph root
Coin3D场景图(Pivy)
sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()
python
from pivy import coin
import FreeCADGuiAdd a custom separator with a sphere
Access the scenegraph root
sep = coin.SoSeparator()
mat = coin.SoMaterial()
mat.diffuseColor.setValue(1.0, 0.0, 0.0) # Red
trans = coin.SoTranslation()
trans.translation.setValue(10, 10, 10)
sphere = coin.SoSphere()
sphere.radius.setValue(2.0)
sep.addChild(mat)
sep.addChild(trans)
sep.addChild(sphere)
sg.addChild(sep)
sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()
Remove later
Add a custom separator with a sphere
sg.removeChild(sep)
undefinedsep = coin.SoSeparator()
mat = coin.SoMaterial()
mat.diffuseColor.setValue(1.0, 0.0, 0.0) # Red
trans = coin.SoTranslation()
trans.translation.setValue(10, 10, 10)
sphere = coin.SoSphere()
sphere.radius.setValue(2.0)
sep.addChild(mat)
sep.addChild(trans)
sep.addChild(sphere)
sg.addChild(sep)
Custom Workbench Creation
Remove later
python
import FreeCADGui
class MyWorkbench(FreeCADGui.Workbench):
MenuText = "My Workbench"
ToolTip = "A custom workbench"
Icon = ":/icons/freecad.svg"
def Initialize(self):
"""Called at workbench activation."""
import MyCommands # Import your command module
self.appendToolbar("My Tools", ["My_CustomCommand"])
self.appendMenu("My Menu", ["My_CustomCommand"])
def Activated(self):
pass
def Deactivated(self):
pass
def GetClassName(self):
return "Gui::PythonWorkbench"
FreeCADGui.addWorkbench(MyWorkbench)sg.removeChild(sep)
undefinedMacro Best Practices
自定义工作台创建
python
undefinedpython
import FreeCADGui
class MyWorkbench(FreeCADGui.Workbench):
MenuText = "My Workbench"
ToolTip = "A custom workbench"
Icon = ":/icons/freecad.svg"
def Initialize(self):
"""Called at workbench activation."""
import MyCommands # Import your command module
self.appendToolbar("My Tools", ["My_CustomCommand"])
self.appendMenu("My Menu", ["My_CustomCommand"])
def Activated(self):
pass
def Deactivated(self):
pass
def GetClassName(self):
return "Gui::PythonWorkbench"
FreeCADGui.addWorkbench(MyWorkbench)Standard macro header
宏最佳实践
-- coding: utf-8 --
—
FreeCAD Macro: MyMacro
—
Description: Brief description of what the macro does
—
Author: YourName
—
Version: 1.0
—
Date: 2026-04-07
—
import FreeCAD
import Part
from FreeCAD import Base
python
undefinedGuard for GUI availability
Standard macro header
—
-- coding: utf-8 --
—
FreeCAD Macro: MyMacro
—
Description: Brief description of what the macro does
—
Author: YourName
—
Version: 1.0
—
Date: 2026-04-07
if FreeCAD.GuiUp:
import FreeCADGui
from PySide2 import QtWidgets, QtCore
def main():
doc = FreeCAD.ActiveDocument
if doc is None:
FreeCAD.Console.PrintError("No active document\n")
return
if FreeCAD.GuiUp:
sel = FreeCADGui.Selection.getSelection()
if not sel:
FreeCAD.Console.PrintWarning("No objects selected\n")
# ... macro logic ...
doc.recompute()
FreeCAD.Console.PrintMessage("Macro completed\n")if name == "main":
main()
undefinedimport FreeCAD
import Part
from FreeCAD import Base
Selection Handling
Guard for GUI availability
python
undefinedif FreeCAD.GuiUp:
import FreeCADGui
from PySide2 import QtWidgets, QtCore
def main():
doc = FreeCAD.ActiveDocument
if doc is None:
FreeCAD.Console.PrintError("No active document\n")
return
if FreeCAD.GuiUp:
sel = FreeCADGui.Selection.getSelection()
if not sel:
FreeCAD.Console.PrintWarning("No objects selected\n")
# ... macro logic ...
doc.recompute()
FreeCAD.Console.PrintMessage("Macro completed\n")if name == "main":
main()
undefinedGet selected objects
选择处理
sel = FreeCADGui.Selection.getSelection() # List of objects
sel_ex = FreeCADGui.Selection.getSelectionEx() # Extended (sub-elements)
for selobj in sel_ex:
obj = selobj.Object
for sub in selobj.SubElementNames:
print(f"{obj.Name}.{sub}")
shape = obj.getSubObject(sub) # Get sub-shape
python
undefinedSelect programmatically
Get selected objects
FreeCADGui.Selection.addSelection(doc.MyBox)
FreeCADGui.Selection.addSelection(doc.MyBox, "Face1")
FreeCADGui.Selection.clearSelection()
undefinedsel = FreeCADGui.Selection.getSelection() # List of objects
sel_ex = FreeCADGui.Selection.getSelectionEx() # Extended (sub-elements)
for selobj in sel_ex:
obj = selobj.Object
for sub in selobj.SubElementNames:
print(f"{obj.Name}.{sub}")
shape = obj.getSubObject(sub) # Get sub-shape
Console Output
Select programmatically
python
FreeCAD.Console.PrintMessage("Info message\n")
FreeCAD.Console.PrintWarning("Warning message\n")
FreeCAD.Console.PrintError("Error message\n")
FreeCAD.Console.PrintLog("Debug/log message\n")FreeCADGui.Selection.addSelection(doc.MyBox)
FreeCADGui.Selection.addSelection(doc.MyBox, "Face1")
FreeCADGui.Selection.clearSelection()
undefinedCommon Patterns
控制台输出
Parametric Pad from Sketch
—
python
doc = FreeCAD.ActiveDocumentpython
FreeCAD.Console.PrintMessage("Info message\n")
FreeCAD.Console.PrintWarning("Warning message\n")
FreeCAD.Console.PrintError("Error message\n")
FreeCAD.Console.PrintLog("Debug/log message\n")Create sketch
常用模式
—
基于草图的参数化拉伸
sketch = doc.addObject("Sketcher::SketchObject", "Sketch")
sketch.addGeometry(Part.LineSegment(FreeCAD.Vector(0,0,0), FreeCAD.Vector(10,0,0)))
sketch.addGeometry(Part.LineSegment(FreeCAD.Vector(10,0,0), FreeCAD.Vector(10,10,0)))
sketch.addGeometry(Part.LineSegment(FreeCAD.Vector(10,10,0), FreeCAD.Vector(0,10,0)))
sketch.addGeometry(Part.LineSegment(FreeCAD.Vector(0,10,0), FreeCAD.Vector(0,0,0)))
python
doc = FreeCAD.ActiveDocumentClose with coincident constraints
Create sketch
for i in range(3):
sketch.addConstraint(Sketcher.Constraint("Coincident", i, 2, i+1, 1))
sketch.addConstraint(Sketcher.Constraint("Coincident", 3, 2, 0, 1))
sketch = doc.addObject("Sketcher::SketchObject", "Sketch")
sketch.addGeometry(Part.LineSegment(FreeCAD.Vector(0,0,0), FreeCAD.Vector(10,0,0)))
sketch.addGeometry(Part.LineSegment(FreeCAD.Vector(10,0,0), FreeCAD.Vector(10,10,0)))
sketch.addGeometry(Part.LineSegment(FreeCAD.Vector(10,10,0), FreeCAD.Vector(0,10,0)))
sketch.addGeometry(Part.LineSegment(FreeCAD.Vector(0,10,0), FreeCAD.Vector(0,0,0)))
Pad (PartDesign)
Close with coincident constraints
pad = doc.addObject("PartDesign::Pad", "Pad")
pad.Profile = sketch
pad.Length = 5.0
sketch.Visibility = False
doc.recompute()
undefinedfor i in range(3):
sketch.addConstraint(Sketcher.Constraint("Coincident", i, 2, i+1, 1))
sketch.addConstraint(Sketcher.Constraint("Coincident", 3, 2, 0, 1))
Export Shapes
Pad (PartDesign)
python
undefinedpad = doc.addObject("PartDesign::Pad", "Pad")
pad.Profile = sketch
pad.Length = 5.0
sketch.Visibility = False
doc.recompute()
undefinedSTEP export
导出形状
Part.export([doc.MyBox], "/path/to/output.step")
python
undefinedSTL export (mesh)
STEP export
import Mesh
Mesh.export([doc.MyBox], "/path/to/output.stl")
Part.export([doc.MyBox], "/path/to/output.step")
IGES export
STL export (mesh)
Part.export([doc.MyBox], "/path/to/output.iges")
import Mesh
Mesh.export([doc.MyBox], "/path/to/output.stl")
Multiple formats via importlib
IGES export
import importlib
importlib.import_module("importOBJ").export([doc.MyBox], "/path/to/output.obj")
undefinedPart.export([doc.MyBox], "/path/to/output.iges")
Units and Quantities
Multiple formats via importlib
python
undefinedimport importlib
importlib.import_module("importOBJ").export([doc.MyBox], "/path/to/output.obj")
undefinedFreeCAD uses mm internally
单位与数量
q = FreeCAD.Units.Quantity("10 mm")
q_inch = FreeCAD.Units.Quantity("1 in")
print(q_inch.getValueAs("mm")) # 25.4
python
undefinedParse user input with units
FreeCAD uses mm internally
q = FreeCAD.Units.parseQuantity("2.5 in")
value_mm = float(q) # Value in mm (internal unit)
undefinedq = FreeCAD.Units.Quantity("10 mm")
q_inch = FreeCAD.Units.Quantity("1 in")
print(q_inch.getValueAs("mm")) # 25.4
Compensation Rules (Quasi-Coder Integration)
Parse user input with units
When interpreting shorthand or quasi-code for FreeCAD scripts:
- Terminology mapping: "box" → , "cylinder" →
Part.makeBox(), "sphere" →Part.makeCylinder(), "merge/combine/join" →Part.makeSphere(), "subtract/cut/remove" →.fuse(), "intersect" →.cut(), "round edges/fillet" →.common(), "bevel/chamfer" →.makeFillet().makeChamfer() - Implicit document: If no document handling is mentioned, wrap in standard
doc = FreeCAD.ActiveDocument or FreeCAD.newDocument() - Units assumption: Default to millimeters unless stated otherwise
- Recompute: Always call after modifications
doc.recompute() - GUI guard: Wrap GUI-dependent code in when the script may run headless
if FreeCAD.GuiUp: - Part.show(): Use for quick display, or
Part.show(shape, "Name")for named persistent objectsdoc.addObject("Part::Feature", "Name")
q = FreeCAD.Units.parseQuantity("2.5 in")
value_mm = float(q) # Value in mm (internal unit)
undefinedReferences
适配规则(伪代码集成)
Primary Links
—
当解析FreeCAD脚本的简写或伪代码时:
- 术语映射:"box" → ,"cylinder" →
Part.makeBox(),"sphere" →Part.makeCylinder(),"merge/combine/join" →Part.makeSphere(),"subtract/cut/remove" →.fuse(),"intersect" →.cut(),"round edges/fillet" →.common(),"bevel/chamfer" →.makeFillet().makeChamfer() - 隐式文档:如果未提及文档处理,使用标准封装
doc = FreeCAD.ActiveDocument or FreeCAD.newDocument() - 单位假设:除非另有说明,默认使用毫米
- 重新计算:修改完成后始终调用
doc.recompute() - GUI防护:当脚本可能在无GUI环境运行时,将依赖GUI的代码封装在 中
if FreeCAD.GuiUp: - Part.show():快速显示使用 ,需要命名持久化对象时使用
Part.show(shape, "Name")doc.addObject("Part::Feature", "Name")
Bundled Reference Documents
参考资料
—
主要链接
See the references/ directory for topic-organized guides:
- scripting-fundamentals.md — Core scripting, document model, console
- geometry-and-shapes.md — Part, Mesh, Sketcher, topology
- parametric-objects.md — FeaturePython, properties, scripted objects
- gui-and-interface.md — PySide, dialogs, task panels, Coin3D
- workbenches-and-advanced.md — Workbenches, macros, FEM, Path, recipes
—
捆绑参考文档
—
查看 references/ 目录获取按主题整理的指南:
- scripting-fundamentals.md — 核心脚本、文档模型、控制台
- geometry-and-shapes.md — Part、Mesh、Sketcher、拓扑结构
- parametric-objects.md — FeaturePython、属性、脚本化对象
- gui-and-interface.md — PySide、对话框、任务面板、Coin3D
- workbenches-and-advanced.md — 工作台、宏、FEM、Path、实用方案