Zum Inhalt springen

Ein Format-Plugin schreiben

Dieses Tutorial zeigt, wie Sie LinkCAD mit Python um Unterstützung für eigene Dateiformate erweitern.

Format-Reader (Import)

Ein Format-Reader überführt ein externes Dateiformat in die Zeichnungsdatenbank von LinkCAD.

Beispiel: einfacher Koordinatendatei-Reader

from pathlib import Path
from linkcad.v1.plugin import format_reader, FormatReader, Option, DrawingContext
@format_reader(
name="Coordinate File",
extensions=["*.xyz", "*.coord"],
description="Simple X Y coordinate files",
)
class CoordReader(FormatReader):
units = Option.choice(
"Units",
choices=["nm", "um", "mil", "mm", "cm", "inch", "m"],
default="um",
tooltip="Physical units of the coordinate values in the file",
)
layer_name = Option.string("Layer name", default="imported")
def read(self, path: Path, drawing: DrawingContext) -> None:
# Name the unit once and LinkCAD converts every coordinate you hand it.
drawing.units = self.units
with drawing.cell(path.stem, main=True) as cell:
with cell.layer(self.layer_name) as layer:
vertices = []
for line in drawing.iter_lines(path):
line = line.strip()
if not line or line.startswith("#"):
# Blank line = end of polygon
if vertices:
layer.polygon(vertices)
vertices = []
continue
parts = line.split()
vertices.append((float(parts[0]), float(parts[1])))
# Don't forget the last polygon
if vertices:
layer.polygon(vertices)

Kernkonzepte

  • @format_reader() registriert die Klasse als Importformat
  • extensions bestimmt, welche Dateien der Reader verarbeitet
  • DrawingContext stellt eine Builder-API zum Erzeugen von Zellen, Ebenen und Formen bereit
  • drawing.units benennt die Einheit Ihrer Koordinaten, sodass Sie nie von Hand skalieren müssen
  • drawing.iter_lines() liest Zeilen mit automatischer Fortschrittsanzeige
  • drawing.iter_binary() liest Binärdaten mit Fortschrittsanzeige

Arbeitseinheiten

Wird drawing.units einmal gesetzt, sind alle Koordinaten und Breiten, die Sie übergeben, in dieser Einheit zu verstehen. Zulässig sind nm, um, mil, mm, cm, inch und m sowie die naheliegenden Aliasse (micron, millimetre, in, …).

Bleibt der Wert ungesetzt, gelten reine Datenbankeinheiten — genau wie vor Einführung der Eigenschaft. Ein unbekannter Name oder eine Zeichnung ohne deklarierte Datenbankeinheiten löst ValidationError aus.

Ein Reader, der Koordinaten mit einer eigenen Skalierungstabelle multipliziert, ist veraltet — löschen Sie die Tabelle und benennen Sie stattdessen die Einheit.

DrawingContext-API

Eigenschaft / MethodeBeschreibung
unitsEinheit Ihrer Koordinaten; None für reine Datenbankeinheiten
cell(name, main=False)Kontextmanager — erzeugt bzw. öffnet eine Zelle
iter_lines(path, encoding="utf-8")Textzeilen mit Fortschrittsanzeige durchlaufen
iter_binary(path, chunk_size=8192)Binärblöcke mit Fortschrittsanzeige durchlaufen
progressFortschritt lesen/setzen (0.0 bis 1.0)

CellContext-API

MethodeBeschreibung
layer(name)Kontextmanager — wählt eine Ebene aus

LayerContext-API

Koordinaten sind in der Arbeitseinheit der Zeichnung angegeben.

MethodeBeschreibung
polygon(vertices)Geschlossenes Polygon aus (x, y)-Tupeln erzeugen
polyline(width, vertices, closed=False)Polylinie erzeugen
circle(center, diameter)Kreis erzeugen

Nachbearbeitungs-Hook

Ein Reader kann optional post_process() definieren, um an den Phasen teilzunehmen, die LinkCAD nach dem Parsen durchläuft — 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 CoordReader(FormatReader):
def post_process(self, phase, drawing, resolution) -> bool:
if phase == Phase.ResolvedRefs:
... # every reference now points at a real cell
return True

Die Phasen kommen in dieser Reihenfolge: ParsedFile, ParsedAll, ClosedOpenCells, ResolvedRefs, SelectedMainCell, ResolvedLayersByBlock, AutoNumberedZ.

Format-Writer (Export)

Ein Format-Writer schreibt LinkCAD-Geometrie in eine Datei.

Beispiel: einfacher Text-Writer

from pathlib import Path
from linkcad.v1.plugin import format_writer, FormatWriter, Option, WriterContext
@format_writer(
name="Simple Text",
extensions=["*.stxt"],
)
class SimpleWriter(FormatWriter):
units = Option.choice(
"Output units",
choices=["nm", "um", "mil", "mm", "cm", "inch", "m"],
default="um",
)
separator = Option.choice(
"Separator",
choices=["Space", "Comma", "Tab"],
default="Space",
)
precision = Option.integer("Decimal places", default=3, min=0, max=10)
flatten = Option.boolean("Flatten hierarchy", default=False)
def write(self, path: Path, drawing: WriterContext) -> None:
sep = {"Space": " ", "Comma": ", ", "Tab": "\t"}[self.separator]
# Name the unit once and every coordinate arrives already converted.
drawing.units = self.units
drawing.flatten = self.flatten
with open(path, "w") as f:
for layer_name, shapes in drawing.shapes_by_layer():
f.write(f"# Layer: {layer_name}\n")
for shape in shapes:
for x, y in shape.vertices:
f.write(f"{x:.{self.precision}f}{sep}"
f"{y:.{self.precision}f}\n")
f.write("\n")

Arbeitseinheiten und Reduzieren

drawing.units funktioniert für Writer genauso wie für Reader, nur in die andere Richtung: einmal gesetzt, kommt jede Koordinate und Breite aus shapes() bereits in dieser Einheit an. Ein Writer, der durch eine eigene Skalierungstabelle dividiert, ist veraltet.

drawing.flatten wählt die Traversierung. Setzen Sie den Wert vor dem Iterieren:

  • flatten = False durchläuft die Formen jeder Zelle und überlässt Ihnen die Zellenreferenzen. Kurven kommen ohne Stützpunkte zurück, weil ihre Tesselierung den Host erfordert.
  • flatten = True lässt den Host die Hierarchie rendern; dieser wendet dabei den Transformationsstapel an und tesseliert Kurven unterwegs. Kreisbögen, Kreise, Ringe und NURBS erreichen einen Writer daher nur bei aktiviertem Reduzieren als Polygone und Polylinien.

In beiden Fällen ist shapes() die einzige Traversierung — einen separaten Iterator für reduzierte Geometrie gibt es nicht.

WriterContext-API

Eigenschaft / MethodeBeschreibung
unitsEinheit, in der Sie die Geometrie erhalten; None für reine Datenbankeinheiten
flattenReduzieren der Hierarchie lesen/setzen. Vor dem Iterieren setzen
shapes(cell=None, layer=None)Formen durchlaufen (optionaler Zellen-/Ebenenfilter)
shapes_by_layer(cell=None)Formen nach Ebene gruppiert durchlaufen
shapes_by_cell(layer=None)Formen nach Zelle gruppiert durchlaufen
cell_names / layer_namesListen aller Zellen- bzw. Ebenennamen
cell_countAnzahl der Zellen
shape_countGesamtzahl der Formen
main_cell_nameName der Top-Zelle
progressFortschritt lesen/setzen (0.0 bis 1.0)

ShapeInfo-Eigenschaften

EigenschaftTypBeschreibung
layer_namestrEbenenname
cell_namestrZellenname
verticeslist[tuple](x, y)-Koordinaten in der Arbeitseinheit
is_polygonboolTrue für Polygone, False für Polylinien
widthintPolylinienbreite in der Arbeitseinheit (0 für Polygone)
is_closedboolOb die Form geschlossen ist

Fehlerbehandlung

Verwenden Sie die eingebauten Ausnahmeklassen für klare Fehlermeldungen:

from linkcad.v1.plugin import ParseError, WriteError, ValidationError
# In a reader:
raise ParseError("Invalid coordinate", line=42, path=path)
# In a writer:
raise WriteError("Cannot write to locked file")
# In option validation:
raise ValidationError("Scale must be positive")

Nächste Schritte