@tool() Decorator
Registers a class as a LinkCAD tool that appears in the application menu with an auto-generated options dialog.
Signature
@tool( name: str, menu: str, tooltip: str = "", shortcut: str = "", icon: str = "", requires_drawing: bool = True,)class MyTool(Tool): ...Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
name | str | required | Display name in the menu |
menu | str | required | Menu path (use / for submenus), e.g. "Tools/Analysis" |
tooltip | str | "" | Tooltip shown on hover |
shortcut | str | "" | Keyboard shortcut (e.g. "Ctrl+Shift+M") |
icon | str | "" | Icon filename, relative to res/ |
requires_drawing | bool | True | Whether the tool is disabled when no drawing is loaded |
Tool Base Class
All tool plugins must extend Tool and implement run():
class Tool: def run(self, drawing) -> dict: """Override to implement the tool logic. Return a result dictionary.""" ...
def format_result(self, result: dict) -> str: """Override to control how the result is displayed.""" ...The drawing argument is a linkcad.v1.db.Drawing — the live database, owned by the application, so never destroy it. If requires_drawing=False, it may be None.
run() returns a result dictionary. The default format_result() prints result["summary"] if present, so include one. Override format_result() for anything richer.
The framework holds a read lock while run() executes. Wrap modifications in a Transaction to make them undoable.
ToolInfo
The decorator creates a ToolInfo object attached to the class:
| Field | Description |
|---|---|
name | Tool display name |
menu | Menu path |
tooltip | Tooltip text |
shortcut | Keyboard shortcut |
icon | Icon filename |
requires_drawing | Whether a drawing is required |
options | The Option attributes collected from the class |
Menu Path
The menu parameter defines where the tool appears in LinkCAD’s menu:
"Edit/Analyze" → Edit → Analyze → <tool name>"Tools/Analysis" → Tools → Analysis → <tool name>"Tools/Drawing/Custom" → Tools → Drawing → Custom → <tool name>Complete Example
from typing import Any, Dict
from linkcad.v1.plugin import tool, Tool, Option
@tool( name="Shape Counter", menu="Tools/Analysis", tooltip="Count shapes per layer", shortcut="Ctrl+Shift+C", requires_drawing=True,)class ShapeCounter(Tool): closed_only = Option.boolean("Closed shapes only", default=False)
def run(self, drawing) -> Dict[str, Any]: counts: Dict[str, int] = {} for cell in drawing.cells: for shape in cell.shapes: if self.closed_only and not shape.closed: continue layer = shape.layer if layer is not None: counts[layer.name] = counts.get(layer.name, 0) + 1
return { "layers": counts, "summary": f"{sum(counts.values())} shapes on {len(counts)} layers", }
def format_result(self, result: Dict[str, Any]) -> str: lines = [f"{name}: {n}" for name, n in sorted(result["layers"].items())] lines += ["", result["summary"]] return "\n".join(lines)