Panel Assembly Tutorial
This advanced tutorial demonstrates building a real-world tool that uses the Option.table() type to create an interactive grid for specifying panel layouts.
The Goal
Create a "Panelizer" tool that arranges multiple cells into a panel grid, with configurable spacing and repetition.
Complete Plugin
from linkcad.plugin import tool, Tool, Option, TableColumn
from linkcad.db import Drawing, Cell, Transaction
from linkcad.geom import Point
@tool(
name="Panelize",
menu="Tools/Drawing",
tooltip="Arrange cells into a panel layout",
requires_drawing=True,
)
class Panelizer(Tool):
panel_name = Option.string(
"Panel cell name",
default="PANEL",
tooltip="Name for the generated panel cell",
)
entries = Option.table(
"Panel Layout",
columns=[
TableColumn(
key="cell_name",
label="Cell",
col_type="cell_choice",
),
TableColumn(
key="offset_x",
label="Offset X (µm)",
col_type="real",
default=0.0,
decimals=3,
),
TableColumn(
key="offset_y",
label="Offset Y (µm)",
col_type="real",
default=0.0,
decimals=3,
),
TableColumn(
key="repeat_x",
label="Repeat X",
col_type="integer",
default=1,
min_value=1,
max_value=1000,
),
TableColumn(
key="repeat_y",
label="Repeat Y",
col_type="integer",
default=1,
min_value=1,
max_value=1000,
),
TableColumn(
key="pitch_x",
label="Pitch X (µm)",
col_type="real",
default=1000.0,
decimals=3,
),
TableColumn(
key="pitch_y",
label="Pitch Y (µm)",
col_type="real",
default=1000.0,
decimals=3,
),
],
)
flatten_result = Option.boolean(
"Flatten result",
default=False,
tooltip="Flatten the panel cell after assembly",
)
def run(self, drawing) -> None:
dwg = Drawing.current()
with Transaction(dwg) as txn:
panel = Cell.create(dwg, self.panel_name)
for row in self.entries:
cell_name = row["cell_name"]
if not cell_name:
continue
cell = dwg.find_cell(cell_name)
if cell is None:
print(f"Warning: cell '{cell_name}' not found, skipping")
continue
ox = int(row["offset_x"] * 1000) # µm to nm
oy = int(row["offset_y"] * 1000)
rx = int(row.get("repeat_x", 1))
ry = int(row.get("repeat_y", 1))
px = int(row["pitch_x"] * 1000)
py = int(row["pitch_y"] * 1000)
for ix in range(rx):
for iy in range(ry):
x = ox + ix * px
y = oy + iy * py
panel.add_ref(cell, Point(x, y))
# auto-commits on successful exit
print(f"Created panel '{self.panel_name}' with "
f"{len(self.entries)} cell group(s)")
How the Table Option Works
Option.table() creates an editable grid in the tool dialog:
- Columns are defined by
TableColumnobjects - Rows are added/removed by the user via buttons
- The value is a
list[dict]where each dict maps column keys to values - Column types include
string,integer,real,choice, andcell_choice
TableColumn Properties
| Property | Description |
|---|---|
key |
Dict key for this column's value |
label |
Column header text |
col_type |
string, integer, real, choice, cell_choice |
default |
Default value for new rows |
choices |
Options for choice columns |
decimals |
Decimal places for real columns |
min_value |
Minimum for numeric columns |
max_value |
Maximum for numeric columns |
The cell_choice Column Type
A cell_choice column renders a dropdown populated with all cell names from the current drawing. This is the recommended way to let users select cells.
Key Patterns
Accessing Table Rows
Using Database Transactions
Always wrap drawing modifications in a transaction:
with Transaction(dwg) as txn:
# ... modify drawing ...
# auto-commits on successful exit; auto-rolls-back on exception
Unit Conversion
The drawing database uses nanometers internally. Convert from user-facing units: