目录[-]
CAD有众多接口可以允许多种编程语言进行接入,对CAD进行二次开发,特别是CAD通过ap加载的插件生态丰富,目前由两个论坛尤为知名,一是明经CAD社区http://www.mjtd.com/,二是晓东CAD家园http://bbs.xdcad.net/,笔者曾想学了Autolisp语言对CAD进行二次开发,但该语言生态教差,网络资料稀缺,故还是选择使用python对CAD进行二次开发。
目前使用python对CAD进行二次开发有一个名为pyautocad的第三方库,通过使用pip安装后即可进行二次开发及测试,主要开发思路为三个方向:
- 根据数据在CAD软件自动批量绘画图形
- 根据现有CAD图形进行批量扫描图形信息
- 对现状CAD图形进行属性修改
考虑到程序开发的实用性与通用性,笔者开发了前两者的代码程序,程序成果如下。
一、代码批量绘画CAD图形
实际效果
界面主代码
#作者:冯桂和
#功能:CAD画图功能集合界面
from tkinter import filedialog
from tkinter import messagebox
import tkinter as tk
from tkinter import *
import tkinter.simpledialog
from draw_line import PLine
from draw_text import Text
from draw_circle import Circle
#创建主窗口、标题、大小
win=tk.Tk()
win.title("CAD画图工具-冯桂和")
win.geometry('1050x800+400+100')
#全局变量
pllist=[]
plxy=[]
textlist=[]
circlelist=[]
#按钮函数
def b1():
global pllist
text1.delete('1.0', END)
ctrlv=win.clipboard_get()
text1.insert(END,ctrlv)
text1.see("end")
pllist=text1.get('1.0', END)
def b2():
try:
global pllist
global plxy
pllist=pllist.split('\n') #将内容进行分割成列表pllist
if pllist[-1]=='':
del pllist[-1] #删除最后一行空内容
plnum=[] #新建一个列表plnum存储每条PL线段数作为标识符
k=0
while k<len(pllist)-1:
j=int(pllist[k])
plnum.append(j)
k=k+j+1
plxy=[] #新建一个列表plxy,存储每段坐标,一维是一条多段线,二维是该多段线的xy坐标,三维是x和y坐标
sum=1
for i in range(len(plnum)):
length=plnum[i]
a=[]
for i in range(length):
b=pllist[i+sum]
b=b.split(' ')
for i in range(2):
b[i]=float(b[i])
a.append(b)
sum=sum+length+1
plxy.append(a)
text1.delete('1.0', END)
for i in range(len(plxy)):
for j in range(len(plxy[i])):
text1.insert(END,plxy[i][j])
text1.insert(END,'\n')
text1.insert(END,'\n')
text0.insert(END,'格式转换成功!\n')
text1.see("end")
except:
text0.insert(END,'错误!\n')
text0.see("end")
def b3():
try:
global pllist
global plxy
if pllist[-1]=='':
del pllist[-1]
pllist=pllist.split('x y ')
if pllist[0]=='':
del pllist[0]
for i in range(len(pllist)):
pllist[i]=pllist[i].split('\n')
for i in range(len(pllist)):
del pllist[i][0]
del pllist[i][-1]
del pllist[-1][-1]
for i in range(len(pllist)):
for j in range(len(pllist[i])):
pllist[i][j]=pllist[i][j].split('\t')
pllist[i][j][0]=float(pllist[i][j][0])
pllist[i][j][1]=float(pllist[i][j][1])
del pllist[i][j][-1]
plxy=pllist.copy()
text1.delete('1.0', END)
for i in range(len(plxy)):
for j in range(len(plxy[i])):
text1.insert(END,plxy[i][j])
text1.insert(END,'\n')
text1.insert(END,'\n')
text1.see("end")
except:
text0.insert(END,'错误!\n')
text0.see("end")
def b4():
text0.insert(END,'保留按钮,功能待开发!\n')
text0.see("end")
print('b4按钮函数')
def b5():
global plxy
if len(pllist)==0:
text0.insert(END,'空!\n')
text0.see("end")
elif len(plxy)==0:
text0.insert(END,'未格式化,默认进行<yao格式>处理\n')
text0.see("end")
b2()
if not len(plxy)==0:
pldraw=PLine(plxy)
text0.insert(END,'{}\n'.format(pldraw))
text0.see("end")
def b6():
global textlist
text2.delete('1.0', END)
ctrlv=win.clipboard_get()
text2.insert(END,ctrlv)
text2.see("end")
textlist=text2.get('1.0', END)
def b7():
global textlist
try:
textlist=textlist.split('\n')
if textlist[-1]=='':
del textlist[-1]
for i in range(len(textlist)):
textlist[i]=textlist[i].split('\t')
del textlist[i][2]
textlist[i][0]=float(textlist[i][0])
textlist[i][1]=float(textlist[i][1])
print(textlist)
except:
text0.insert(END,'错误!\n')
text0.see("end")
def b8():
text0.insert(END,'保留按钮,功能待开发!\n')
text0.see("end")
def b9():
text0.insert(END,'保留按钮,功能待开发!\n')
text0.see("end")
def b10():
global textlist
if len(textlist)==0:
text0.insert(END,'空!\n')
text0.see("end")
elif type(textlist)==type(''):
text0.insert(END,'未格式化,默认进行<yao格式>处理\n')
text0.see("end")
b7()
text_h=tkinter.simpledialog.askfloat(title= "请输入字高",prompt = "请输入字高",initialvalue=10)
textdraw=Text(textlist,text_h)
text0.insert(END,'{}\n'.format(textdraw))
else:
text_h=tkinter.simpledialog.askfloat(title= "请输入字高",prompt = "请输入字高",initialvalue=10)
textdraw=Text(textlist,text_h)
text0.insert(END,'{}\n'.format(textdraw))
def b11():
global circlelist
text3.delete('1.0', END)
ctrlv=win.clipboard_get()
text3.insert(END,ctrlv)
text3.see("end")
circlelist=text3.get('1.0', END)
def b12():
global circlelist
circlelist=circlelist.replace(' ','\t')
circlelist=circlelist.split('\n')
if circlelist[-1]=='':
del circlelist[-1]
if circlelist[-1]=='':
del circlelist[-1]
for i in range(len(circlelist)):
circlelist[i]=circlelist[i].split('\t')
circlelist[i][0]=float(circlelist[i][0])
circlelist[i][1]=float(circlelist[i][1])
circlelist[i][2]=float(circlelist[i][2])
print(circlelist)
text3.delete('1.0', END)
for i in range(len(circlelist)):
text3.insert(END,circlelist[i])
text3.insert(END,'\n')
text3.see("end")
def b13():
text0.insert(END,'保留按钮,功能待开发!\n')
text0.see("end")
def b14():
text0.insert(END,'保留按钮,功能待开发!\n')
text0.see("end")
def b15():
global circlelist
if type(circlelist)==type(''):
text0.insert(END,'未格式化,默认进行<x y r格式>处理\n')
text0.see("end")
b12()
if not len(circlelist)==0:
circledraw=Circle(circlelist)
text0.insert(END,'{}\n'.format(circledraw))
text0.see("end")
else:
text0.insert(END,'空!\n')
text0.see("end")
#创建多行文本控件
text0=tk.Text(win,width=40,height=10,font=('微软雅黑',15))
text0.place(x=10,y=400,width=1000,height=400)
text1=tk.Text(win,width=40,height=10,font=('微软雅黑',10))
text1.grid(row=1,column=0,padx=10,pady=8)
text2=tk.Text(win,width=40,height=10,font=('微软雅黑',10))
text2.grid(row=1,column=1,padx=10,pady=8)
text3=tk.Text(win,width=40,height=10,font=('微软雅黑',10))
text3.grid(row=1,column=2,padx=10,pady=8)
#创建按钮
Button1=tk.Button(win,text='粘贴多段线数据',command=b1,font=('微软雅黑',15))
Button2=tk.Button(win,text='yao格式',command=b2,font=('微软雅黑',15))
Button3=tk.Button(win,text='tqddx格式',command=b3,font=('微软雅黑',15))
Button4=tk.Button(win,text='保留按钮',command=b4,font=('微软雅黑',15))
Button5=tk.Button(win,text='绘画CAD多段线',command=b5,font=('微软雅黑',15))
Button1.grid(row=2,column=0,padx=5,pady=5,sticky="new")
Button2.grid(row=3,column=0,padx=5,pady=5,sticky="w")
Button3.grid(row=3,column=0,padx=5,pady=5,sticky="n")
Button4.grid(row=3,column=0,padx=5,pady=5,sticky="e")
Button5.grid(row=4,column=0,padx=5,pady=5,sticky="new")
Button6=tk.Button(win,text='粘贴文本内容数据',command=b6,font=('微软雅黑',15))
Button7=tk.Button(win,text='gxtq格式',command=b7,font=('微软雅黑',15))
Button8=tk.Button(win,text='保留按钮',command=b8,font=('微软雅黑',15))
Button9=tk.Button(win,text='保留按钮',command=b9,font=('微软雅黑',15))
Button10=tk.Button(win,text='绘画CAD文本',command=b10,font=('微软雅黑',15))
Button6.grid(row=2,column=1,padx=5,pady=5,sticky="new")
Button7.grid(row=3,column=1,padx=5,pady=5,sticky="w")
Button8.grid(row=3,column=1,padx=5,pady=5,sticky="n")
Button9.grid(row=3,column=1,padx=5,pady=5,sticky="e")
Button10.grid(row=4,column=1,padx=5,pady=5,sticky="new")
Button11=tk.Button(win,text='粘贴圆数据',command=b11,font=('微软雅黑',15))
Button12=tk.Button(win,text='x y r格式',command=b12,font=('微软雅黑',15))
Button13=tk.Button(win,text='保留按钮',command=b13,font=('微软雅黑',15))
Button14=tk.Button(win,text='保留按钮',command=b14,font=('微软雅黑',15))
Button15=tk.Button(win,text='绘画CAD圆',command=b15,font=('微软雅黑',15))
Button11.grid(row=2,column=2,padx=5,pady=5,sticky="new")
Button12.grid(row=3,column=2,padx=5,pady=5,sticky="w")
Button13.grid(row=3,column=2,padx=5,pady=5,sticky="n")
Button14.grid(row=3,column=2,padx=5,pady=5,sticky="e")
Button15.grid(row=4,column=2,padx=5,pady=5,sticky="new")
#显示主窗口
win.mainloop()
线段绘画功能代码
#作者:冯桂和
#功能:直线、多段线绘制
from pyautocad import Autocad, APoint
from numpy import double
#绘画多段线功能进行封装为函数PLine,一维或二维传参
def PLine(pl_list):
#连接CAD,如果未打开CAD软件,则自动打开并创建
#如果程序出错,请打开任务管理器把CAD后台关闭干净
acad = Autocad(create_if_not_exists=True)
#在CAD命令窗口输出内容
acad.prompt("python成功连接CAD软件")
#打印文件名字
print("CAD文件:{}".format(acad.doc.Name))
for i in range(len(pl_list)):
pl_list1=pl_list[i]
pl_list2=[]
for i in pl_list1:
pl_list2.append(i[0])
pl_list2.append(i[1])
#将一维多段线坐标参数进行双精度转换
plinepnts = double ( pl_list2 )
#将PL坐标绘制至CAD软件中
polylneobj = acad.ActiveDocument.ModelSpace.AddLightweightPolyline ( plinepnts )
return "成功连接CAD文件:{}\n成功画入{}条多段线!".format(acad.doc.Name,len(pl_list))
# if type(pl_list[0])==type([0]):
# #将二维列表转为一维列表
# pl_list1=[]
# for i in pl_list:
# pl_list1.append(i[0])
# pl_list1.append(i[1])
# #将一维多段线坐标参数进行双精度转换
# plinepnts = double ( pl_list1 )
# #将PL坐标绘制至CAD软件中
# polylneobj = acad.ActiveDocument.ModelSpace.AddLightweightPolyline ( plinepnts )
# elif type(pl_list[0])==type(1.0) or type(pl_list[0])==type(1):
# #将一维多段线坐标参数进行双精度转换
# plinepnts = double ( pl_list )
# #将PL坐标绘制至CAD软件中
# polylneobj = acad.ActiveDocument.ModelSpace.AddLightweightPolyline ( plinepnts )
# else:
# print('数据错误')
#绘画直线功能进行封装为函数l,一维或二维传参
def Line(l_list):
#连接CAD,如果未打开CAD软件,则自动打开并创建
#如果程序出错,请打开任务管理器把CAD后台关闭干净
acad = Autocad(create_if_not_exists=True)
#在CAD命令窗口输出内容
acad.prompt("python成功连接CAD软件")
#打印文件名字
print("CAD文件:{}".format(acad.doc.Name))
if type(l_list[0])==type([0]):
for i in range(len(l_list)-1):
x1=l_list[i][0]
y1=l_list[i][1]
xy1=APoint(x1,y1)
x2=l_list[i+1][0]
y2=l_list[i+1][1]
xy2=APoint(x2,y2)
acad.model.AddLine(xy1, xy2) # 画线:指定起点和终点
elif type(l_list[0])==type(1.0) or type(l_list[0])==type(1):
for i in range(int(len(l_list)/2)-1):
x1=l_list[i*2]
y1=l_list[i*2+1]
xy1=APoint(x1,y1)
x2=l_list[i*2+2]
y2=l_list[i*2+3]
xy2=APoint(x2,y2)
acad.model.AddLine(xy1, xy2) # 画线:指定起点和终点
else:
pass
#以脚本的方式运行代码
if __name__ == '__main__':
# l_list=[60, 10, 65, 10, 68, 14, 57, 14]
# a=PLine(l_list)
# l_list=[[60, 10],[65, 10],[68, 14],[57, 14]]
# a=PLine(l_list)
# l_list=[60, 10, 65, 10, 68, 14, 57, 14]
# b=Line(l_list)
# l_list=[[60, 10], [65, 10], [68, 14], [57, 14]]
# b=Line(l_list)
pass
文本绘画功能代码
#作者:冯桂和
#功能:文字绘制
from pyautocad import Autocad, APoint
#将文字写入CAD文件,穿入坐标列表及标高(list[x,y,t],h)
def Text(text_list,h):
#连接CAD,如果未打开CAD软件,则自动打开并创建
#如果程序出错,请打开任务管理器把CAD后台关闭干净
acad = Autocad(create_if_not_exists=True)
#在CAD命令窗口输出内容
acad.prompt("python成功连接CAD软件")
#打印文件名字
print("CAD文件:{}".format(acad.doc.Name))
#添加文本acad.model.AddText("内容",坐标,字高)
for i in range(len(text_list)):
x=text_list[i][0]
y=text_list[i][1]
xy=APoint(x,y)
text = acad.model.AddText(text_list[i][2],xy,h)
return "成功连接CAD文件:{}\n成功画入{}个文本内容!".format(acad.doc.Name,len(text_list))
#以脚本的方式运行代码
if __name__ == '__main__':
text_list=[[459556.424755,2569060.610921,'公安村'],
[457482.826133,2570459.522426,'叶岭村'],
[458176.049175,2566500.618737,'塔岗村'],
[459290.182806,2564799.845843,'陂头村'],
[456959.824833,2565123.713121,'蒌园村'],
[457677.444445,2563697.34995,'简村村'],
[456555.29631,2562169.616319,'岗丰村'],
[457703.500601,2560464.881934,'长岗村']]
a=Text(text_list,180)
pass
圆形绘画功能代码
#作者:冯桂和
#功能:圆绘制
from pyautocad import Autocad, APoint
#绘画圆图形,穿入参数为坐标及半径列表
def Circle(circle_list):
#连接CAD,如果未打开CAD软件,则自动打开并创建
#如果程序出错,请打开任务管理器把CAD后台关闭干净
acad = Autocad(create_if_not_exists=True)
#在CAD命令窗口输出内容
acad.prompt("python成功连接CAD软件")
#打印文件名字
print("CAD文件:{}".format(acad.doc.Name))
for i in range(len(circle_list)):
x=circle_list[i][0]
y=circle_list[i][1]
d=circle_list[i][2]
xy=APoint(x,y)
acad.model.AddCircle(xy,d) # 指定圆心;半径
return "成功连接CAD文件:{}\n成功画入{}个圆!".format(acad.doc.Name,len(circle_list))
#以脚本的方式运行代码
if __name__ == '__main__':
circle_list=[[459556.424755,2569060.610921,300],
[457482.826133,2570459.522426,300],
[458176.049175,2566500.618737,300],
[459290.182806,2564799.845843,300],
[456959.824833,2565123.713121,300],
[457677.444445,2563697.34995,300],
[456555.29631,2562169.616319,300],
[457703.500601,2560464.881934,300]]
a=Circle(circle_list)
print(a)
pass
二、CAD图形信息读取并存储
在实际应用中,不仅需要使用数据绘画图形,还需对现状图形的数据信息进行读取操作,因此,将常见的直线、多段线、样条曲线、圆、圆弧、椭圆、文本、块、点、图案填充进行读取属性,属性包括有类型、图层、面积、颜色、线型、标高、半径、内容、长度、坐标。
含5000+图形对象的CAD信息提取效果图示为
含各种图像的各类信息提取预览(与上图非同个CAD文件)
实现代码为
from pyautocad import Autocad,APoint
import pandas as pd
import datetime
import time
def get_information():
starttime = datetime.datetime.strftime(datetime.datetime.now(),'%X')
stime=time.time()
print('开始时间:{}'.format(starttime))
#连接CAD,如果未打开CAD软件,则自动打开并创建
#如果程序出错,请打开任务管理器把CAD后台关闭干净
acad = Autocad(create_if_not_exists=True)
#在CAD命令窗口输出内容
acad.prompt("连接CAD软件:成功")
#打印文件名字
print("连接CAD文件:{}".format(acad.doc.Name))
#存储对象类型字典
CADbojectsdict={'AcDbText':'单行文本',
'AcDbPolyline':'多段线',
'AcDbMText':'多行文本',
'AcDbCircle':'圆形',
'AcDbBlockReference':'块',
'AcDbLine':'直线',
'AcDbEllipse':'椭圆',
'AcDbArc':'圆弧',
'AcDbSpline':'样条曲线',
'AcDbPoint':'点',
'AcDbHatch':'图案填充'}
#获取所有图像信息并写入
All_information = []
print('--------读取对象--------')
# 获取直线
num=0
for line in acad.iter_objects('Line'):
try:
xy1='{},{}'.format(line.StartPoint[0],line.StartPoint[1])
xy2='{},{}'.format(line.EndPoint[0],line.EndPoint[1])
All_information.append(['线',line.Layer,'',line.Color,line.Linetype,'','','',line.Length,'{}\n{}'.format(xy1,xy2)])
num=num+1
except:
pass
if num:
print(' 直线\t\t{}'.format(num))
# 获取多段线
num=0
for polyLine in acad.iter_objects('PolyLine'):
allxy=polyLine.Coordinates
xy=''
for i in range(int(len(allxy)/2)):
xy=xy+str(allxy[i*2])+','+str(allxy[i*2+1])+'\n'
All_information.append(['多段线',polyLine.Layer,polyLine.Area,polyLine.Color,polyLine.Linetype,polyLine.Elevation,'','',polyLine.Length,xy])
num=num+1
if num:
print(' 多段线\t{}'.format(num))
# 获取样条曲线
num=0
for spline in acad.iter_objects('Spline'):
allxy=spline.FitPoints
xy=''
for i in range(int(len(allxy)/2)):
xy=xy+str(allxy[i*2])+','+str(allxy[i*2+1])+'\n'
All_information.append(['样条曲线',spline.Layer,spline.Area,spline.Color,spline.Linetype,'','','',"",xy])
num=num+1
if num:
print(' 样条曲线\t{}'.format(num))
# 获取圆
num=0
for circle in acad.iter_objects('Circle'):
All_information.append(['圆',circle.Layer,circle.Area,circle.Color,circle.Linetype,'',circle.Radius,'',circle.Circumference,'{},{}'.format(circle.Center[0],circle.Center[1])])
num=num+1
if num:
print(' 圆\t\t{}'.format(num))
# 获取圆弧
num=0
for arc in acad.iter_objects('Arc'):
All_information.append(['圆弧',arc.Layer,arc.Area,arc.Color,arc.Linetype,'',arc.Radius,'',arc.ArcLength,'{},{}'.format(arc.Center[0],arc.Center[1])])
num=num+1
if num:
print(' 圆弧\t\t{}'.format(num))
# 获取椭圆
num=0
for ellipse in acad.iter_objects('Ellipse'):
All_information.append(['椭圆',ellipse.Layer,ellipse.Area,ellipse.Color,ellipse.Linetype,'','{},{}'.format(ellipse.MajorRadius,ellipse.MinorRadius),'','','{},{}'.format(ellipse.Center[0],ellipse.Center[1])])
num=num+1
if num:
print(' 椭圆\t\t{}'.format(num))
# 获取文本
num=0
for Text in acad.iter_objects('Text'):
a=Text.TextString
b=a.replace(r'\P','\n')
All_information.append(['单/多行文本',Text.Layer,'',Text.Color,Text.Linetype,'','',b,'','{},{}'.format(Text.InsertionPoint[0],Text.InsertionPoint[1])])
num=num+1
if num:
print(' 文本\t\t{}'.format(num))
#获取块
num=0
for BlockReference in acad.iter_objects('BlockReference'):
All_information.append(['块',BlockReference.Layer,'',BlockReference.Color,BlockReference.Linetype,'','',BlockReference.Name,'',''])
num=num+1
if num:
print(' 块\t\t{}'.format(num))
#获取点
num=0
for Point in acad.iter_objects('Point'):
All_information.append(['点',Point.Layer,'', Point.Color,Point.Linetype,Point.Coordinates[2],'','','','{},{}'.format(Point.Coordinates[0],Point.Coordinates[1])])
num=num+1
if num:
print(' 点\t\t{}'.format(num))
#获取图案填充
num=0
for Hatch in acad.iter_objects('Hatch'):
# for i in range(len(dir(Hatch))):
# print(dir(Hatch)[i])
All_information.append(['图案填充',Hatch.Layer,Hatch.Area,Hatch.Color,Hatch.Linetype,Hatch.Elevation,'','','',''])
num=num+1
if num:
print(' 图案填充\t{}'.format(num))
name=['类型','图层','面积','颜色','线型','标高','半径','内容','长度','坐标']
information=pd.DataFrame(columns=name,data=All_information,index=range(1,len(All_information)+1))
information.to_excel('CAD图形信息.xlsx',encoding='gbk')
# information.to_csv('CAD图形信息.csv',encoding='gbk')
print('--------提取完毕--------')
print(' 共计对象:{}个'.format(acad.ActiveDocument.ModelSpace.Count))
print(' 写出对象:{}个'.format(len(All_information)))
print(' 存储时间:{}'.format(datetime.datetime.strftime(datetime.datetime.now(),'%X')))
mtime=time.time()
print(' 读取时间:{}秒'.format(round(float(mtime-stime),1)))
print(' 保存文件:CAD图形信息.xlsx')
print('--------未知对象--------')
#获取对象类型
All_ObjectName=[]
for obj in acad.iter_objects():
if All_ObjectName.count(obj.ObjectName)>0:
pass
else:
All_ObjectName.append(obj.ObjectName)
unknow=[]
for i in All_ObjectName:
if i in CADbojectsdict:
pass
else:
unknow.append(i)
if unknow:
for i in unknow:
print(' ',i)
else:
print('\t\t无')
print('--------运行完毕--------')
endtime = datetime.datetime.strftime(datetime.datetime.now(),'%X')
etime=time.time()
print(' 结束时间:{}'.format(endtime))
print(' 共计耗时:{}秒'.format(round(float(etime-stime),1)))
#以脚本的方式运行代码
if __name__ == '__main__':
a=get_information()
pass