Skip to content

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 TableColumn objects
  • 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, and cell_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

for row in self.entries:
    cell_name = row["cell_name"]
    offset_x = row["offset_x"]

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:

x_nm = int(x_um * 1000)  # µm → nm