基于ezdxf的DXF编写

2020年03月16日 星期一

本文主要用于讲采用python的ezdxf模块进行DXF格式的CAD创建。主要讲清楚了具体对象结构和使用流程。在此基础上结合相关帮助文件可以进行较为复杂的图形绘制了。……

1 前言

​ 最近有一个项目是接口ETABS数据在CAD中显示结果,希望做到的不打开CAD而实现。理论上可以手工编写DXF文件,即按照DXF文档格式来实现,如下面是节选一个DXF文件,DXF文件是由多个节组成的,每个节又是由多个组组成。每个组占两行,第一行为组码,是一个整数;第二行是组值,其数据类型取决于组码。

  0
SECTION
  2
HEADER
  9
$ACADVER
  1
AC1024
  9
$ACADMAINTVER
 70
6
  9
$DWGCODEPAGE
  3
ANSI_936
  9
$LASTSAVEDBY
  1
ezdxf
  9
$INSBASE
 10
0.0
 20
0.0
 30
0.0
  9
$EXTMIN
 10
-100
 20
-100
 30
-100
  9
$EXTMAX
 10
100
 20
100
 30
100
  9
$LIMMIN
 10
0.0

尽管可以借助DXF官方帮助文件来理清楚节、组,但是存在问题:

(1)条目非常之多。对于仅仅绘制一个圆型的DXF,共计16710行。尽管实际上这些条目中大部分并不是必须的,换句话说可以通过需要确定哪些项必须要输入,然后利用print语句实现。例如下面是一个自己编的写DXF文档的:

# 用途:输出DXF格式所需的圆语句
# 日期: 2019年2月23日
# 版权所有©kiritanimirei.cn

# r:半径
# x,y :坐标
# la:图层
# fh:文件对象
def circle(fh,r,x,y,la,ha):
    print('0', sep=' ', end='\n', file=fh)  
    print('CIRCLE', sep=' ', end='\n', file=fh)
    print('5', sep=' ', end='\n', file=fh)  
    print(ha, sep=' ', end='\n', file=fh)    
    print('100', sep=' ', end='\n', file=fh)  
    print('AcDbCircle', sep=' ', end='\n', file=fh)  
    print('10', sep=' ', end='\n', file=fh)
    print(x, sep=' ', end='\n', file=fh)
    print('20', sep=' ', end='\n', file=fh)
    print(y, sep=' ', end='\n', file=fh)
    print('30', sep=' ', end='\n', file=fh)
    print(0, sep=' ', end='\n', file=fh)
    print('40', sep=' ', end='\n', file=fh)
    print(r, sep=' ', end='\n', file=fh)
    print('8', sep=' ', end='\n', file=fh)
    print(la, sep=' ', end='\n', file=fh)
# 用途:输出DXF格式所需的圆语句
# 日期: 2019年2月23日
# 版权所有©kiritanimirei.cn

from my_dxf import circle
try:
    fh = open("testfile.dxf", "w")
    print(0, sep=' ', end='\n', file=fh) 
    print('SECTION', sep=' ', end='\n', file=fh)
    print(2, sep=' ', end='\n', file=fh) 
    print('ENTITIES', sep=' ', end='\n', file=fh)    
    circle(fh,100,0,0,'0',1)
    print(0, sep=' ', end='\n', file=fh) 
    print('ENDSEC', sep=' ', end='\n', file=fh)
    print(0, sep=' ', end='\n', file=fh) 
    print('EOF', sep=' ', end='\n', file=fh)    
except IOError:
    print("Error: 没有找到文件或读取文件失败")
else:
    print("内容写入文件成功")
    fh.close()

上述方法实现了绘制一个半径100的圆,但是太费劲。一则要在my_dxf编写好多类似于circle的代码,二来还得了解清楚DXF文件格式与内容,且很可能产生额外的错误。如果对于大规模绘图而言并不现实。

​ 故想着类似于pyautocad的用法,找到一个第三方库。有若干可供选择的库,有一个叫 ezdxf的,而且不断在更新之中,最新版本为2020年2月29日的0.11.1版本。所需Python版本不低于Python 3.6。相关链接:官方帮助文件

​ ezdxf用于读写dxf文件,既可以是已经存在的,也可以是创建新的。使用它的好处在于不需要过于了解DXF文件自身格式。

ezdxf is a Python interface to the DXF (drawing interchange file) format developed by Autodesk, it allows developers to read and modify existing DXF drawings or create new DXF drawings.

​ 对于其如何实现读写的,待以后有空再研究下。

2 HelloWorld

# 用途    在CAD中绘制文字“Hello World"
# 日期    2020年3月15日 星期日 天气晴
# 作者    -Y-__-Y-
# 版权    ©kiritanimirei.cn
# python 3.6.5 
# ezdxf 0.11.1

import ezdxf
doc = ezdxf.new('AC1024') # 建立一个新的CAD2010文档
doc.encoding='gbk' # 设置编码为简体中文
msp = doc.modelspace() # 获取模型空间
msp.add_text("Hello World") # 添加文字
doc.saveas("helloworld.dxf") # 保存图形

运行生成了一个CAD2010版本的DXF文件,用AutoCAD打开如下图所示,此时文字图层、样式、位置等属性均为默认。

批注 2020-03-15 153001

3 ezdxf基本对象结构

相关帮助文件

在ezdxf中,相关对象大类包括(这里大类是指其下可能包含子对象):

基本的逻辑是:

  1. 得到一个Drawing对象(相当于ActiveX中的ActiveDocument,也即所要保存的DXF文件)
  2. 在Drawing对象中添加Table对象,具体可为图层、线性、文字类型等子对象;
  3. 创建或得到一个Layout对象,Layout对象具体可为Modelspace、Paperspace或BlockLayout。
  4. 在Layout对象中绘制图形实体对象DXFEntity,具体可为直线、圆、文字等子对象
  5. 对所绘制图形实体进行属性修改。

注:类/对象名均为大驼峰式,方法/属性均为小写。

3.1 Drawing对象

Drawing对象相当于ActiveX中的ActiveDocument,生成方法:

doc = ezdxf.new(dxfversion='AC1027') 

后续均用doc指代所得到的Drawing对象。这里dxfversion为版本信息,可为:

Version AutoCAD Release
AC1009 AutoCAD R12
AC1012 AutoCAD R13 -> R2000
AC1014 AutoCAD R14 -> R2000
AC1015 AutoCAD R2000
AC1018 AutoCAD R2004
AC1021 AutoCAD R2007
AC1024 AutoCAD R2010
AC1027 AutoCAD R2013
AC1032 AutoCAD R2018

如果是需要出现中文字体,有必要通过doc.encoding='gbk' 设置编码为简体中文,更多的编码格式如下所示:

DXF Python Name
ANSI_874 cp874 Thai
ANSI_932 cp932 Japanese
ANSI_936 gbk UnifiedChinese
ANSI_949 cp949 Korean
ANSI_950 cp950 TradChinese
ANSI_1250 cp1250 CentralEurope
ANSI_1251 cp1251 Cyrillic
ANSI_1252 cp1252 WesternEurope
ANSI_1253 cp1253 Greek
ANSI_1254 cp1254 Turkish
ANSI_1255 cp1255 Hebrew
ANSI_1256 cp1256 Arabic
ANSI_1257 cp1257 Baltic
ANSI_1258 cp1258 Vietnam

其具有的相关方法:

方法 用途 属性 用途
save()/saveas() 保存文件/另存文件 encoding 设置编码格式
modelspace() 获取模型空间 layouts 获取Layouts对象
layouts() 获取所有的布局空间 tables 获取TableSection对象

对于saveas() 方法可以适用带路径的文件名从而保存在指定文件夹下。

3.2 Layouts/BaseLayout对象

Layouts对象相当于BaseLayout对象的集合,并且可用于生成新的BaseLayout对象。而BaseLayout对象又存在两层继承关系:

BaseLayout对象→Layout对象→Modelspace/Paperspace/BlockLayout对象

对于一个DXF文件而言,只有一个模型空间,即Modelspace对象,而布局空间Paperspace、块BlockLayout等可以通过new()方法添加。

表——Layouts对象方法与属性

方法 用途 属性 用途
new() 建立新Layout对象
__len__() 得到对象长度
__iter__() 得到对象迭代器
modelspace() 获取模型空间

BaseLayout对象具有相应方法可以添加实体,其基本格式是add_实体类型名,如

add_point(location, dxfattribs) # 点
add_line(start,end, dxfattribs) # 直线
add_circle(center,radius, dxfattribs) # 圆形
add_text(text, dxfattribs) # 文字
add_polyline2d(points, dxfattribs) # 多段线

上述方法具体是在Modelspace、Paperspace或BlockLayout对象中实现,一般作图就在模型空间中进行,对于Modelspace对象,其有快捷方式可以得到:

# 获取模型空间
msp = doc.modelspace()

3.3 TableSection/Table对象

TableSection对象相当于各类Table对象的集合,其可通过doc.tables得到。而Table对象包含以下一些子对象:

LayerTable LineType StyleTable DimStyleTable

上述Table也相当于集合。以LayerTable为例,其为Layer对象的集合。要得到这些对象理论上可以通过TableSection对象的属性得到,不过通过Drawing对象属性快捷得到:

doc.layers
# Shortcut for Drawing.tables.layers,Layer对象集合

doc.styles
# Shortcut for Drawing.tables.styles,Style对象集合

doc.dimstyles
# Shortcut for Drawing.tables.dimstyles

doc.llinetypes
# Shortcut for Drawing.tables.linetypes

表——Table对象方法与属性

方法 用途 属性 用途
new() 建立新Table对象
__len__() 得到对象长度
__iter__() 得到对象迭代

3.4 DXF Entity Base/DXF Graphic Entity Base对象

​ 上述Layer、Style对象等等均是DXF Entity Base类的子类,通过上述new()方法生成,而DXF Graphic Entity Base继承于DXF Entity Base,又是各类图形实体如Line,Point,Circle的父类。这些图形实体通过前述add方法生成。

4 绘制基本实体

​ 上面讲到在得到Modelspace对象后即可以在模型空间绘制图形了,相关实际操作如下:

cir = msp.add_circle((0,0),radius=100)  # 圆形
line = msp.add_line(start = (0,0),end =(100,100)) # 直线
points = [(0, 0), (3, 0), (6, 3), (6, 6)]
polyline = msp.add_lwpolyline(points) # 绘制多段线并返回对象
text = msp.add_text("Hello World") # 添加文字

上述绘制完的实体属性是默认的,修改实体属性可以有两种方法:

1、在新建实体时可以通过设置dxfattribs参数进行初始化,dxfattribs为字典格式,相关键值可参见https://ezdxf.mozman.at/docs/dxfentities/dxfgfx.html 。需要注意的是键名应去掉前缀dxf.如下面

# 添加文字
text = msp.add_text("Hello World", dxfattribs={'layer':'MyLayer'}) 

2、另一种即对生成后的对象设置属性

polyline.dxf.const_width = 10  # 赋予多段线全局宽度

~~填充问题~~有待补充

5 图层、文字类型、线性等

5.1 图层处理

​ 基于LayerTable对象可以生成Layer对象,即CAD中的图层,对于图层的操作即可以在此基础上展开。

# 新建图层
layer = doc.layers.new(name='图层名称', dxfattribs={'color': 7})

表——Layer对象属性

方法 用途 属性 用途
dxf.name 得到图层名称
dxf.color 得到图层颜色
dxf.linetype 得到图层线性

注意到对于Layer对象,其非继承属性均有前缀dxf.

​ 需要注意的是,LayerTable对象作为图层集合,ezdxf并不允许通过索引方式获取,即layers[i]这种会报错。故要得到图层要么通过get('图层名称')或者通过迭代器。

​ 由于LayerTable对象继承于Table对象,故继承了相应方法,可以使用迭代器。

layers = doc.layers.__iter__() # 生成图层迭代器
# 输出所有图层名称
for obj in layers:
    print(obj.dxf.name)

参见https://ezdxf.mozman.at/docs/tables/layer_table_entry.html

注意到LayerTable对象具有快捷方式,故实际设置图层通过以下代码实现。

# 新建图层
doc.layers.new(name='图层名称', dxfattribs={'color': 7})
# 设置图层
GraphicEntity.dxf.layer= '图层名称'

5.2 文字样式

​ 同样的我们可以实现新建文字样式并赋予给具体字体。

# 新建文字样式
doc.styles.new(name='文字样式', dxfattribs={'font' : 'simhei.ttf'})
# 设置文字样式
text.dxf.style= '文字样式'

需要注意的是文字样式的名字,如黑体并不是‘黑体’,而是‘simhei.ttf’,就是在电脑字体文件夹中该字体的文件名。通常而言,在CAD重显示数字时采用形函数字体比较合适,如系统自带的txt.ttf,如此打印时文字笔画比较细,文字较小时 容易看清。

​ 综上我们对HelloWorld程序代码进行修改如下所示。

# 用途    在CAD中绘制文字“Hello World"
# 日期    2020年3月15日 星期日 天气晴
# 作者    -Y-__-Y-
# 版权    ©kiritanimirei.cn

import ezdxf
doc = ezdxf.new('AC1024') # 建立一个新的CAD2010文档
doc.encoding='gbk' # 设置编码为简体中文
msp = doc.modelspace() # 获取模型空间
doc.layers.new(name='MyLayer', dxfattribs={'color': 10}) # 添加新图层

# 添加文字并设置图层及旋转
att = {'layer': 'MyLayer','rotation':30}
text = msp.add_text("Hello World",dxfattribs=att) 

text.set_pos([10,10]) # 设置文字位置
doc.styles.new(name='MyText', dxfattribs={'font' : 'simhei.ttf'})
text.dxf.style= 'MyText'
doc.saveas("helloworld.dxf") # 保存图形

批注 2020-03-16 111256

​ 对于Text对象更多设置,可参见https://ezdxf.mozman.at/docs/dxfentities/text.html ,其中对于对齐设置可通过set_align()方法实现,其中参数如下所示。

Vertical Left Center Right
Top TOP_LEFT TOP_CENTER TOP_RIGHT
Middle MIDDLE_LEFT MIDDLE_CENTER MIDDLE_RIGHT
Bottom BOTTOM_LEFT BOTTOM_CENTER BOTTOM_RIGHT
Baseline LEFT CENTER RIGHT

后续讲通过一些实例再讲解如何将ezdxf综合应用于一些工程上去得。

6 导入外部DXF内容

​ 如果对于大量的重复的且内容确定的对象的绘制,例如轴网,图框等等,尝试采用插入块的形式。这需要做两步:

(1)在一个既有的DXF中建立块,此块即为所需插入的内容;

(2)建立两个DXF之间的连接,并在新DXF中插入块。此操作需要应用 Importer类。具体示例如下:

![批注 2020-05-08 164354](/static/img/批注 2020-05-08 164354.png)import ezdxf
from ezdxf.addons import Importer

sdoc = ezdxf.readfile('helloworld.dxf')
tdoc = ezdxf.new()

importer = Importer(sdoc, tdoc)

importer.import_block('bk')
importer.finalize()

msp = tdoc .modelspace()
blockref=msp.add_blockref('bk', [0,0]) # 插入块参照

tdoc.saveas('imported.dxf')

上述先导入一个叫‘bk'的块,然后插入。

块属性

对于涉及到块属性的内容,可以采用insert类下add_auto_attribs()方法进行修改。

values = {
    '标记':'值'
}

blockref.add_auto_attribs(values)

需要注意的是0.12版本才具有此功能

批注 2020-05-08 164354

0 注意点

1、没有多线命令


更新于 2020年5月8日 星期五 天气晴

精选博客

资治通鉴(摘录2)

卫鞅欲变法,秦人不悦。卫鞅言于秦孝公曰:“夫民不可与虑始,而可与乐成。论至德者不和于俗,成大功者不谋于众。是以圣人苟可以强国,不法其故。”甘龙曰:“不然。缘法而治者,吏习而民安之。”卫鞅曰:“常人安于故俗,学者溺于所闻,以此两者,居官守法可也,非所与论于法之外也。智者作法,愚者制焉;贤者更礼,不肖者拘焉。”公曰:“善。”以卫鞅为左庶长,卒定变法之令。令民为什伍而相收司、连坐,告奸者与斩敌首同赏,不……

继 续 阅 读

稳定与屈曲

一直觉得这些写稳定的书籍有一个事情没有讲清楚,就是什么是稳定。一上来就讲几种失稳(稳定分叉、不稳定分叉等)会让人觉得有些不知所措。这里就谈谈自己的理解。……

继 续 阅 读

关于结构体系

在整理新体系的时候想起一个问题,有几个问题困扰着: (1)多层建筑采用异形柱是会比剪力墙更经济么? (2)一直有个核心筒的概念,那么剪力墙布置成核心筒形式会比剪力墙布置在外部会更好?……

继 续 阅 读