资治通鉴(摘录2)
卫鞅欲变法,秦人不悦。卫鞅言于秦孝公曰:“夫民不可与虑始,而可与乐成。论至德者不和于俗,成大功者不谋于众。是以圣人苟可以强国,不法其故。”甘龙曰:“不然。缘法而治者,吏习而民安之。”卫鞅曰:“常人安于故俗,学者溺于所闻,以此两者,居官守法可也,非所与论于法之外也。智者作法,愚者制焉;贤者更礼,不肖者拘焉。”公曰:“善。”以卫鞅为左庶长,卒定变法之令。令民为什伍而相收司、连坐,告奸者与斩敌首同赏,不……
本文主要用于讲采用python的ezdxf模块进行DXF格式的CAD创建。主要讲清楚了具体对象结构和使用流程。在此基础上结合相关帮助文件可以进行较为复杂的图形绘制了。……
最近有一个项目是接口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.
对于其如何实现读写的,待以后有空再研究下。
# 用途 在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打开如下图所示,此时文字图层、样式、位置等属性均为默认。
在ezdxf中,相关对象大类包括(这里大类是指其下可能包含子对象):
- Drawing 文档
- Sections
- Tables 图层、线性、文字类型等
- Blocks
- Layouts 模型空间、布局空间等
- Groups
- DXF Entities 绘制图形实体
- DXF Objects
基本的逻辑是:
注:类/对象名均为大驼峰式,方法/属性均为小写。
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()
方法可以适用带路径的文件名从而保存在指定文件夹下。
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()
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__() |
得到对象迭代 |
上述Layer、Style对象等等均是DXF Entity Base类的子类,通过上述new()
方法生成,而DXF Graphic Entity Base继承于DXF Entity Base,又是各类图形实体如Line,Point,Circle的父类。这些图形实体通过前述add
方法生成。
上面讲到在得到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 # 赋予多段线全局宽度
~~填充问题~~有待补充
基于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= '图层名称'
同样的我们可以实现新建文字样式并赋予给具体字体。
# 新建文字样式
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") # 保存图形
对于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综合应用于一些工程上去得。
如果对于大量的重复的且内容确定的对象的绘制,例如轴网,图框等等,尝试采用插入块的形式。这需要做两步:
(1)在一个既有的DXF中建立块,此块即为所需插入的内容;
(2)建立两个DXF之间的连接,并在新DXF中插入块。此操作需要应用 Importer类。具体示例如下:
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版本才具有此功能
1、没有多线命令