目录[-]

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图形

实际效果

pyautocad1

pyautocad2

界面主代码

#作者:冯桂和
#功能: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信息提取效果图示为

pyautocad3

含各种图像的各类信息提取预览(与上图非同个CAD文件)

pyautocad3

实现代码为

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