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 millimetresdrawing.units = "um" # in a writer: the numbers you get back are microns| Zulässig | Aliasse |
|---|---|
nm, um, mil, mm, cm, inch, m | nanometer/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 metreDekoratoren
@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 TrueFormatReader 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:
| Factory | Ergebnistyp | UI-Element |
|---|---|---|
Option.integer(label, default, min, max) | int | Drehfeld |
Option.real(label, default, min, max, decimals) | float | Dezimal-Drehfeld |
Option.boolean(label, default) | bool | Kontrollkästchen |
Option.string(label, default) | str | Textfeld |
Option.choice(label, choices, default) | str | Auswahlliste |
Option.path(label, default, file_filter) | str | Dateiauswahl |
Option.color(label, default) | str | Farbauswahl |
Option.table(label, columns, default) | list[dict] | Editierbares Raster |
Option.cell_choice(label, default) | str | Zellen-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 Arbeitseinheitencell(name, main=False)— Kontextmanager, der einenCellContextliefertiter_lines(path, encoding="utf-8")— Zeileniterator mit Fortschrittsanzeigeiter_binary(path, chunk_size=8192)— Binäriterator mit Fortschrittsanzeigeprogress— Fortschritt von0.0bis1.0lesen/setzen
CellContext
Wird von DrawingContext.cell() geliefert.
layer(name)— Kontextmanager, der einenLayerContextliefert
LayerContext
Wird von CellContext.layer() geliefert. Koordinaten sind in der Arbeitseinheit der Zeichnung angegeben.
polygon(vertices)— geschlossenes Polygon aus(x, y)-Tupeln erzeugenpolyline(width, vertices, closed=False)— Polylinie erzeugencircle(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 Arbeitseinheitenflatten— Reduzieren der Hierarchie lesen/setzen. Vor dem Iterieren setzenshapes(cell=None, layer=None)— Formen mit Fortschrittsanzeige durchlaufenshapes_by_layer(cell=None)— Formen nach Ebene gruppiertshapes_by_cell(layer=None)— Formen nach Zelle gruppiertcell_names/layer_names— Namen, als Listencell_count/shape_count— Anzahlenmain_cell_name— Name der Top-Zelleprogress— Fortschritt von0.0bis1.0lesen/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— Ebenennamecell_name: str— Zellennamevertices: list[tuple]—(x, y)-Koordinaten in der Arbeitseinheitis_polygon: bool— Polygon oder polylinienartige Ausgabewidth: int— Polylinienbreite in der Arbeitseinheitis_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.
| Eigenschaft | Beschreibung |
|---|---|
builder.resolution | Einstellungen zur Kurventesselierung |
builder.cell | Aktuelle Zelle oder None |
builder.layer | Aktuelle Ebene oder None |
builder.cell_object | Zuletzt erzeugtes Zellenobjekt oder None |
builder.drawing | Die im Aufbau befindliche Zeichnung |
| Methodengruppe | Methoden |
|---|---|
| Zeichnungsmetadaten | set_drawing_name(name), set_drawing_modif_time(time), set_drawing_access_time(time), set_progress(percent) |
| Zellen | open_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) |
| Ebenen | select_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) |
| Formen | create_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) |
| Text | create_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) |
| Referenzen | create_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) |
| Kontext | save_context(), enter_context(handle), leave_context() |
| Protokollierung | log_info(message), log_warning(message), log_error(message) |
| Eigenschaften und Stile | set_entity_layer_style(flags), set_current_cell_object_real_property(name, value), set_current_cell_object_bool_property(name, value) |
| Schriften | DrawingBuilder.register_odb_fonts(fonts_dir) |
from linkcad.v1.db import EndCap, TextStyle, TextStyleMaskfrom 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.
| Eigenschaft | Beschreibung |
|---|---|
controller.file_name | Pfad der Ausgabedatei |
controller.resolution | Einstellungen zur Kurventesselierung |
controller.drawing | Die zu exportierende Zeichnung |
controller.main_cell | Hauptzelle |
controller.units_per_meter | Datenbankeinheiten pro Meter, oder 0, wenn die Zeichnung sie nicht deklariert |
controller.layers | Alle aktivierten Ebenen, in regulärer Reihenfolge |
controller.layer_names | Namen aller aktivierten Ebenen |
controller.layer_count | Anzahl der aktivierten Ebenen |
controller.cells | Alle Unterzellen, Kinder zuerst (ohne die Hauptzelle) |
controller.cell_names | Namen aller Zellen, einschließlich der Hauptzelle |
controller.cell_count | Anzahl der Zellen, einschließlich der Hauptzelle |
controller.fonts | Alle in der Zeichnung verwendeten Schriften |
controller.object_count | Aktuelle Anzahl verarbeiteter Objekte |
controller.total_object_count | Gesamtzahl zu exportierender Objekte |
controller.fill_rule | Aktuelle FillRule |
controller.transformation | Die lebende Spitze des Koordinaten-Transformationsstapels |
| Methodengruppe | Methoden |
|---|---|
| Protokollierung und Fortschritt | log_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) |
| Exportkonfiguration | set_polygon_mode(holes_mode, polygon_type) |
| Eingeschränkte Zugriffe | filtered_layers(sort_order=SortOrder.Regular), filtered_cells(layer=None) |
| Schrittweise Aufzählung | start_enum_layers(sort_order=SortOrder.Regular), next_layer(), start_enum_cells(), next_cell(layer=None), start_enum_fonts(), next_font() |
| Transformationen | transform_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()| Typ | Zweck |
|---|---|
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-Methode | Beschreibung |
|---|---|
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_NAMESfmt.layer_max_length = 32Ausnahmen
| Ausnahme | Verwendung |
|---|---|
PluginError | Basisklasse aller Plugin-Fehler |
ParseError | Fehler beim Parsen von Dateien; akzeptiert die Schlüsselwortargumente line und path |
WriteError | Fehler beim Schreiben von Dateien |
ValidationError | Fehler bei der Validierung von Optionen |
TableColumn
Datenklasse zur Definition von Spalten einer Tabellenoption:
key: str— Dictionary-Schlüssellabel: str— Spaltenüberschriftcol_type: str—string,integer,real,choice,cell_choicedefault: Any— Standardwertchoices: list[str]— fürchoice-Spaltendecimals: int— fürreal-Spaltenmin_value/max_value— für numerische Spalten
Enums
| Enum | Werte | Verwendung |
|---|---|---|
Phase | ParsedFile, ParsedAll, ClosedOpenCells, ResolvedRefs, SelectedMainCell, ResolvedLayersByBlock, AutoNumberedZ | Phasen-Hooks für die Nachbearbeitung im Reader |
LayerFlags | Normal, ByLayer, ByBlock | Vererbung von Ebeneneigenschaften für DrawingBuilder.set_entity_layer_style() |
SortOrder | Regular, Reverse | Reihenfolge der Ebenenaufzählung für WriterController |
EndCap | Round, SquareExtended, SquareFlat | Re-Export von linkcad.v1.db.EndCap für die Formerzeugung im Builder |
FillRule | NonZero, EvenOdd | Re-Export von linkcad.v1.db.FillRule für die Füllregelbehandlung im Writer |
Die Polygonmodus-Enums stehen in linkcad.v1.plugin zur Verfügung:
| Enum | Werte | Verwendung |
|---|---|---|
HolesMode | Link, Split, Extract, Keep | Wie Löcher beim Polygonexport dargestellt werden |
PolygonType | AllowComplex, ForceSimple | Ob gerenderte Polygone komplex sein dürfen |