作为一个道路设计人员,在学会Python如何与AutoCAD连接后,一直尝试自己手搓一个绘制道路平面线形的代码自我满足一下。经过一段时间的摸索,小有所成,现留下文字,一是给代码一个停留的驻点,便于以后想用时备查;二是自我感动一下。文章末尾附有完整的代码!! 程序的主界面如下图所示: 程序的绘制成果展示如下:一、编程环境的搭建1、基本程序及库的安装import tkinter as tk from tkinter import filedialog, messagebox, ttk import ttkbootstrap as ttkb import pythoncom import win32com.client import math import pandas as pd import numpy as np import pywintypes from sympy import* from scipy.integrate import quad tkinter和ttkbootstrap为设置UI界面而加载,其中ttkbootstrap内置有十多个精心策划的光暗主题,大家可以上官网查询,里面不仅有主题的运用方式,还有大量的源代码可供学习; pythoncom和win32com.client 则是为python连接AutoCAD而必需导入的库; pywintyptes是 ‌pywin32‌ 库中的一个子模块,用于在 Windows 平台上帮助 Python 与 COM(Component Object Model)对象交互,并处理 Windows 特定的数据类型;常用于与 Excel、Word等 Windows 应用程序自动化交互。 math、pandas、numpy、scipy均为数学计算而导入的计算库,本篇文章在介绍绘制缓和曲线时采用了纯数字计算和积分计算这两种方法进行绘制,scipy.integrate就是为积分导入的库;2、主窗口的创建及AutoCAD基本参数的设置 在上篇文章悬臂挡土墙的绘制中较为详细的介绍了这部分的设置方法、参数选取等内容,这里不再重复,具体代码如下所示:# ======================== # 主窗口的创建 # ======================== class RoadPlan: def __init__(self): self.acad = None self.doc = None self.model_space = None # --- GUI Setup --- self.root = ttkb.Window() screenWidth = self.root.winfo_screenwidth() screenHeight = self.root.winfo_screenheight() w=600; h=250 x=((screenWidth-w)//2); y=((screenHeight-h)//2) self.root.geometry('%dx%d+%d+%d' % (w,h,x,y)) self.root.title("道路平面绘制 2026") self.root.resizable(width=1, height=1) tmp = open("tmp.ico", "wb+") tmp.write(base64.b64decode(img)) tmp.close() self.root.iconbitmap("tmp.ico") os.remove("tmp.ico") # 主界面 main_frame = ttk.Frame(self.root, padding=10) main_frame.pack(fill=tk.BOTH, expand=True) # 分布界面 frame_top = ttkb.Labelframe(main_frame,text='第一步 检查 AutoCAD 是否打开',bootstyle='info',padding=5) frame_top.pack(side=tk.TOP,fill=tk.X, expand=True, pady=10) frame01 = ttkb.Frame(frame_top); frame01.pack(fill=tk.X, expand=True,pady=5) ttkb.Label(frame01,text='起点桩号:',bootstyle='info').pack(side=tk.LEFT,padx=5) self.ent_prefix = ttkb.Entry(frame01); self.ent_prefix.pack(side=tk.LEFT,fill=tk.X,expand=True,padx=5); self.ent_prefix.insert(0, "") self.bnt_open = ttkb.Button(frame01,text="检查CAD是否打开",width = 20,command=self.open_acad,padding=5,bootstyle=('info','outline')) self.bnt_open.pack(side=tk.LEFT,fill=tk.X, expand=True,padx=5) frame02 = ttkb.Frame(frame_top); frame02.pack(fill=tk.X,expand=True,pady=5) ttkb.Label(frame02,text='导入数据:',bootstyle='info').pack(side=tk.LEFT,padx=5) self.entry_path = ttkb.Entry(frame02); self.entry_path.pack(side=tk.LEFT,fill=tk.X,expand=True,padx=5) self.bnt_load = ttkb.Button(frame02,text="浏 览 ...",width = 20 ,command= self.load_excel,padding=5,bootstyle=('info','outline')) self.bnt_load.pack(side=tk.LEFT,fill=tk.X, expand=True,padx=5) frame_botton = ttkb.Labelframe(main_frame,text='第二步 命令执行',bootstyle='info',padding=5) frame_botton.pack(side=tk.TOP,fill=tk.X, expand=True,pady=10) frame03 = ttkb.Frame(frame_botton); frame03.pack(side=tk.LEFT,fill=tk.X, expand=True,pady=5) self.btn_count = ttk.Button(frame03,text="计 算",padding=5,command=self.count_data,bootstyle='infor') self.btn_count.pack(side=tk.LEFT,fill=tk.X, expand=True,padx=5) self.btn_draw = ttk.Button(frame03,text="绘 图",padding=5,command=self.centerLine,bootstyle='infor') self.btn_draw.pack(side=tk.LEFT,fill=tk.X, expand=True,padx=5) self.btn_exit = ttk.Button(frame03,text="退 出",padding=5,command=self.root.quit,bootstyle='danger') self.btn_exit.pack(side=tk.LEFT,fill=tk.X, expand=True,padx=5) # ======================== # 启动 AutoCAD # ======================== def open_acad(self): # 首先尝试连接到已运行的 AutoCAD 实例 try: self.acad = win32com.client.GetActiveObject("AutoCAD.Application") except (pywintypes.com_error, OSError, AttributeError) as e: # print(f"GetActiveObject failed: {e}") try: # 如果连接失败(没有运行实例),则启动新的 AutoCAD 实例 self.acad = win32com.client.Dispatch("AutoCAD.Application") self.acad.Visible = True # 使新启动的实例可见 except Exception as e2: messagebox.showerror("错误", f"无法启动或连接到 AutoCAD。\n请确保 AutoCAD 已正确安装。\n错误信息: {e2}") return # 如果连接失败,则不启用绘图按钮 # --- 连接成功后的操作 --- # 最大化 AutoCAD 窗口 if self.acad: self.acad.WindowState = 3 self.doc = self.acad.ActiveDocument # 获取当前文档 self.doc.Utility.Prompt("Python 连接CAD成功") self.model_space = self.doc.ModelSpace # 获取模型空间 # ======================== # 激活 CAD窗口 # ======================== def activation_CAD(self): self.acad = win32com.client.GetActiveObject("AutoCAD.Application") self.doc = self.acad.ActiveDocument # 获取当前文档 self.model_space = self.doc.ModelSpace # 获取模型空间 # ======================== # 数据 格式转换成CAD模式 # ======================== def vtPnt(self,x, y, z=0): # 坐标点转化为浮点数 return win32com.client.VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_R8, (x, y, z)) def vtFloat(self,list): # 列表转化为浮点数 return win32com.client.VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_R8, list) def vtObj(self,object): # 转化为对象数组 return win32com.client.VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_DISPATCH,object) def vtInt(list): # 列表转化为整数 return win32com.client.VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_I2, list) def vtVariant(list): # 列表转化为变体 return win32com.client.VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_VARIANT, list) # ======================== # 定制 文字格式 # ======================== def TextStyle(self): self.NewTexStyle = self.doc.TextStyles.Add('myText') self.NewTexStyle.SetFont('仿宋_GB2312',Bold=False,Italic=False,Charset=134,PitchAndFamily=0 or 0) # 设置字体为 仿宋 self.NewTexStyle.Width = 0.75 # 设置文字格式的宽度因子,本程式设置为0.75 # ======================== # 定制 尺寸标注样式 # ======================== def DimStyle(self): self.NewDimStyle = self.doc.DimStyles.Add('myDim') self.doc.SetVariable('DIMCLRD',80) # 定义尺寸线颜色 self.doc.SetVariable('DIMCLRE',80) # 定义尺寸线颜色 self.doc.SetVariable('DIMCLRT',1) # 定义文字颜色 self.doc.SetVariable('DIMSCALE',1) # 定义尺寸标注的全局比例 self.doc.SetVariable('DIMSOXD',1) # 在箭头不能放在尺寸界线内,则将其消除 self.doc.SetVariable('DIMTOFL',0) # 在尺寸界线之间绘制尺寸线,0为不设置 self.doc.SetVariable('DIMDEC',3) # 单位格式为小数时的精度为0 self.doc.SetVariable('DIMLFAC',1) self.doc.SetVariable('DIMDSEP','.') # 小数分隔符为句号 self.doc.SetVariable('DIMTXSTY','myText') # 尺寸标注字体采用本程式定制的字体格式 self.NewDimStyle.CopyFrom(self.doc) # 将所有尺寸定义标注赋值给 mydim # ======================== # 定制 图层样式 # ======================== def LayerStyle(self): self.CenterLine = self.doc.Layers.Add("平面中心线") # 新建图层,图层名称为"平面中心线" self.CenterLine.color = 1 self.PlanCond = self.doc.Layers.Add("平面导线") # 新建图层,图层名称为"平面导线" self.PlanCond.color = 4二、绘制平面线形1、导入基础数据 在主界面一部分的第一行,第一个控件和第二个控件设置了起点桩号,本想着绘完图后再将平面特征点的桩号、坐标按垂直平面线形的方向标注;但手上新接了一个项目,近段时间是无法完成了,但还是备在这里,以利于后续时间提醒一下好进一步完备; 在主界面第一部分的第二行设置了三个控件,第一个为Lable控件,文字标识为“导入数据:”;第二个为Entry控件,用于显示导入的EXCEL文件地址;第三个为Button控件,文字标识为“浏览...”,用于导入EXCEL文件的命令执行;# ======================== # 导入 EXCEL数据 # ======================== def load_excel(self): # 打开文件对话框 file_path = filedialog.askopenfilename(filetypes=[("Excel files", "*.xlsx;*.xls")]) if file_path: # 更新Entry控件显示文件路径 self.entry_path.delete(0, tk.END) self.entry_path.insert(0, file_path) # 使用pandas读取Excel文件 try: # 根据文件扩展名选择合适的引擎 if file_path.endswith('.xlsx'): self.df_data = pd.read_excel(file_path, engine='openpyxl') elif file_path.endswith('.xls'): self.df_data = pd.read_excel(file_path, engine='xlrd') else: # 默认尝试 openpyxl(或抛出错误) self.df_data = pd.read_excel(file_path, engine='openpyxl') # print(self.df_data.head()) # 打印前几行数据作为示例 except Exception as e: messagebox.showwarning('警告',f"读取Excel数据失败: {e}") 此处将EXCEL数据直接转换成了Pandas数据格式,代码中的名称为self.df_data;后续所有计算所需数据、计算结果笔者均添加到了该数据文件中,为绘制CAD图所需的数据来源进行了统一,为后续无论是计算、还是引用数据都统