linkcad.plugin
插件模块提供创建工具、格式读取器和格式写入器的框架。在脚本和插件中请使用 linkcad.v1.plugin;它完整镜像 linkcad.plugin 的公开命名空间,两者公开完全相同的 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,)工作单位
DrawingContext(导入)和 WriterContext(导出)都带有 units 属性。设置一次之后,跨越插件边界的每个坐标和宽度都以该单位表示——插件不再需要自带缩放表:
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| 可接受的名称 | 别名 |
|---|---|
nm、um、mil、mm、cm、inch、m | nanometer/nanometre、micron/micrometer/micrometre、millimeter/millimetre、centimeter/centimetre、in、meter/metre |
不设置 units(或赋值 None)表示使用原始数据库单位,这与该属性出现之前插件的行为完全一致。名称未知,或图纸未声明其数据库单位时,会抛出 ValidationError。
UNIT_IN_METERS 是唯一的换算表,在此导出,使任何插件都不必自带一份——一份可能悄然不一致的第二张表,正是当年把千倍放置误差带进已发布工具的那条路径:
from linkcad.v1.plugin import UNIT_IN_METERS
to_db_units = UNIT_IN_METERS["mm"] * drawing.units # drawing.units is per metre装饰器
@tool()
把类注册为菜单工具。参见工具装饰器。
@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()
把类注册为文件导入格式。参见格式装饰器。
@format_reader(name="My Format", extensions=["*.myf"])class MyReader(FormatReader): def read(self, path: Path, drawing: DrawingContext) -> None: ...读取器还可以定义可选的 post_process(phase, drawing, resolution) 钩子,它在解析之后 LinkCAD 运行的每个阶段被调用——与内置原生读取器使用的钩子相同。它在与 read() 相同的实例上运行,因此在那里记录的内容仍然可用。返回 False 会中止导入:
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 还提供 LinkCAD 使用的原生桥接方法 parse_file(filepath, builder, file_size, progress_callback)。在用户插件中请实现 read(),让桥接去调用它。
@format_writer()
把类注册为文件导出格式。参见格式装饰器。
@format_writer(name="My Format", extensions=["*.myf"])class MyWriter(FormatWriter): def write(self, path: Path, drawing: WriterContext) -> None: ...FormatWriter 还提供 LinkCAD 使用的原生桥接方法 write_file(filepath, controller, set_entity_sink=None)。在用户插件中请实现 write(),让桥接去调用它。
Option 类
Option 提供用于定义带类型且可持久化选项的工厂方法:
| 工厂方法 | 结果类型 | UI 控件 |
|---|---|---|
Option.integer(label, default, min, max) | int | 微调框 |
Option.real(label, default, min, max, decimals) | float | 小数微调框 |
Option.boolean(label, default) | bool | 复选框 |
Option.string(label, default) | str | 文本框 |
Option.choice(label, choices, default) | str | 下拉列表 |
Option.path(label, default, file_filter) | str | 文件选择器 |
Option.color(label, default) | str | 颜色选择器 |
Option.table(label, columns, default) | list[dict] | 可编辑网格 |
Option.cell_choice(label, default) | str | 单元下拉列表 |
所有工厂方法都接受可选的 tooltip 和 enabled_when 参数,以及一个仅限关键字的 name 参数,用于覆盖自动生成的选项键。
选项键与应用程序自身的设置共享同一命名空间,因此由类属性生成的键是被限定的,而不只是加了前缀:AsciiWriter 上的 flatten 变成 PyPlugin.AsciiWriter.flatten。保留根 PyPlugin 把插件选项挡在应用程序命名空间之外,类名段则把两个都叫 flatten 的插件选项区分开。属性名原样使用,因此键与读取它的 Python 属性拼写一致。
每个插件选项也都可以用该键从命令行设置:
linkcad --PyPlugin.AsciiWriter.precision=5 ...上下文类
DrawingContext
面向格式读取器的 Python 风格构建器接口。
units— 你的坐标所用的单位;参见工作单位cell(name, main=False)— 返回CellContext的上下文管理器iter_lines(path, encoding="utf-8")— 带进度的行迭代器iter_binary(path, chunk_size=8192)— 带进度的二进制迭代器progress— 读取/设置0.0到1.0的进度
CellContext
由 DrawingContext.cell() 返回。
layer(name)— 返回LayerContext的上下文管理器
LayerContext
由 CellContext.layer() 返回。坐标以图纸的工作单位表示。
polygon(vertices)— 从(x, y)元组创建闭合多边形polyline(width, vertices, closed=False)— 创建折线circle(center, diameter)— 创建圆
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
面向格式写入器的 Python 风格读取接口。
units— 你收到的几何图形所用的单位;参见工作单位flatten— 读取/设置层次结构展平。请在迭代前设置shapes(cell=None, layer=None)— 带进度地迭代形状shapes_by_layer(cell=None)— 按图层分组的形状shapes_by_cell(layer=None)— 按单元分组的形状cell_names/layer_names— 名称列表cell_count/shape_count— 计数main_cell_name— 顶层单元名progress— 读取/设置0.0到1.0的进度
除 shapes() 及其两个分组形式外,其余都是名词,因而都是属性。cells() 和 layers() 生成器已被移除:它们产出的字符串正是 cell_names 和 layer_names 已经持有的内容,而同一个列表有两种写法是一个没有正确答案的选择。
无论是否展平,shapes() 都是遍历几何图形的唯一入口。flatten = False 时它遍历每个单元自身的形状,把引用留给你处理。flatten = True 时它通过宿主控制器渲染层次结构,宿主在下降时应用变换栈并沿途细分曲线——因此圆弧、圆、圆环和 NURBS 只有在启用展平时才会以多边形和折线的形式到达写入器。
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")展平遍历需要宿主写入器,因此只能在 FormatWriter.write() 内部使用。手工构造的 WriterContext 只能使用 flatten = False。
ShapeInfo
WriterContext.shapes() 产出的数据类:
layer_name: str— 图层名cell_name: str— 单元名vertices: list[tuple]— 以工作单位表示的(x, y)坐标is_polygon: bool— 多边形还是类折线输出width: int— 以工作单位表示的折线宽度is_closed: bool— 形状是否闭合
原生格式接口
DrawingContext 和 WriterContext 覆盖了大多数格式插件的需求。对于需要与 LinkCAD 底层格式 API 完全对等的插件,还公开了原生的 DrawingBuilder 和 WriterController 接口。
DrawingBuilder
DrawingBuilder 由 LinkCAD 运行时提供给导入插件,用户代码不会直接创建它。
| 属性 | 说明 |
|---|---|
builder.resolution | 曲线细分设置 |
builder.cell | 当前单元,或 None |
builder.layer | 当前图层,或 None |
builder.cell_object | 最近创建的单元对象,或 None |
builder.drawing | 正在构建的图纸 |
| 方法分组 | 方法 |
|---|---|
| 图纸元数据 | set_drawing_name(name)、set_drawing_modif_time(time)、set_drawing_access_time(time)、set_progress(percent) |
| 单元 | 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) |
| 图层 | 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) |
| 形状 | 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) |
| 文本 | 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) |
| 引用 | 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) |
| 上下文 | save_context()、enter_context(handle)、leave_context() |
| 日志 | log_info(message)、log_warning(message)、log_error(message) |
| 属性与样式 | set_entity_layer_style(flags)、set_current_cell_object_real_property(name, value)、set_current_cell_object_bool_property(name, value) |
| 字体 | 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 由 LinkCAD 运行时提供给导出插件,用户代码不会直接创建它。
| 属性 | 说明 |
|---|---|
controller.file_name | 输出文件路径 |
controller.resolution | 曲线细分设置 |
controller.drawing | 正在导出的图纸 |
controller.main_cell | 主单元 |
controller.units_per_meter | 每米对应的数据库单位数;图纸未声明时为 0 |
controller.layers | 全部已启用图层,按常规顺序 |
controller.layer_names | 全部已启用图层的名称 |
controller.layer_count | 已启用图层的数量 |
controller.cells | 全部子单元,子级优先(不含主单元) |
controller.cell_names | 全部单元的名称,含主单元 |
controller.cell_count | 单元数量,含主单元 |
controller.fonts | 图纸中使用的全部字体 |
controller.object_count | 当前已处理的对象数 |
controller.total_object_count | 待导出对象总数 |
controller.fill_rule | 当前的 FillRule |
controller.transformation | 坐标变换栈的当前栈顶 |
| 方法分组 | 方法 |
|---|---|
| 日志与进度 | 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) |
| 导出配置 | set_polygon_mode(holes_mode, polygon_type) |
| 带筛选的访问器 | filtered_layers(sort_order=SortOrder.Regular)、filtered_cells(layer=None) |
| 逐步枚举 | start_enum_layers(sort_order=SortOrder.Regular)、next_layer()、start_enum_cells()、next_cell(layer=None)、start_enum_fonts()、next_font() |
| 变换 | transform_point(point)、transform_distance(distance) |
layers 和 cells 覆盖常见情形;filtered_layers(sort_order) 和 filtered_cells(layer) 暴露这些属性隐藏起来的参数——与 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): ...推送一侧属于框架
LinkCAD 以两种方式导出几何图形:插件把形状拉出来,或者控制器把渲染好的实体推给写入器。只有推送方向能够展平,因为只有控制器持有沿引用下降时建立的变换栈——手工驱动推送一侧的插件,得到的是没有放置信息的形状,而且不会有任何错误提示。
因此 _render_cell、_render_cell_in_layer_order、_flatten_cell_hierarchy 和 _get_shapes 都带下划线前缀:它们属于框架,由 WriterContext 代你驱动。请设置 drawing.flatten = True 并迭代 drawing.shapes(),这是同一次遍历,只是放置信息已经应用好了。
HolesMode 和 PolygonType 可从稳定的插件模块获取,供 WriterController.set_polygon_mode() 使用:
from linkcad.v1.plugin import HolesMode, PolygonType
controller.set_polygon_mode(HolesMode.Link, PolygonType.AllowComplex)DialogSpec
DialogSpec 以声明方式描述格式或工具的选项对话框。它对应 Rust 插件的对话框模型,并序列化为 LinkCAD UI 所消费的 JSON 格式。
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()| 类型 | 用途 |
|---|---|
DialogSpec(title) | 顶层对话框描述符 |
DialogGroup(label, row=None) | 一组相关字段;row 相同的组可以并排布局 |
UiWidget(option_name, label, kind) | 组内的单个 UI 元素 |
WidgetKind(kind_type, **kwargs) | 控件类型及其配置 |
ChoiceItem(value, display, description=None) | 列表/下拉框的选项条目 |
| 构建器方法 | 说明 |
|---|---|
group(label) | 开始一个组 |
group_in_row(label, row) | 在指定行开始一个组 |
bool_field(option_name, label, default) | 复选框 |
int_field(option_name, label, min, max, default) | 整数微调框 |
real_field(option_name, label, min, max, default) | 小数微调框 |
string_field(option_name, label, default) | 文本行 |
combo_field(option_name, label, choices, default) | 由字符串构成的下拉框 |
single_select_list(option_name, label, static_choices=None, inspection_key=None) | 单选列表 |
multi_select_list(option_name, label, static_choices=None, inspection_key=None, value_separator=",") | 多选列表 |
note(text) | 仅供显示的说明 |
separator() | 水平分隔线 |
to_json() | 序列化为 JSON |
ChoiceItem 提供 simple(value)、with_display(value, display) 和 full(value, display, description) 三个工厂方法。
格式元数据
读取器和写入器装饰器会在插件类上附加一个 FormatInfo 对象。对于更底层的格式约束,请使用 linkcad.v1.conv.Format 和 FormatAttributes;参见 linkcad.conv。
from linkcad.v1.conv import Format, FormatAttributes
fmt = Format()fmt.attributes = FormatAttributes.LAYER_NAMES | FormatAttributes.CELL_NAMESfmt.layer_max_length = 32异常
| 异常 | 用途 |
|---|---|
PluginError | 所有插件错误的基类 |
ParseError | 文件解析错误;接受 line 和 path 关键字参数 |
WriteError | 文件写入错误 |
ValidationError | 选项验证错误 |
TableColumn
用于定义表格选项列的数据类:
key: str— 字典键label: str— 列标题col_type: str—string、integer、real、choice、cell_choicedefault: Any— 默认值choices: list[str]— 用于choice列decimals: int— 用于real列min_value/max_value— 用于数值列
枚举
| 枚举 | 取值 | 用途 |
|---|---|---|
Phase | ParsedFile、ParsedAll、ClosedOpenCells、ResolvedRefs、SelectedMainCell、ResolvedLayersByBlock、AutoNumberedZ | 读取器后处理的阶段钩子 |
LayerFlags | Normal、ByLayer、ByBlock | 用于 DrawingBuilder.set_entity_layer_style() 的图层属性继承 |
SortOrder | Regular、Reverse | WriterController 的图层枚举顺序 |
EndCap | Round、SquareExtended、SquareFlat | 为构建器创建形状而重新导出的 linkcad.v1.db.EndCap |
FillRule | NonZero、EvenOdd | 为写入器填充规则而重新导出的 linkcad.v1.db.FillRule |
写入器的多边形模式枚举可从 linkcad.v1.plugin 获取:
| 枚举 | 取值 | 用途 |
|---|---|---|
HolesMode | Link、Split、Extract、Keep | 多边形导出时孔的表示方式 |
PolygonType | AllowComplex、ForceSimple | 渲染出的多边形是否可以是复杂多边形 |