跳转到内容

linkcad.db

数据库模块提供对 LinkCAD 内存图纸数据库的访问。为了性能,所有数据库对象都由原生实现支撑。

在脚本和插件中请使用稳定的 linkcad.v1.db 命名空间。它完整镜像 linkcad.db 的公开命名空间,因此是同一批对象。

from linkcad.v1 import db
with db.Drawing("scratch") as dwg:
cell = dwg.add_cell("TOP")

或者只导入你要用的名称:

from linkcad.v1.db import (
Drawing, Cell, Layer, Object, DrawingObject, CellObject, Shape,
Polygon, Polyline, Arc, Ellipse, Donut, Nurbs, Text, Ref,
Color, Property, ReadLock, WriteLock, Transaction,
Unit, ObjectType, CellContext, EndCap, FillRule,
BooleanOperation, MergeLayerPolarityResult, TextStyle, TextStyleMask,
)

先了解这些规则

整个 API 始终遵循同一套约定——本模块如此,其他模块也一样。掌握它们之后,你几乎不需要再查文档:

  1. 名词是属性,动词是方法。 shape.areacell.shapespolygon.verticesarc.radiusobj.boundsobj.selected——不带括号。destroy()clone()rotate()reverse()toggle_selection() 保留括号。
  2. 读取器/设置器成对时只是一个可读写属性,绝不是两个名称。 请赋值而不是调用设置器:arc.center = Point(0, 0)layer.enabled = Falseobj.selected = True。几何对象上没有 set_* 方法。
  3. 几何图形由将要拥有它的对象创建。 图纸创建单元和图层(drawing.add_celldrawing.add_layer);单元创建形状(cell.add_polygoncell.add_arccell.add_ref 等)。既没有独立的构造函数,也没有静态工厂。
  4. 当访问器需要参数时,属性覆盖常见情形,filtered_* 方法处理其余——cell.shapes 是全部形状,cell.filtered_shapes(selected_only=True) 则做筛选。

另有两条约定,省去了每个调用点上的转换:

  • 坐标是 Point(x, y),在 linkcad.db 接受坐标的任何地方都可互换——顶点序列、add_arc(center=…)add_text(position=…)text.position = …
  • 图层是 Layer、图层名或 None obj.layer = "METAL1" 把对象移到该图层,若不存在则创建;obj.layer = None 把它移到默认图层。

Drawing

所有版图数据的顶层容器。

图纸把整个数据库保存在内存中,必须确定性地释放——若它存活到解释器退出,进程会崩溃。用作上下文管理器就不会忘记:

from linkcad.v1.db import Drawing
with Drawing("scratch") as dwg:
cell = dwg.add_cell("TOP")
dwg.main_cell = cell
metal = dwg.add_layer("METAL1")
cell.add_polygon(metal, [(0, 0), (10_000, 0), (10_000, 10_000), (0, 10_000)])

在工具插件内部,图纸会传给 run() 并由应用程序拥有——不要在那里销毁它。

属性 / 方法说明
Drawing(name="")创建图纸。省略 name 时 LinkCAD 会分配一个唯一名称
with Drawing(...) as dwg:推荐写法;退出时释放数据库
drawing.name图纸名称
drawing.units每米对应的数据库单位数(浮点数,例如纳米数据库为 1e9)。0.0 表示图纸未声明其单位。数据库中一旦有内容,赋值将抛出 RuntimeError,因此请先设置
drawing.main_cell读取或设置主(顶层)单元
drawing.cells所有单元,列表形式
drawing.layers所有图层,列表形式
drawing.cell(name)按名称查找单元;未找到返回 None
drawing.layer(name)按名称查找图层;未找到返回 None
drawing.add_cell(name)添加单元,或返回同名的已有单元
drawing.add_layer(name)添加图层,或返回同名的已有图层
drawing.modif_time / drawing.access_timeUnix 秒时间戳
drawing.undo_enabled启用或禁用撤销记录
drawing.begin_undo_marker(tag=0) / drawing.end_undo_marker()手动界定一个可撤销事务
drawing.undo() / drawing.redo()撤销或重做上一个事务
drawing.can_undo / drawing.can_redo(是否可用, tag) 元组
drawing.destroy_layer_by_name(name)按名称删除图层;存在则返回 True
drawing.rename_layer(from_name, to_name)重命名图层;成功返回 True
drawing.boolean_layers_by_name(op, result_layer, operand_layer, maximum_error=100, minimum_facets=16)按名称对两个图层做布尔运算;result_layer 是操作数 A 并接收结果
drawing.merge_layer_polarity_group(group_name, maximum_error=100, minimum_facets=16, progress_from=0, progress_to=100)合并一个延迟极性图层组
drawing.merge_all_polarity_groups(maximum_error=100, minimum_facets=16, progress_from=0, progress_to=100)合并所有延迟极性图层组
drawing.destroy()销毁图纸及其内容
drawing.memory_usage原生数据库的内存占用(字节)
drawing.locked只要有任何线程持有数据库锁即为 True
drawing.locked_by_this_thread调用线程持有锁时为 True

后三者读取的是进程级状态,因此既可以通过实例访问,也可以通过类访问:Drawing.memory_usagedwg.memory_usage 给出同一个数值。

修改图纸的图层辅助方法会各自创建自己的可撤销事务。

from linkcad.v1.db import BooleanOperation, Drawing, MergeLayerPolarityResult
with Drawing("example") as dwg:
ok = dwg.boolean_layers_by_name(
BooleanOperation.Or,
result_layer="metal",
operand_layer="vias",
)
result = dwg.merge_all_polarity_groups()
if result != MergeLayerPolarityResult.Success:
raise RuntimeError("Polarity merge failed")

枚举值请用 == 比较,不要用 is。绑定层每次读取都会交回一个新的 Python 对象,因此即使合并成功,result is MergeLayerPolarityResult.Success 仍为 False

drawing.units 表示 1 米等于多少数据库单位,正是把物理尺寸换算成数据库所存整数所需的值:

units_per_meter = drawing.units
if units_per_meter <= 0.0:
raise ValueError("This drawing does not declare its database units")
seven_microns = 7.0 * 1e-6 * units_per_meter

插件有更省事的途径:在 DrawingContextWriterContext 上声明工作单位,LinkCAD 会替你完成换算。参见 linkcad.plugin

Cell

存放形状和单元引用的命名容器。单元来自拥有它的图纸:

cell = drawing.add_cell("TOP") # creates, or returns the existing "TOP"
cell = drawing.cell("TOP") # looks up; None if not found

创建几何图形

每种形状类型都遵循同一模式:add_<something>(layer, …),返回新对象。

坐标可以是 Point 对象,也可以是普通的 (x, y) 元组,两者可互换——无论是在顶点序列中,还是作为单个坐标,例如圆弧、圆或圆环的 center,以及文本的 position。一个对恰好是两个坐标;其他任何形式都会被拒绝,而不是被截断。

方法说明
cell.add_polygon(layer, vertices)添加闭合多边形
cell.add_polyline(layer, vertices, width=0, closed=False, end_cap=EndCap.Round)添加折线
cell.add_arc(layer, center, radius, width=0, start_angle=None, end_angle=None)添加圆弧;当 start_angle <= end_angle 时按逆时针绘制。省略角度则得到完整圆轮廓
cell.add_circle(layer, center, diameter)添加填充圆
cell.add_donut(layer, center, mean_diameter, width)添加圆环;mean_diameter(外径 + 内径) / 2
cell.add_text(layer, content, position, height=1.0, font="")添加文本形状
cell.add_nurbs(layer, control_points, degree, knots, width=0, weights=None, periodic=False)添加 NURBS 曲线;次数必须为 1、2、3 或 5
cell.add_ref(cell, transformation=None, columns=1, rows=1, column_spacing=0, row_spacing=0, layer=None)在此放置另一个单元。将 columns/rows 设为大于 1 可生成阵列
from linkcad.v1.db import Drawing, EndCap
from linkcad.v1.geom import Angle, Point, Transformation
with Drawing("shapes") as dwg:
top = dwg.add_cell("TOP")
dwg.main_cell = top
metal = dwg.add_layer("METAL1")
top.add_polygon(metal, [(0, 0), (1000, 0), (1000, 1000), (0, 1000)])
top.add_polyline(metal, [(0, 0), (2000, 2000)], width=100, end_cap=EndCap.SquareFlat)
top.add_arc(metal, Point(5000, 0), radius=1000, width=50,
start_angle=Angle.from_degrees(0), end_angle=Angle.from_degrees(90))
top.add_circle(metal, Point(0, 5000), diameter=800)
top.add_donut(metal, Point(3000, 5000), mean_diameter=800, width=100)
top.add_text(metal, "TOP", Point(0, 8000), height=500)
stamp = dwg.add_cell("STAMP")
stamp.add_circle(metal, Point(0, 0), diameter=200)
top.add_ref(stamp, Transformation().translate(10_000, 0), columns=4, rows=2,
column_spacing=1000, row_spacing=1000)

检查单元

属性 / 方法说明
cell.name单元名称
cell.shapes该单元拥有的每个形状,均以实际类型返回(PolygonPolyline 等)
cell.filtered_shapes(selected_only=False)筛选后的形状。shapes 等价于使用默认参数调用它
cell.cell_objects该单元持有的全部内容,包括 Ref 对象,均以实际类型返回
cell.filtered_cell_objects(selected_only=False)筛选后的单元对象
cell.bounds本单元及其子单元中所有内容的边界框
cell.filtered_bounds(layer=None)限定到某一图层的边界框
cell.nesting_level最大嵌套层级(顶层单元为 0)
cell.child_levels本单元之下的子层级数
cell.enabled若单元在任一上下文中启用则为 True
cell.enabled_in_context(context_cell)单元在另一个单元中是否启用
cell.enable(enabled=True, context_cell=None, context=CellContext.Descend)启用或禁用单元
cell.uses_layer(layer, context=CellContext.Descend, enabled_only=False)单元在某图层上是否有内容
cell.first_shape_layer所含形状使用的第一个图层
cell.selected_count已选中的单元对象数量
cell.first_selected第一个已选中的单元对象,或 None
cell.modif_time / cell.access_timeUnix 秒时间戳
cell.clone(name)以新名称复制该单元

由于 cell_objects 返回实际类型,isinstance 会按预期工作:

from linkcad.v1 import db
for obj in cell.cell_objects:
if isinstance(obj, db.Ref):
print(f"reference to {obj.ref_cell.name}")
elif isinstance(obj, db.Polygon):
print(f"polygon with {obj.vertex_count} vertices")

Layer

带显示属性的命名图层。图层来自图纸:

metal = drawing.add_layer("METAL1") # creates, or returns the existing layer
metal = drawing.layer("METAL1") # looks up; None if not found
属性 / 方法说明
layer.name图层名称
layer.color以打包 RGBA 整数表示的图层颜色
layer.enabled图层是否启用;可赋值
layer.hidden图层是否隐藏;可赋值
layer.used若有启用的单元在该图层上放置了内容则为 True
layer.move_before(other_layer=None)在图层列表中重新排序
layer.clone(drawing, name)复制图层,可复制到另一图纸。两个参数也可用关键字传入,此时 drawing=None 表示本图纸
layer.destroy()销毁图层

没有静态工厂:drawing.add_layer() 创建图层,drawing.layer() 查找图层。

Object

所有数据库对象的基类。

属性 / 方法说明
obj.id对象 ID
obj.valid对象被销毁后为 False
obj.dynamic_type对象的实际类型,为 ObjectType
obj.drawing该对象所属的 Drawing
obj.destroy()移除并销毁对象。此后它即失效

DrawingObjectLayerCell 的基类)在 Object 之外没有新增内容。

CellObject

单元内所有对象的基类——包括全部形状以及 Ref

属性 / 方法说明
obj.owning_cell包含该对象的 Cell
obj.bounds边界框(Bounds
obj.layer对象的 Layer;可赋值
obj.layer_name图层名称
obj.selected选择状态;可赋值
obj.toggle_selection()切换选择状态

layer 在每种单元对象上都可读写,Ref 也不例外,并接受 Layer、图层名或 None

obj.layer = drawing.layer("METAL1") # a Layer
obj.layer = "METAL2" # by name; created if it does not exist
obj.layer = None # the default layer

toggle_selection() 之所以不受读取器/设置器规则约束,是因为它并不等于 selected = not selected:那会是在两把互相独立的锁下的一次读取加一次写入,而数据库把这个翻转作为单一操作提供。

Shape

几何形状的基类:多边形、折线、圆弧、椭圆、圆环、NURBS 和文本。继承 CellObject

属性 / 方法说明
shape.closed形状闭合则为 True
shape.width以数据库单位表示的线宽(填充形状为 0)
shape.area围成的面积,单位为数据库单位的平方
shape.equivalent_to(other, ignore_sense=True)比较两个形状是否等价
shape.layer / shape.bounds继承自 CellObjectlayer 在那里是可读写的
shape.destroy()移除并销毁该形状

vertices 不属于 Shape:只有 PolygonPolylineNurbs 拥有它。曲线——圆弧、圆、圆环——由圆心和半径描述,由宿主按需细分。

total = sum(shape.area for shape in cell.shapes if shape.closed)
print(f"filled area: {total}")

Polygon

闭合的填充形状。继承 Shape,用 cell.add_polygon(layer, vertices) 创建。

属性 / 方法说明
polygon.verticesPoint 列表表示的顶点。可赋值 Point 对象或 (x, y) 元组
polygon.vertex_count顶点数量
polygon.head / polygon.tail第一个和最后一个顶点
polygon.is_box若多边形为轴对齐矩形则为 True
polygon.is_self_intersecting若轮廓自相交则为 True
polygon.has_bulges若任一边带有非零凸度则为 True
polygon.add_vertex(point)追加一个顶点
polygon.add_vertices(points)追加多个顶点
poly = cell.add_polygon(layer, [(0, 0), (1000, 0), (1000, 1000)])
poly.add_vertex((0, 1000))
poly.vertices = [(p.x * 2, p.y * 2) for p in poly.vertices]

Polyline

带可选宽度的开放或闭合路径。继承 Shape,用 cell.add_polyline(...) 创建。

属性 / 方法说明
polyline.verticesPoint 列表表示的顶点;可赋值
polyline.vertex_count顶点数量
polyline.head / polyline.tail第一个和最后一个顶点
polyline.width以数据库单位表示的路径宽度
polyline.closed路径是否闭合
polyline.end_cap_styleEndCap
polyline.add_vertex(point) / polyline.add_vertices(points)追加顶点
polyline.reverse()反转顶点顺序

Arc

圆弧。继承 Shape,用 cell.add_arc(...) 创建。

from linkcad.v1.geom import Angle, Point
arc = cell.add_arc(
layer,
center=Point(0, 0),
radius=10_000,
width=500,
start_angle=Angle.from_degrees(0),
end_angle=Angle.from_degrees(90),
)
arc.center = Point(1000, 1000)
arc.radius = 12_000
属性说明
arc.center圆心;可赋值
arc.radius以数据库单位表示的半径;可赋值
arc.width以数据库单位表示的描边宽度;可赋值
arc.start_angle起始角(Angle),自 x 轴逆时针测量
arc.end_angle结束角(Angle

Ellipse

圆基元。继承 Shape,用 cell.add_circle(layer, center, diameter) 创建。

属性说明
ellipse.center圆心;可赋值
ellipse.diameter以数据库单位表示的直径;可赋值
ellipse.radius半径,即直径的一半(只读)

Donut

圆环基元。继承 Shape,用 cell.add_donut(layer, center, mean_diameter, width) 创建。

属性说明
donut.center圆心;可赋值
donut.mean_diameter平均直径,(外径 + 内径) / 2;可赋值
donut.width环宽;可赋值
donut.outer_diameter / donut.inner_diameter派生直径(只读)
donut.mean_radius / donut.outer_radius / donut.inner_radius派生半径(只读)

Nurbs

非均匀 B 样条曲线。继承 Shape,用 cell.add_nurbs(...) 创建。

from linkcad.v1.geom import Point
nurbs = cell.add_nurbs(
layer,
control_points=[Point(0, 0), Point(10, 20), Point(20, 20), Point(30, 0)],
degree=3,
knots=[0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0],
width=5,
)
属性 / 方法说明
nurbs.width描边宽度;可赋值
nurbs.degree曲线次数
nurbs.knots / nurbs.knot_count节点向量及其数量
nurbs.vertices / nurbs.control_pointsPoint 对象表示的控制点(两个名称等价)
nurbs.control_point_count控制点数量
nurbs.weights权重向量
nurbs.rational存在权重时为 True
nurbs.periodic曲线为周期/闭合时为 True;可赋值

Text

带格式的文本几何。继承 Shape,用 cell.add_text(...) 创建。

from linkcad.v1.geom import Angle, Point
label = cell.add_text(
layer,
content="Hello LinkCAD",
position=Point(100, 200),
height=12.5,
font="simplex.shx",
)
label.content = "Goodbye"
label.rotate(Angle.from_degrees(90))
属性 / 方法说明
text.content带格式的文本内容;可赋值。text.text 是别名
text.font字体名称;可赋值
text.position附着点;可赋值
text.height文本高度;可赋值
text.line_spacing行距;可赋值
text.box_width换行宽度;可赋值
text.rotation旋转 Angle。赋值时相对于任何外层引用旋转
text.rotate(angle, absolute=False)旋转;absolute=True 时忽略外层引用施加的旋转
text.width_factor水平缩放;可赋值
text.stroke_width描边宽度;可赋值
Text.escape(content)转义纯文本以便按带格式文本存储

Ref

另一个单元的放置实例,附带一个变换。继承 CellObject,用 cell.add_ref(...) 创建。

from linkcad.v1.geom import Transformation
xform = Transformation()
xform.rotate(45.0)
xform.translate(1000, 2000)
ref = parent_cell.add_ref(child_cell, xform)
ref.columns = 4
ref.column_spacing = 5000
属性 / 方法说明
ref.ref_cell被引用的 Cell;可赋值
ref.transformation放置用的 Transformation(作用于整个阵列);可赋值
ref.columns / ref.rows阵列尺寸;1 表示单个实例
ref.column_spacing / ref.row_spacing以数据库单位表示的阵列间距
ref.apply_transformation(transformation)施加一个附加变换
ref.clone(cell=None, transformation=None)复制该引用,可复制到另一个单元
ref.destroy()移除并销毁该引用

cell.add_ref() 会抛出 ValueError,而不是返回一个无法使用的值:循环层次结构(把某个单元放进它自己的后代中)如此,目标单元不存在时也是如此——后者以前会产生一个指向空处的引用。若单元来自用户输入,请捕获该异常:

try:
ref = parent_cell.add_ref(child_cell, xform)
except ValueError as exc:
print(f"cannot place '{child_cell.name}': {exc}")

Color

RGB 颜色值。

属性 / 方法说明
Color() / Color(red, green, blue)构造颜色
color.red / color.green / color.blue各通道(0-255);可赋值

layer.color 是打包的 RGBA 整数,而不是 Color;在 API 要求 Color 的地方请使用 Color

Property

附加在数据库对象上的命名属性。

属性 / 方法说明
prop.owner_type该属性所附着对象的类型,为 ObjectType
prop.destroy()销毁该属性
Property.properties(drawing, holding_type)列出某对象类型的属性名
Property.lookup_type(drawing, name, holding_type)按名称查询属性的类型
from linkcad.v1.db import ObjectType, Property
for name in Property.properties(drawing, ObjectType.Cell):
print(name, Property.lookup_type(drawing, name, ObjectType.Cell))

枚举

ObjectType

数据库对象的具体类型,由 obj.dynamic_typeprop.owner_type 返回,并被 Property 的类方法接受。

分组取值
容器DrawingLayerCell
单元对象RefArcPolygonPolylineDonutTextEllipseNurbs
基类ObjectDrawingObjectCellObjectShapeProperty
属性BooleanPropertyIntegerPropertyRealPropertyStringProperty,每种还各有 Drawing… / Cell… / CellObject… / Layer… 形式
哨兵值Invalid

CellContext

控制操作是否深入子单元。

说明
CellContext.Descend包含子单元
CellContext.DontDescend仅本单元

Unit

命名的测量单位,供单位相关 API 和选项对话框使用。

说明
Unit.PicometerUnit.NanometerUnit.MicronUnit.MillimeterUnit.CentimeterUnit.Meter公制单位
Unit.MilUnit.InchUnit.Feet英制单位
Unit.Point排印点(1/72 英寸)
Unit.Database数据库单位(皮米)
Unit.DotsPerInchUnit.Facets选项对话框使用的分辨率和计数“单位”

还有一个表示“无单位”的成员,但由于 None 是 Python 关键字,只能通过 getattr(Unit, "None") 访问。

drawing.units 返回 Unit。它是表示每米数据库单位数的浮点数——参见 Drawing

EndCap

折线端帽样式。也从 linkcad.v1.plugin 重新导出。

说明
EndCap.Round圆形端帽
EndCap.SquareExtended超出端点延伸的方形端帽
EndCap.SquareFlat在端点处结束的方形端帽

FillRule

多边形填充规则。为写入器 API 也从 linkcad.v1.plugin 重新导出。

说明
FillRule.NonZero非零环绕规则
FillRule.EvenOdd奇偶填充规则

BooleanOperation

用于 drawing.boolean_layers_by_name() 的图层级布尔运算。

说明
BooleanOperation.Or将操作数图层并入结果图层
BooleanOperation.AMinusB从结果图层中减去操作数图层

MergeLayerPolarityResult

延迟极性合并辅助函数的返回结果。

说明
MergeLayerPolarityResult.Success合并成功完成
MergeLayerPolarityResult.Failure合并失败

TextStyle 与 TextStyleMask

TextStyle 包含文本对齐、方向和行距的位标志。TextStyleMask 选择 DrawingBuilder.set_text_style() 会修改哪些样式位组。

枚举常用值
TextStyleDefaultAlignHLeftAlignHCenterAlignHRightAlignVBaselineAlignVBottomAlignVMiddleAlignVMiddleAscentAlignVTopOrientHOrientVLineSpacingExactLineSpacingCompact
TextStyleMaskNone_AlignHAlignVOrientLineSpacing

加锁

需要哪种锁取决于执行上下文。

插件上下文(@tool@format_reader@format_writer

框架会在调用 run()read()write() 之前持有相应的锁。读操作无需显式加锁。需要可撤销的写操作则要使用 Transaction

from linkcad.v1.db import Transaction
def run(self, drawing):
for cell in drawing.cells:
print(cell.name)
with Transaction(drawing, "My Operation"):
result = drawing.add_cell("RESULT")
layer = drawing.add_layer("OUTPUT")
result.add_polygon(layer, [(0, 0), (100, 0), (100, 100), (0, 100)])
return {"summary": "Created RESULT"}

独立脚本(linkcad --python-script

在插件框架之外运行的脚本必须显式获取锁:

from linkcad.v1.db import Drawing, ReadLock, WriteLock
with Drawing("scratch") as dwg:
with ReadLock():
for cell in dwg.cells:
print(cell.name)
with WriteLock():
for layer in list(dwg.layers):
if layer.name.startswith("TEMP_"):
dwg.destroy_layer_by_name(layer.name)

ReadLock

用于独立脚本中只读访问的上下文管理器。

from linkcad.v1.db import ReadLock
with ReadLock():
main = dwg.main_cell
for shape in main.shapes:
print(shape.bounds)

WriteLock

用于独立脚本中写访问的上下文管理器。不会创建撤销条目。

from linkcad.v1.db import WriteLock
with WriteLock():
for cell in dwg.cells:
for obj in list(cell.cell_objects):
if obj.layer_name == "SCRATCH":
obj.destroy()

Transaction

用于写访问的上下文管理器,会在撤销历史中创建单个可撤销条目。请在工具插件中使用。它接受一个显示在“Edit”菜单中的描述字符串,或一个数字标签。

from linkcad.v1.db import Transaction
with Transaction(drawing, "Create Panel"):
panel = drawing.add_cell("PANEL")
panel.add_ref(drawing.cell("UNIT"))

块正常退出时事务提交,异常逸出时回滚。transaction.commit() 可提前提交。