Zum Inhalt springen

linkcad.plugin

Das Plugin-Modul stellt das Framework zum Erstellen von Tools, Format-Readern und Format-Writern bereit. Verwenden Sie in Skripten und Plugins linkcad.v1.plugin; es spiegelt den gesamten öffentlichen Namensraum von linkcad.plugin, beide bieten also exakt dieselbe API.

from linkcad.v1.plugin import (
tool, Tool, Option, TableColumn,
format_reader, FormatReader, DrawingContext, CellContext, LayerContext, DrawingBuilder,
format_writer, FormatWriter, WriterContext, ShapeInfo, WriterController,
DialogSpec, ChoiceItem,
EventLog, Phase, LayerFlags, SortOrder,
HolesMode, PolygonType, EndCap, FillRule,
PluginError, ParseError, WriteError, ValidationError,
UNIT_IN_METERS,
)

Arbeitseinheiten

Sowohl DrawingContext (Import) als auch WriterContext (Export) tragen eine Eigenschaft units. Setzen Sie sie einmal, und jede Koordinate und Breite, die die Plugin-Grenze überschreitet, wird in dieser Einheit ausgedrückt — ein Plugin braucht nie eine eigene Skalierungstabelle:

drawing.units = "mm" # in a reader: the numbers you hand in are millimetres
drawing.units = "um" # in a writer: the numbers you get back are microns
ZulässigAliasse
nm, um, mil, mm, cm, inch, mnanometer/nanometre, micron/micrometer/micrometre, millimeter/millimetre, centimeter/centimetre, in, meter/metre

Bleibt units ungesetzt (oder wird None zugewiesen), gelten reine Datenbankeinheiten — genau so, wie sich Plugins vor Einführung der Eigenschaft verhalten haben. Ein unbekannter Name oder eine Zeichnung, die ihre Datenbankeinheiten nicht deklariert, löst ValidationError aus.

UNIT_IN_METERS ist die eine Umrechnungstabelle, hier exportiert, damit kein Plugin eine eigene mitschleppen muss — eine zweite Tabelle, die stillschweigend abweichen kann, ist genau der Weg, auf dem einmal ein tausendfacher Platzierungsfehler in ein ausgeliefertes Tool gelangt ist:

from linkcad.v1.plugin import UNIT_IN_METERS
to_db_units = UNIT_IN_METERS["mm"] * drawing.units # drawing.units is per metre

Dekoratoren

@tool()

Registriert eine Klasse als Menü-Tool. Siehe Tool-Dekorator.

@tool(name="My Tool", menu="Tools/Custom")
class MyTool(Tool):
def run(self, drawing) -> dict:
# `drawing` is a linkcad.v1.db.Drawing owned by the application.
return {"summary": f"{len(drawing.cells)} cells"}

@format_reader()

Registriert eine Klasse als Importformat. Siehe Format-Dekoratoren.

@format_reader(name="My Format", extensions=["*.myf"])
class MyReader(FormatReader):
def read(self, path: Path, drawing: DrawingContext) -> None:
...

Ein Reader kann außerdem einen optionalen Hook post_process(phase, drawing, resolution) definieren, der nach dem Parsen für jede von LinkCAD durchlaufene Phase aufgerufen wird — derselbe Hook, den auch die eingebauten nativen Reader verwenden. Er läuft auf derselben Instanz wie read(), sodass dort Notiertes weiterhin verfügbar ist. Ein False bricht den Import ab:

from linkcad.v1.plugin import Phase
class MyReader(FormatReader):
def post_process(self, phase, drawing, resolution) -> bool:
if phase == Phase.ResolvedRefs:
... # every reference now points at a real cell
return True

FormatReader stellt außerdem parse_file(filepath, builder, file_size, progress_callback) bereit, die von LinkCAD genutzte native Brücke. Implementieren Sie in eigenen Plugins read() und lassen Sie die Brücke es aufrufen.

@format_writer()

Registriert eine Klasse als Exportformat. Siehe Format-Dekoratoren.

@format_writer(name="My Format", extensions=["*.myf"])
class MyWriter(FormatWriter):
def write(self, path: Path, drawing: WriterContext) -> None:
...

FormatWriter stellt außerdem write_file(filepath, controller, set_entity_sink=None) bereit, die von LinkCAD genutzte native Brücke. Implementieren Sie in eigenen Plugins write() und lassen Sie die Brücke es aufrufen.

Die Klasse Option

Option bietet Factory-Methoden zum Definieren typisierter, dauerhafter Optionen:

FactoryErgebnistypUI-Element
Option.integer(label, default, min, max)intDrehfeld
Option.real(label, default, min, max, decimals)floatDezimal-Drehfeld
Option.boolean(label, default)boolKontrollkästchen
Option.string(label, default)strTextfeld
Option.choice(label, choices, default)strAuswahlliste
Option.path(label, default, file_filter)strDateiauswahl
Option.color(label, default)strFarbauswahl
Option.table(label, columns, default)list[dict]Editierbares Raster
Option.cell_choice(label, default)strZellen-Auswahlliste

Alle Factories akzeptieren optionale Parameter tooltip und enabled_when sowie ein reines Schlüsselwortargument name, das den generierten Optionsschlüssel überschreibt.

Optionsschlüssel teilen sich einen Namensraum mit den Einstellungen der Anwendung. Ein aus einem Klassenattribut generierter Schlüssel ist daher eingegrenzt statt bloß mit einem Präfix versehen: aus flatten in AsciiWriter wird PyPlugin.AsciiWriter.flatten. Die reservierte Wurzel PyPlugin hält Plugin-Optionen aus dem Namensraum der Anwendung heraus, und das Klassensegment hält zwei Plugins auseinander, die beide eine Option flatten nennen. Der Attributname wird unverändert übernommen, Schlüssel und lesendes Python-Attribut sind also gleich geschrieben.

Jede Plugin-Option ist unter diesem Schlüssel auch über die Befehlszeile setzbar:

linkcad --PyPlugin.AsciiWriter.precision=5 ...

Kontextklassen

DrawingContext

Pythonische Builder-Schnittstelle für Format-Reader.

  • units — Einheit Ihrer Koordinaten; siehe Arbeitseinheiten
  • cell(name, main=False) — Kontextmanager, der einen CellContext liefert
  • iter_lines(path, encoding="utf-8") — Zeileniterator mit Fortschrittsanzeige
  • iter_binary(path, chunk_size=8192) — Binäriterator mit Fortschrittsanzeige
  • progress — Fortschritt von 0.0 bis 1.0 lesen/setzen

CellContext

Wird von DrawingContext.cell() geliefert.

  • layer(name) — Kontextmanager, der einen LayerContext liefert

LayerContext

Wird von CellContext.layer() geliefert. Koordinaten sind in der Arbeitseinheit der Zeichnung angegeben.

  • polygon(vertices) — geschlossenes Polygon aus (x, y)-Tupeln erzeugen
  • polyline(width, vertices, closed=False) — Polylinie erzeugen
  • circle(center, diameter) — Kreis erzeugen
def read(self, path: Path, drawing: DrawingContext) -> None:
drawing.units = "um"
with drawing.cell(path.stem, main=True) as cell:
with cell.layer("METAL1") as layer:
layer.polygon([(0, 0), (100, 0), (100, 100), (0, 100)]) # a 100 µm square
layer.polyline(2.5, [(0, 0), (200, 200)])
layer.circle((500, 500), 50)

WriterContext

Pythonische Lese-Schnittstelle für Format-Writer.

  • units — Einheit, in der Sie die Geometrie erhalten; siehe Arbeitseinheiten
  • flatten — Reduzieren der Hierarchie lesen/setzen. Vor dem Iterieren setzen
  • shapes(cell=None, layer=None) — Formen mit Fortschrittsanzeige durchlaufen
  • shapes_by_layer(cell=None) — Formen nach Ebene gruppiert
  • shapes_by_cell(layer=None) — Formen nach Zelle gruppiert
  • cell_names / layer_names — Namen, als Listen
  • cell_count / shape_count — Anzahlen
  • main_cell_name — Name der Top-Zelle
  • progress — Fortschritt von 0.0 bis 1.0 lesen/setzen

Alles außer shapes() und den beiden Gruppierungen ist ein Substantiv und daher eine Eigenschaft. Die Generatoren cells() und layers() sind entfallen: sie lieferten genau die Zeichenketten, die cell_names und layer_names schon enthalten, und zwei Schreibweisen einer Liste sind eine Wahl ohne richtige Antwort.

shapes() ist die einzige Traversierung für Geometrie, reduziert oder nicht. Mit flatten = False werden die Formen jeder Zelle durchlaufen und Referenzen bleiben Ihnen überlassen. Mit flatten = True wird die Hierarchie über den Host-Controller gerendert, der beim Absteigen den Transformationsstapel anwendet und Kurven unterwegs tesseliert — Kreisbögen, Kreise, Ringe und NURBS erreichen einen Writer also nur bei aktiviertem Reduzieren als Polygone und Polylinien.

def write(self, path: Path, drawing: WriterContext) -> None:
drawing.units = "mm"
drawing.flatten = True
with open(path, "w") as f:
for shape in drawing.shapes():
for x, y in shape.vertices: # millimetres
f.write(f"{x:.4f} {y:.4f}\n")
f.write("\n")

Die reduzierte Traversierung benötigt den Host-Writer und steht daher innerhalb von FormatWriter.write() zur Verfügung. Ein selbst gebauter WriterContext kann nur flatten = False verwenden.

ShapeInfo

Datenklasse, die WriterContext.shapes() liefert:

  • layer_name: str — Ebenenname
  • cell_name: str — Zellenname
  • vertices: list[tuple](x, y)-Koordinaten in der Arbeitseinheit
  • is_polygon: bool — Polygon oder polylinienartige Ausgabe
  • width: int — Polylinienbreite in der Arbeitseinheit
  • is_closed: bool — ob die Form geschlossen ist

Native Formatschnittstellen

DrawingContext und WriterContext decken die meisten Format-Plugins ab. Die nativen Schnittstellen DrawingBuilder und WriterController sind ebenfalls verfügbar, für Plugins, die direkte Parität mit der Low-Level-Format-API von LinkCAD brauchen.

DrawingBuilder

DrawingBuilder wird Import-Plugins von der LinkCAD-Laufzeit übergeben. Er wird nicht von Benutzercode erzeugt.

EigenschaftBeschreibung
builder.resolutionEinstellungen zur Kurventesselierung
builder.cellAktuelle Zelle oder None
builder.layerAktuelle Ebene oder None
builder.cell_objectZuletzt erzeugtes Zellenobjekt oder None
builder.drawingDie im Aufbau befindliche Zeichnung
MethodengruppeMethoden
Zeichnungsmetadatenset_drawing_name(name), set_drawing_modif_time(time), set_drawing_access_time(time), set_progress(percent)
Zellenopen_cell(name_or_number, is_main_cell=False, reopen=False), close_cell(), delete_cell(), set_cell_name(name), set_cell_modif_time(time), set_cell_access_time(time), find_cell(name)
Ebenenselect_layer(name_or_number), select_layer(major, minor), set_layer_comment(comment), set_layer_color(rgba), set_layer_enabled(enabled), set_layer_z(z), set_layer_polarity_positive(positive=True), set_layer_polarity_group(group_name), set_layer_polarity_sequence(sequence)
Formencreate_polygon(vertices, make_simple=False), create_polygon_with_bulges(vertices, bulges), create_rectangle(corner1, corner2), create_polyline(width, vertices, closed=False, end_cap=EndCap.Round), create_circle(center, diameter, donut=False), create_arc(center, radius, width, start_angle=0.0, end_angle=360.0, end_cap=EndCap.Round), create_donut(center, mean_diameter, width), create_nurbs(width, degree, knots, control_points, periodic=False), create_nurbs_weighted(width, degree, knots, control_points, weights, periodic=False)
Textcreate_text(), set_text_position(position), set_text_height(height), set_text_stroke_width(width), set_text_style(flags, mask=TextStyleMask.None_), set_formatted_text(text), set_unformatted_text(text), set_text_font(font_name), set_text_width_factor(factor), set_text_obliquing_angle(angle_degrees), set_text_mirrored_x(mirror=True), set_text_mirrored_y(mirror=True), set_text_rotation(angle_degrees, absolute=False), set_text_box_width(width), set_text_line_spacing(spacing)
Referenzencreate_ref(cell_name_or_number), scale_ref(scale, absolute=False), mirror_ref_x(negate=True), mirror_ref_y(negate=True), rotate_ref(angle_degrees, absolute=False), translate_ref(position), set_ref_array_spacing(dx, dy), set_ref_array_size(cols, rows)
Kontextsave_context(), enter_context(handle), leave_context()
Protokollierunglog_info(message), log_warning(message), log_error(message)
Eigenschaften und Stileset_entity_layer_style(flags), set_current_cell_object_real_property(name, value), set_current_cell_object_bool_property(name, value)
SchriftenDrawingBuilder.register_odb_fonts(fonts_dir)
from linkcad.v1.db import EndCap, TextStyle, TextStyleMask
from linkcad.v1.geom import Point
builder.open_cell("TOP", is_main_cell=True)
builder.select_layer("metal1")
builder.create_arc(Point(0, 0), radius=10_000, width=500, end_cap=EndCap.Round)
builder.create_text()
builder.set_text_position(Point(0, 12_000))
builder.set_text_height(1_000)
builder.set_text_style(TextStyle.AlignHCenter, TextStyleMask.AlignH)
builder.set_unformatted_text("TOP")
builder.close_cell()

WriterController

WriterController wird Export-Plugins von der LinkCAD-Laufzeit übergeben. Er wird nicht von Benutzercode erzeugt.

EigenschaftBeschreibung
controller.file_namePfad der Ausgabedatei
controller.resolutionEinstellungen zur Kurventesselierung
controller.drawingDie zu exportierende Zeichnung
controller.main_cellHauptzelle
controller.units_per_meterDatenbankeinheiten pro Meter, oder 0, wenn die Zeichnung sie nicht deklariert
controller.layersAlle aktivierten Ebenen, in regulärer Reihenfolge
controller.layer_namesNamen aller aktivierten Ebenen
controller.layer_countAnzahl der aktivierten Ebenen
controller.cellsAlle Unterzellen, Kinder zuerst (ohne die Hauptzelle)
controller.cell_namesNamen aller Zellen, einschließlich der Hauptzelle
controller.cell_countAnzahl der Zellen, einschließlich der Hauptzelle
controller.fontsAlle in der Zeichnung verwendeten Schriften
controller.object_countAktuelle Anzahl verarbeiteter Objekte
controller.total_object_countGesamtzahl zu exportierender Objekte
controller.fill_ruleAktuelle FillRule
controller.transformationDie lebende Spitze des Koordinaten-Transformationsstapels
MethodengruppeMethoden
Protokollierung und Fortschrittlog_info(message), log_warning(message), log_error(message), set_progress(percent), init_progress_counter(force_flattened=False), set_object_count(count), set_total_object_count(count)
Exportkonfigurationset_polygon_mode(holes_mode, polygon_type)
Eingeschränkte Zugriffefiltered_layers(sort_order=SortOrder.Regular), filtered_cells(layer=None)
Schrittweise Aufzählungstart_enum_layers(sort_order=SortOrder.Regular), next_layer(), start_enum_cells(), next_cell(layer=None), start_enum_fonts(), next_font()
Transformationentransform_point(point), transform_distance(distance)

layers und cells decken den Normalfall ab; filtered_layers(sort_order) und filtered_cells(layer) legen die Parameter offen, die diese Eigenschaften verbergen — dasselbe Paar wie cell.shapes / cell.filtered_shapes.

from linkcad.v1.plugin import SortOrder
for layer in controller.filtered_layers(SortOrder.Reverse):
for cell in controller.filtered_cells(layer):
...

Die Push-Seite gehört dem Framework

LinkCAD exportiert Geometrie auf zwei Wegen: ein Plugin holt Formen heraus, oder der Controller schiebt gerenderte Entitäten zum Writer. Nur die Push-Richtung kann reduzieren, weil nur der Controller den Transformationsstapel hält, der beim Absteigen durch Referenzen entsteht — ein Plugin, das sie von Hand steuert, bekommt Formen ohne ihre Platzierung und keinen Fehler, der das sagt.

_render_cell, _render_cell_in_layer_order, _flatten_cell_hierarchy und _get_shapes sind daher mit Unterstrich versehen: sie gehören dem Framework, und WriterContext steuert sie für Sie. Setzen Sie drawing.flatten = True und iterieren Sie drawing.shapes() — dieselbe Traversierung, mit bereits angewandter Platzierung.

HolesMode und PolygonType stehen im stabilen Plugin-Modul zur Verfügung und werden von WriterController.set_polygon_mode() verwendet:

from linkcad.v1.plugin import HolesMode, PolygonType
controller.set_polygon_mode(HolesMode.Link, PolygonType.AllowComplex)

DialogSpec

DialogSpec beschreibt einen Format- oder Tool-Optionsdialog deklarativ. Es entspricht dem Rust-Plugin-Dialogmodell und wird in das von der LinkCAD-UI konsumierte JSON-Format serialisiert.

from linkcad.v1.plugin import DialogSpec, ChoiceItem
spec = (
DialogSpec("Import Options")
.group("Geometry")
.bool_field("merge", "Merge polygons", True)
.int_field("facets", "Minimum facets", 4, 256, 32)
.combo_field("units", "Units", ["nm", "um", "mm"], "um")
.group_in_row("Layers", row=1)
.single_select_list(
"layer",
"Layer",
[ChoiceItem.with_display("metal1", "Metal 1")],
)
)
json_payload = spec.to_json()
TypZweck
DialogSpec(title)Deskriptor des Dialogs auf oberster Ebene
DialogGroup(label, row=None)Gruppe zusammengehöriger Felder; Gruppen mit gleichem row können nebeneinander angeordnet werden
UiWidget(option_name, label, kind)Einzelnes UI-Element in einer Gruppe
WidgetKind(kind_type, **kwargs)Widget-Typ samt Konfiguration
ChoiceItem(value, display, description=None)Eintrag für Liste oder Auswahlliste
Builder-MethodeBeschreibung
group(label)Eine Gruppe beginnen
group_in_row(label, row)Eine Gruppe in einer bestimmten Zeile beginnen
bool_field(option_name, label, default)Kontrollkästchen
int_field(option_name, label, min, max, default)Ganzzahl-Drehfeld
real_field(option_name, label, min, max, default)Dezimal-Drehfeld
string_field(option_name, label, default)Textzeile
combo_field(option_name, label, choices, default)Auswahlliste aus Zeichenketten
single_select_list(option_name, label, static_choices=None, inspection_key=None)Einfachauswahlliste
multi_select_list(option_name, label, static_choices=None, inspection_key=None, value_separator=",")Mehrfachauswahlliste
note(text)Reiner Anzeigehinweis
separator()Waagerechte Trennlinie
to_json()Nach JSON serialisieren

ChoiceItem bietet die Factory-Methoden simple(value), with_display(value, display) und full(value, display, description).

Formatmetadaten

Reader- und Writer-Dekoratoren hängen ein FormatInfo-Objekt an die Plugin-Klasse. Für Low-Level-Formatbeschränkungen verwenden Sie linkcad.v1.conv.Format und FormatAttributes; siehe linkcad.conv.

from linkcad.v1.conv import Format, FormatAttributes
fmt = Format()
fmt.attributes = FormatAttributes.LAYER_NAMES | FormatAttributes.CELL_NAMES
fmt.layer_max_length = 32

Ausnahmen

AusnahmeVerwendung
PluginErrorBasisklasse aller Plugin-Fehler
ParseErrorFehler beim Parsen von Dateien; akzeptiert die Schlüsselwortargumente line und path
WriteErrorFehler beim Schreiben von Dateien
ValidationErrorFehler bei der Validierung von Optionen

TableColumn

Datenklasse zur Definition von Spalten einer Tabellenoption:

  • key: str — Dictionary-Schlüssel
  • label: str — Spaltenüberschrift
  • col_type: strstring, integer, real, choice, cell_choice
  • default: Any — Standardwert
  • choices: list[str] — für choice-Spalten
  • decimals: int — für real-Spalten
  • min_value / max_value — für numerische Spalten

Enums

EnumWerteVerwendung
PhaseParsedFile, ParsedAll, ClosedOpenCells, ResolvedRefs, SelectedMainCell, ResolvedLayersByBlock, AutoNumberedZPhasen-Hooks für die Nachbearbeitung im Reader
LayerFlagsNormal, ByLayer, ByBlockVererbung von Ebeneneigenschaften für DrawingBuilder.set_entity_layer_style()
SortOrderRegular, ReverseReihenfolge der Ebenenaufzählung für WriterController
EndCapRound, SquareExtended, SquareFlatRe-Export von linkcad.v1.db.EndCap für die Formerzeugung im Builder
FillRuleNonZero, EvenOddRe-Export von linkcad.v1.db.FillRule für die Füllregelbehandlung im Writer

Die Polygonmodus-Enums stehen in linkcad.v1.plugin zur Verfügung:

EnumWerteVerwendung
HolesModeLink, Split, Extract, KeepWie Löcher beim Polygonexport dargestellt werden
PolygonTypeAllowComplex, ForceSimpleOb gerenderte Polygone komplex sein dürfen