freecad

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

FreeCAD Control

FreeCAD Control

FreeCAD (
/Applications/FreeCAD.app
) is scriptable in Python against the
FreeCAD
/
FreeCADGui
/
Part
/
Sketcher
/
PartDesign
/
Draft
/
Mesh
/
TechDraw
modules. Drive it through the FreeCAD Bridge — a small script running inside a running FreeCAD GUI that executes whatever build script is POSTed to
127.0.0.1:8735
. Because the script runs in the live session on the GUI thread, the viewport updates as you build and
Gui.*
/
saveImage
work directly. Everything below was proven on FreeCAD 1.1.3.
  • scripts/freecad-bridge.py — the bridge to run inside FreeCAD.
  • scripts/freecad-send.sh — send a
    .py
    file (or
    -c 'inline code'
    ) to the bridge and print its captured output;
    --ping
    checks it's up.
  • scripts/example-bracket.py — a complete build→check→measure→ export→screenshot script to copy from.
  • references/api-reference.md — condensed, version-checked Python API (document, Part, Sketcher attachment, PartDesign features, Draft, Mesh, TechDraw drawings, import/export, GUI). Read it before writing anything past the cheatsheet below — several PartDesign/TechDraw properties are not what you'd guess.
FreeCAD(
/Applications/FreeCAD.app
)可通过 Python 针对
FreeCAD
/
FreeCADGui
/
Part
/
Sketcher
/
PartDesign
/
Draft
/
Mesh
/
TechDraw
modules 编写脚本。
通过 FreeCAD Bridge 控制它——这是一个在正在运行的 FreeCAD GUI 内执行的小型脚本,会运行 POST 到
127.0.0.1:8735
的构建脚本。由于脚本在 GUI 线程的实时会话中运行,viewport 会随着构建过程更新,
Gui.*
/
saveImage
也可以直接使用。以下所有内容均已在 FreeCAD 1.1.3 上验证。
  • scripts/freecad-bridge.py——需要在 FreeCAD 内运行的 bridge。
  • scripts/freecad-send.sh——向 bridge 发送
    .py
    文件(或使用
    -c 'inline code'
    ),并打印其捕获的输出;
    --ping
    用于检查 bridge 是否在线。
  • scripts/example-bracket.py——一个完整的 build→check→measure→export→screenshot 脚本,可直接复制使用。
  • references/api-reference.md——经过版本校验的精简版 Python API 参考,涵盖 document、Part、Sketcher attachment、PartDesign features、Draft、Mesh、TechDraw drawings、import/export 和 GUI。在编写超出以下速查表范围的任何内容前,请先阅读它——若直接凭直觉猜测,若干 PartDesign/TechDraw 属性可能会出错。

The control loop

控制循环

  1. Make sure the bridge is up:
    bash scripts/freecad-send.sh --ping
    . A live bridge answers
    {"ok": true, "bridge": "freecad", "version": "1.1.3"}
    . If it doesn't, the bridge has to be started — it is a local code-execution server (see Security), so say that before starting it:
    • FreeCAD not running (
      pgrep -xi freecad
      finds nothing): launch it with the bridge as a startup script — FreeCAD executes a
      .py
      passed on its command line:
      open -a FreeCAD --args "$PWD/scripts/freecad-bridge.py"
      (absolute path), then poll
      --ping
      for up to ~60 s while the GUI starts.
    • FreeCAD already running:
      open
      doesn't hand arguments to a running app, so ask the user to paste scripts/freecad-bridge.py into the Python console (View ▸ Panels ▸ Python console) or run it via Macro ▸ Macros… ▸ Execute after copying it into the user macro folder (shown in that dialog).
  2. Write a build script to the scratchpad — Python against the API (start from references/api-reference.md and scripts/example-bracket.py).
  3. Send it:
    OUT=/path/to/outdir bash scripts/freecad-send.sh /path/build.py
    . The bridge runs it in the live session and returns the script's captured stdout; on a Python exception it returns the traceback and the sender exits non-zero.
    FREECAD_SEND_TIMEOUT=600
    (seconds) for heavy builds.
    App
    /
    FreeCAD
    /
    Gui
    are pre-imported in the script's namespace, and
    OUT
    is injected (from
    $OUT
    ) as both a global and
    os.environ["OUT"]
    .
  4. Check, then measure: a feature that fails to compute does not raise — see the recompute check below. Then write a
    metrics.json
    (bounding box, volume,
    isValid()
    , solid count) and Read it; for a picture,
    saveImage(...)
    in the same script and Read the PNG. Export STEP/STL/FCStd into
    $OUT
    for the deliverable.
  5. Iterate: inspect output/PNG, fix the script, re-send. Scripts must be re-runnable: close any document of the same name before
    App.newDocument(...)
    (FreeCAD silently renames a duplicate to
    thing1
    , and the old one keeps cluttering the session). Don't close documents you didn't create — the session is the user's.
A failed send prints the Python traceback the bridge captured. The bridge stays up across sends; if it becomes unresponsive, ask the user to re-run the bridge script.
No GUI needed? For pure build→measure→export work (no screenshot, no TechDraw PDF),
/Applications/FreeCAD.app/Contents/Resources/bin/freecadcmd build.py
runs the same script headless — useful when the user can't start the bridge. There
Gui
is undefined (guard with
globals().get("Gui")
), stdout carries FreeCAD's own log noise, and an uncaught exception still exits 0 — wrap the script in
try/except
and
sys.exit(1)
so failures surface.
  1. 确保 bridge 已启动:
    bash scripts/freecad-send.sh --ping
    。在线的 bridge 会返回
    {"ok": true, "bridge": "freecad", "version": "1.1.3"}
    。如果没有响应,就必须启动 bridge——它是一个本地代码执行服务器(请参阅 Security),因此在启动前必须先向用户说明这一点:
    • FreeCAD 未运行(
      pgrep -xi freecad
      找不到任何进程):将 bridge 作为启动脚本启动 FreeCAD——FreeCAD 会执行命令行传入的
      .py
      文件:
      open -a FreeCAD --args "$PWD/scripts/freecad-bridge.py"
      (使用绝对路径),随后在 GUI 启动期间轮询
      --ping
      ,最长约 60 秒。
    • FreeCAD 已在运行:
      open
      无法向已运行的应用传递参数,因此请用户将 scripts/freecad-bridge.py 粘贴到 Python console(View ▸ Panels ▸ Python console),或者先将其复制到用户 macro 文件夹(该文件夹会显示在对话框中),再通过 Macro ▸ Macros… ▸ Execute 运行。
  2. 编写构建脚本并放入 scratchpad——使用 Python 调用 API(从 references/api-reference.md 和 scripts/example-bracket.py 开始)。
  3. 发送脚本:
    OUT=/path/to/outdir bash scripts/freecad-send.sh /path/build.py
    。bridge 会在实时会话中运行脚本并返回脚本捕获的 stdout;如果发生 Python exception,则返回 traceback,发送器也会以非零状态退出。对于耗时较长的构建,可将
    FREECAD_SEND_TIMEOUT
    设置为
    600
    秒。
    App
    /
    FreeCAD
    /
    Gui
    已预先导入脚本 namespace,
    OUT
    则通过
    $OUT
    注入,同时作为 global 和
    os.environ["OUT"]
    使用。
  4. 检查,然后测量:计算失败的特征不会抛出异常——请参阅下方 recompute check。接着生成
    metrics.json
    (bounding box、volume、
    isValid()
    、solid count)并 Read;如需图片,则在同一脚本中调用
    saveImage(...)
    并 Read PNG。将 STEP/STL/FCStd 导出到
    $OUT
    ,作为最终交付物。
  5. 迭代:检查输出和 PNG,修复脚本后重新发送。脚本必须能够重复运行:调用
    App.newDocument(...)
    前,先关闭同名 document(FreeCAD 会将重复文档静默重命名为
    thing1
    ,旧文档会继续在会话中堆积)。不要关闭并非由你创建的 document——该会话属于用户。
发送失败时,会打印 bridge 捕获的 Python traceback。bridge 会在多次发送之间持续运行;如果它停止响应,请用户重新运行 bridge 脚本。
不需要 GUI? 对于仅涉及 build→measure→export 的工作(不需要截图或 TechDraw PDF),
/Applications/FreeCAD.app/Contents/Resources/bin/freecadcmd build.py
可以以 headless 模式运行同一脚本——当用户无法启动 bridge 时,这会非常有用。在该模式下
Gui
未定义(使用
globals().get("Gui")
进行保护),stdout 中会混入 FreeCAD 自身的日志,而且未捕获的 exception 仍会以 0 退出——应使用
try/except
和
sys.exit(1)
包装脚本,让失败明确暴露出来。

Modeling cheatsheet (proven patterns)

建模速查表(已验证模式)

Units are mm and degrees.
App
=
FreeCAD
(both pre-imported by the bridge). Always
recompute()
before measuring.
单位为 mm 和 degrees。
App
=
FreeCAD
(两者均已由 bridge 预先导入)。测量前始终要执行
recompute()
。

Solid via primitives + boolean (verified: plate with a bored hole)

使用 primitives + Boolean 构建实体(已验证:带钻孔的板件)

python
import os, json, Part, Mesh
OUT = globals().get("OUT") or os.environ["OUT"]
if "bracket" in App.listDocuments():                # re-runnable: drop the previous build
    App.closeDocument("bracket")
doc = App.newDocument("bracket")
box = doc.addObject("Part::Box", "Plate"); box.Length, box.Width, box.Height = 40, 20, 10
hole = doc.addObject("Part::Cylinder", "Hole"); hole.Radius, hole.Height = 5, 10
hole.Placement.Base = App.Vector(20, 10, 0)          # centre of the plate
cut = doc.addObject("Part::Cut", "Bracket"); cut.Base, cut.Tool = box, hole
doc.recompute()                                      # REQUIRED before measuring
python
import os, json, Part, Mesh
OUT = globals().get("OUT") or os.environ["OUT"]
if "bracket" in App.listDocuments():                # re-runnable: drop the previous build
    doc = App.newDocument("bracket")
box = doc.addObject("Part::Box", "Plate"); box.Length, box.Width, box.Height = 40, 20, 10
hole = doc.addObject("Part::Cylinder", "Hole"); hole.Radius, hole.Height = 5, 10
hole.Placement.Base = App.Vector(20, 10, 0)          # centre of the plate
cut = doc.addObject("Part::Cut", "Bracket"); cut.Base, cut.Tool = box, hole
doc.recompute()                                      # REQUIRED before measuring

Check, measure, screenshot (the live feedback)

检查、测量、截图(实时反馈)

doc.recompute()
never raises: a broken feature (bad sketch, pocket that misses the solid, fillet too big) is marked
Invalid
and its
.Shape
is stale or empty, so the metrics would quietly describe the wrong part. Check first:
python
bad = [(o.Name, o.State) for o in doc.Objects if "Invalid" in o.State or "Error" in o.State]
if bad:
    raise RuntimeError("recompute failed: %s" % bad)   # comes back as the send's traceback

shp = cut.Shape; bb = shp.BoundBox
m = {"bbox": [round(bb.XLength,3), round(bb.YLength,3), round(bb.ZLength,3)],
     "volume": round(shp.Volume,3), "valid": shp.isValid(), "solids": len(shp.Solids)}
json.dump(m, open(OUT+"/metrics.json","w"), indent=2)
print("METRICS", json.dumps(m))                      # returned to the sender

view = Gui.getDocument(doc.Name).activeView()        # live GUI — snapshot inline
view.setAnimationEnabled(False)                      # else saveImage catches the camera mid-turn
view.viewIsometric(); view.fitAll()
view.saveImage(OUT+"/check.png", 900, 675, "White")
Compare the metrics against what the user asked for (e.g. expected volume computed by hand) — that catches a hole in the wrong place or a pad in the wrong direction, which a valid, single-solid shape would not.
doc.recompute()
永远不会抛出异常:损坏的特征(错误的 sketch、未命中实体的 pocket、尺寸过大的 fillet)会被标记为
Invalid
,其
.Shape
可能已失效或为空,因此指标会悄悄描述错误的零件。请先检查:
python
bad = [(o.Name, o.State) for o in doc.Objects if "Invalid" in o.State or "Error" in o.State]
if bad:
    raise RuntimeError("recompute failed: %s" % bad)   # comes back as the send's traceback

shp = cut.Shape; bb = shp.BoundBox
m = {"bbox": [round(bb.XLength,3), round(bb.YLength,3), round(bb.ZLength,3)],
     "volume": round(shp.Volume,3), "valid": shp.isValid(), "solids": len(shp.Solids)}
json.dump(m, open(OUT+"/metrics.json","w"), indent=2)
print("METRICS", json.dumps(m))                      # returned to the sender

view = Gui.getDocument(doc.Name).activeView()        # live GUI — snapshot inline
view.setAnimationEnabled(False)                      # else saveImage catches the camera mid-turn
view.viewIsometric(); view.fitAll()
view.saveImage(OUT+"/check.png", 900, 675, "White")
将指标与用户要求的内容进行比较(例如,与手动计算的预期 volume 对比)——这样可以发现孔位错误或 pad 方向错误等问题,而仅检查有效且单一的 solid 无法发现这些问题。

Export (pick the module by format — see the reference's matrix)

导出(根据格式选择 module——请参阅参考文档中的矩阵)

python
Part.export([cut], OUT+"/bracket.step")   # B-rep: .step/.stp .iges/.igs .brep
Mesh.export([cut], OUT+"/bracket.stl")    # mesh: .stl .obj .ply .3mf .amf .off
doc.saveAs(OUT+"/bracket.FCStd")          # native, re-editable
python
Part.export([cut], OUT+"/bracket.step")   # B-rep: .step/.stp .iges/.igs .brep
Mesh.export([cut], OUT+"/bracket.stl")    # mesh: .stl .obj .ply .3mf .amf .off
doc.saveAs(OUT+"/bracket.FCStd")          # native, re-editable

Parametric route (Sketcher → PartDesign, verified: rectangle → pad)

参数化流程(Sketcher → PartDesign,已验证:矩形 → pad)

python
import Part, Sketcher
body = doc.addObject("PartDesign::Body", "Body")
sk = body.newObject("Sketcher::SketchObject", "Sketch")   # unattached = lies on XY
V = App.Vector
for a, b in [((0,0),(30,0)), ((30,0),(30,20)), ((30,20),(0,20)), ((0,20),(0,0))]:
    sk.addGeometry(Part.LineSegment(V(*a,0), V(*b,0)), False)
for i in range(4):
    sk.addConstraint(Sketcher.Constraint("Coincident", i, 2, (i+1) % 4, 1))
doc.recompute()
pad = body.newObject("PartDesign::Pad", "Pad"); pad.Profile = sk; pad.Length = 10
doc.recompute()
When the user wants to edit dimensions later, drive them with dimensional constraints (
DistanceX
,
DistanceY
,
Diameter
, …) and confirm
sk.FullyConstrained
is
True
. To sketch on another plane or on a face, set
sk.AttachmentSupport
+
sk.MapMode = "FlatFace"
; through-all pockets are
pocket.Type = "ThroughAll"
; after adding a Linear/PolarPattern set
body.Tip = pattern
yourself. All are in the reference, with revolution axes, fillets, and how to pick edges by geometry instead of guessing
Edge1
.
Feature types, constraint kinds, Draft/Mesh calls, TechDraw drawings, and the full export matrix are in references/api-reference.md.
python
import Part, Sketcher
body = doc.addObject("PartDesign::Body", "Body")
sk = body.newObject("Sketcher::SketchObject", "Sketch")   # unattached = lies on XY
V = App.Vector
for a, b in [((0,0),(30,0)), ((30,0),(30,20)), ((30,20),(0,20)), ((0,20),(0,0))]:
    sk.addGeometry(Part.LineSegment(V(*a,0), V(*b,0)), False)
for i in range(4):
    sk.addConstraint(Sketcher.Constraint("Coincident", i, 2, (i+1) % 4, 1))
doc.recompute()
pad = body.newObject("PartDesign::Pad", "Pad"); pad.Profile = sk; pad.Length = 10
doc.recompute()
当用户希望以后编辑尺寸时,使用 dimensional constraints(
DistanceX
、
DistanceY
、
Diameter
等)驱动尺寸,并确认
sk.FullyConstrained
为
True
。如果要在另一个平面或面上绘制 sketch,请设置
sk.AttachmentSupport
和
sk.MapMode = "FlatFace"
;贯通 pocket 的设置为
pocket.Type = "ThroughAll"
;添加 Linear/PolarPattern 后,需要自行设置
body.Tip = pattern
。这些内容以及 revolution axes、fillets 和按几何特征而非猜测
Edge1
来选择边的方法,都记录在参考文档中。
Feature types、constraint kinds、Draft/Mesh calls、TechDraw drawings 以及完整的 export matrix 参见 references/api-reference.md。

Verification

验证

  • Returned stdout is the immediate signal —
    print(...)
    in the script comes straight back through the sender.
  • The
    Invalid
    -state check
    is what turns a silent recompute failure into a traceback.
  • metrics.json (bbox / volume /
    isValid()
    / solid count) is the structured check — write it and Read it to confirm geometry without eyeballing.
  • Inline screenshot: because the bridge runs in the live GUI,
    saveImage(...)
    in the same script produces a viewport PNG to Read — no separate render step. For a TechDraw PDF,
    qlmanage -t -s 1000 -o <dir> page.pdf
    renders a PNG thumbnail to Read.
  • Hand-off: an exported
    .stl
    /
    .step
    opens in any slicer/CAD tool the user already has.
  • 返回的 stdout 是即时信号——脚本中的
    print(...)
    会通过发送器直接返回。
  • Invalid
    -state 检查
    可将静默的 recompute failure 转换为 traceback。
  • metrics.json
    (bbox / volume /
    isValid()
    / solid count)是一种结构化检查——应将其写入文件并 Read,以确认几何结构,而不只是凭肉眼判断。
  • 内嵌截图:由于 bridge 在实时 GUI 中运行,同一脚本中的
    saveImage(...)
    会生成可供 Read 的 viewport PNG,无需单独的渲染步骤。对于 TechDraw PDF,
    qlmanage -t -s 1000 -o <dir> page.pdf
    会渲染可供 Read 的 PNG 缩略图。
  • 交付:导出的
    .stl
    /
    .step
    可在用户已有的任意 slicer/CAD tool 中打开。

Gotchas (hard-won on this machine)

常见问题(本机实践总结)

  • The bridge runs in the live session, so state persists between sends: open documents, imported assets, and view settings all stick. Close-then-create your own document by name so a re-send starts clean.
  • newDocument
    never fails on a duplicate name
    — it silently creates
    name1
    , so
    App.getDocument("name")
    later returns the old build.
  • Failed features don't raise —
    recompute()
    returns normally and leaves the object
    Invalid
    . Check
    o.State
    before trusting any measurement.
  • Gui
    is live here
    —
    saveImage
    ,
    viewIsometric
    ,
    fitAll
    all work. (Under headless
    freecadcmd
    it is not defined.)
  • Blank or off-angle screenshots come from view animation:
    viewIsometric()
    starts a camera animation that
    saveImage
    doesn't wait for.
    view.setAnimationEnabled(False)
    first. Always Read the PNG before handing it over.
  • print()
    and captured stdout
    come back in the response;
    App.Console.PrintMessage
    goes to FreeCAD's Report view, not to the sender — use
    print()
    for anything you want returned.
  • recompute()
    before every measure or export
    — parametric attributes don't reach
    .Shape
    until then.
  • TechDraw views compute in a background thread — right after
    recompute()
    a view has no edges yet, and a dimension referencing
    Edge0
    measures
    0.0
    . Wait for
    view.getVisibleEdges()
    before adding dimensions (pattern in the reference). A view's
    Scale
    is ignored unless
    ScaleType = "Custom"
    , and edge lengths come back scaled.
  • TechDraw exports skip the frame until the page has been opened in the GUI (
    page.ViewObject.doubleClicked()
    ), and the default A4 template is an empty sheet — use the ISO templates (reference).
  • A pattern doesn't become the Body's Tip —
    body.Shape
    keeps showing the unpatterned solid until you set
    body.Tip = pattern
    .
  • Guessed edge/face numbers break —
    Edge1
    is often a seam; select by curve type, radius or position (snippet in the reference).
  • PySide binding: FreeCAD 1.1 ships PySide6 (Qt6); the bridge imports it with a PySide2 fallback. If you script Qt directly, import the same way.
  • Booleans keep their inputs in the tree —
    Part::Cut
    /
    Fuse
    hide
    Base
    /
    Tool
    automatically, but
    Part::Fillet
    /
    Chamfer
    leave their base visible (it shows through in screenshots). Hide inputs yourself; export only the result feature.
  • STL resolution: in the GUI,
    Mesh.export
    reuses the coarse display mesh. For anything printed, mesh explicitly with
    MeshPart.meshFromShape(...)
    (reference) and report the facet count.
  • Save the native
    .FCStd
    from the script so parametric work survives and can be re-edited Re-saving over an existing file leaves a timestamped
    *.FCBak
    beside it — delete it from
    $OUT
    if the folder is a deliverable.
  • bridge 在实时会话中运行,因此状态会在多次发送之间持续保留:打开的 document、导入的资源以及 view settings 都会保留。按名称先关闭再创建自己的 document,使重新发送时从干净状态开始。
  • newDocument
    遇到重复名称时永远不会失败
    ——它会静默创建
    name1
    ,因此后续调用
    App.getDocument("name")
    会返回旧构建。
  • 计算失败的特征不会抛出异常——
    recompute()
    会正常返回,并将对象保留为
    Invalid
    。在信任测量结果前,请检查
    o.State
    。
  • 此处
    Gui
    是实时可用的
    ——
    saveImage
    、
    viewIsometric
    、
    fitAll
    均可正常工作。(在 headless 的
    freecadcmd
    下,
    Gui
    未定义。)
  • 截图空白或角度异常通常由 view animation 导致:
    viewIsometric()
    会启动 camera animation,而
    saveImage
    不会等待动画结束。请先执行
    view.setAnimationEnabled(False)
    。交付前始终要 Read PNG。
  • print()
    和捕获的 stdout
    会在响应中返回;
    App.Console.PrintMessage
    会输出到 FreeCAD 的 Report view,而不会发送给发送器——需要返回给发送器的内容请使用
    print()
    。
  • 每次测量或导出前都要执行
    recompute()
    ——否则 parametric attributes 不会传递到
    .Shape
    。
  • TechDraw views 在后台线程中计算——刚执行
    recompute()
    后,view 尚无 edges,引用
    Edge0
    的 dimension 会测得
    0.0
    。添加 dimensions 前请等待
    view.getVisibleEdges()
    。除非将
    ScaleType
    设置为
    "Custom"
    ,否则 view 的
    Scale
    会被忽略,而且返回的 edge lengths 已应用缩放。
  • TechDraw exports 会在页面通过 GUI 打开前跳过 frame(
    page.ViewObject.doubleClicked()
    ),并且默认 A4 模板是空白图纸——请使用 ISO templates(参考文档)。
  • Pattern 不会自动成为 Body 的 Tip——除非设置
    body.Tip = pattern
    ,否则
    body.Shape
    仍会显示未应用 pattern 的 solid。
  • 猜测 edge/face 编号会出错——
    Edge1
    经常是 seam;应根据 curve type、radius 或 position 进行选择(参考文档中有示例)。
  • PySide binding:FreeCAD 1.1 搭载 PySide6(Qt6);bridge 导入它时会回退到 PySide2。如果直接编写 Qt 脚本,也应采用相同方式导入。
  • Boolean 会将输入对象保留在 tree 中——
    Part::Cut
    /
    Fuse
    会自动隐藏
    Base
    /
    Tool
    ,但
    Part::Fillet
    /
    Chamfer
    会让 base 保持可见(因此会透过模型显示在截图中)。请自行隐藏输入对象,并且只导出结果 feature。
  • STL resolution:在 GUI 中,
    Mesh.export
    会复用较粗的 display mesh。对于任何需要打印的模型,请使用
    MeshPart.meshFromShape(...)
    显式生成 mesh(参考文档),并报告 facet count。
  • 从脚本保存原生
    .FCStd
    ,使 parametric work 能够保留并可再次编辑。覆盖现有文件重新保存时,会在旁边留下带时间戳的
    *.FCBak
    ;如果该文件夹是交付目录,请将其从
    $OUT
    中删除。

Security

Security

Running the bridge means running a code-execution server on the user's machine. Say so before asking them to start it.
  • The bridge binds
    127.0.0.1:8735
    (
    FREECAD_BRIDGE_PORT
    ) and executes any Python POSTed to
    /run
    inside the live session — the user's privileges, the user's open documents. Requests carry no authentication: every local process, and every other user on a shared machine, can drive FreeCAD through it.
  • Web pages cannot. Requests carrying an
    Origin
    header or a cross-site
    Sec-Fetch-Site
    are rejected with 403, so a page in the user's browser can't reach the bridge. That check is the only gate — there is no token.
  • Nothing listens until the bridge is started. All three routes (launch with the script as an argument, Python console paste, Macro ▸ Execute) are per-session — the Macro folder copy only saves the pasting, it does not autostart. Turning it into an autoload macro would make FreeCAD listen on every launch; don't suggest that without saying so.
  • To stop the bridge, quit FreeCAD. There is no remote shutdown; the port is released with the process.
运行 bridge 意味着在用户计算机上运行一个代码执行服务器。在要求用户启动之前,必须先说明这一点。
  • bridge 绑定
    127.0.0.1:8735
    (
    FREECAD_BRIDGE_PORT
    ),并执行 POST 到
    /run
    的任意 Python 脚本,且这些脚本会在实时会话中运行——拥有用户权限,并能操作用户打开的 document。请求不携带身份验证:共享机器上的每个本地进程以及每位其他用户都可以控制 FreeCAD。
  • 网页无法访问。 携带
    Origin
    header 或跨站
    Sec-Fetch-Site
    的请求会以 403 拒绝,因此用户浏览器中的页面无法访问 bridge。该检查是唯一的访问门槛——没有 token。
  • 在 bridge 启动前,不会有任何服务监听。所有三种启动方式(作为参数启动、通过 Python console 粘贴、通过 Macro ▸ Execute 运行)均按 session 生效——Macro 文件夹中的副本只用于省去粘贴步骤,并不会自动启动。如果将其改成 autoload macro,FreeCAD 每次启动时都会开始监听;除非明确告知用户,否则不要建议这样做。
  • 要停止 bridge,请退出 FreeCAD。 它没有远程关闭功能;该端口会随进程退出而释放。