和我的python帖子一样,能用AI解决的,能很容易查到资料的内容不多赘述
1.圆弧半径标注与缩放ZOOM(e)
(command "zoom" "e")存在圆弧半径标注时,一旦范围缩放,必定包含圆心位置,所以在半径很大,圆心很远的时候,整个缩放会往圆心的另一侧偏移。当然,没有缩放需求用不到,这里我是在切换到图纸空间后需要在视口中调整模型空间中内容的位置。
关于后续如解决这个问题,我的方案是直接将圆弧半径标注使用多重引线替代。
;; 使用多重引线标注半径(文字格式为 R+数值) (defun ut_mleader_radius (arc_pt landing_pt r_val dim_text / mleader_text dir_angle ext_pt mleader_obj) ;; 消除后续零 (setvar "dimzin" 8) ;; 构造标注文字 (setq mleader_text (if (and dim_text (vl-string-search "<>" dim_text)) (vl-string-subst (strcat "R" (rtos r_val 2 3)) "<>" dim_text) (strcat "R" (rtos r_val 2 3)) ) ) ;; 沿arc_pt→landing_pt方向延伸作为引线落点 (setq dir_angle (angle arc_pt landing_pt)) (setq ext_pt (polar arc_pt dir_angle (if ut_mleader_ext_len ut_mleader_ext_len (/ a0 4)))) (command "mleader" arc_pt ext_pt "" mleader_text) ;; 应用 "Underline_Leader" 样式 (setq mleader_obj (vlax-ename->vla-object (entlast))) (vl-catch-all-apply 'vla-put-StyleName (list mleader_obj "Underline_Leader")) )附上视口创建的lisp代码
(defun create_simple_viewport (save_path / vp_center vp_width vp_height vp_ent vp_obj doc) ;; 创建视口,然后设置比例 ;; 使用标准视口位置和大小 (setq vp_center (list 5 15)) ; A4图纸中心 (setq vp_width 250) ; 视口宽度 (setq vp_height 180) ; 视口高度 ;; 创建视口 (command "mview" vp_center (list (+ (car vp_center) vp_width) (+ (cadr vp_center) vp_height))) ;; 等待命令完成 (ut_wait_cmd) ;; 将视口对象移动到"视口"图层 (setq vp_ent (entlast)) (command "change" vp_ent "" "p" "la" "视口" "") ;; 获取文档对象和视口对象(ActiveX) (setq doc (vla-get-ActiveDocument (vlax-get-acad-object))) (setq vp_obj (vlax-ename->vla-object vp_ent)) ;; 使用 vl-catch-all-apply 激活视口,防止 ActiveX 类型不匹配错误 (if (and doc vp_obj) (progn (vl-catch-all-apply 'vla-put-ActiveViewport (list doc vp_obj)) (vl-catch-all-apply 'vla-put-MSpace (list doc :vlax-true)) ) ) ;; 设置注释比例与视口自定义比例,这里的变量可以自己定义。 (if (and vp_obj ut_vp_scale) (progn (setvar "CANNOSCALE" ut_scale) (vl-catch-all-apply 'vla-put-CustomScale (list vp_obj ut_vp_scale)) (vl-catch-all-apply 'vla-update (list vp_obj)) ) ) ;; 返回图纸空间(使用 vl-catch-all-apply 防止错误) (if doc (vl-catch-all-apply 'vla-put-MSpace (list doc :vlax-false)) ) )可能大家不太理解注释性:注释性的标注可以在视口中设置注释比例和视口比例:
假设视口比例1:1,注释比例为1或者1:1,则注释的文字大小,箭头大小,视口中的图形都和模型空间中相同,
设置视口比例2:1,注释比例为2:1,则注释性的文字大小,箭头大小同上,保持不变,但视口中的图形大小扩大为两倍
(setvar "CANNOSCALE" ut_scale) ;;这个是注释性标注对象的比例 (vl-catch-all-apply 'vla-put-CustomScale (list vp_obj ut_vp_scale)) ;;这里设置的是自定义视口比例上面的视口创建代码中并未使用到zoom,e,原因如下:
MVIEW 创建视口时的"缩放至范围"
视口是这么建的(create_simple_viewport开头):
(command "mview" vp_center (list (+ (car vp_center) vp_width) (+ (cadr vp_center) vp_height)))AutoCAD 在新建一个图纸空间视口时,默认会把该视口内的视图初始化为"显示模型空间全部内容并居中"(相当于对视口内部做了一次zoom extents)。
所以时序是这样的:
① MVIEW 创建视口
→ 视口"视图中点"被自动设为模型空间内容的中心
→ 此时内容已经居中
② vla-put-CustomScale ut_vp_scale
→ 只改变缩放倍数,但围绕①中已固定的"视图中点"缩放
→ 中心点没变,所以无论放大缩小都保持居中
这种居中其实是一种隐式依赖:
- 如果模型空间里除了目标图形外还有别的实体(残留辅助线、参考块等),
zoom extents会把它们也框进来,中心就会跑偏。 - 一旦内容中心 ≠ 视口视图中点,这里就不会居中
2.python和lisp联动的方式
具体实现可以使用AI,但我觉得这个思路有时候还是挺有用的。
from pyautocad import Autocad # 连接到AutoCAD应用程序 acad = Autocad() print(f"正在控制: {acad.doc.Name}") # 1. 发送纯LISP代码 # 在AutoCAD中画一条从 (0,0) 到 (10,10) 的直线 lisp_code = '(command "LINE" "0,0" "10,10" "")' acad.SendCommand(f"(progn {lisp_code})\n") # 用 progn 包裹确保执行 # 2. 加载并执行外部的 .lsp 文件 # 假设你的脚本是 C:/my_scripts/my_tool.lsp,其中定义了一个 C:MYCOMMAND 命令 lisp_file_path = r'C:/my_scripts/my_tool.lsp' # 加载文件 acad.SendCommand(f'(load "{lisp_file_path}")\n') # 执行文件中定义的命令 (注意命令名要大写) acad.SendCommand('MYCOMMAND\n')from pyautocad import Autocad acad = Autocad() # 1. 画一条线 (命令行输入: LINE 0,0 10,10 回车) acad.SendCommand('LINE 0,0 10,10 \n') # 2. 执行LISP函数 (比如获取当前图层名并弹窗显示) acad.SendCommand('(alert (getvar "CLAYER")) \n') # 3. 设置一个系统变量 (比如把十字光标大小设为50) acad.SendCommand('CURSORSIZE 50 \n')这种方式基本上就是直接向命令行发送简短的lisp代码段了。对于不想花大量时间和lisp纠缠,并且希望主要使用python一种语言开发是很友好的。一些使用python效果不好的代码实现,比如:
# 设置点样式 (PDMODE) 和点大小 (PDSIZE)[reference:0][reference:1][reference:2] acad.ActiveDocument.SetVariable("PDMODE", 35) acad.ActiveDocument.SetVariable("PDSIZE", 10) # 开启/关闭栅格显示 (GRIDMODE)[reference:3][reference:4] acad.ActiveDocument.SetVariable("GRIDMODE", 1) # 1为开启,0为关闭这只是一个示例,有些变量设置是生效的,有些不生效,我也没测试过。假设遇到不生效的情况,这个时候可以使用:
from pyautocad import Autocad acad = Autocad() doc = acad.ActiveDocument # 1. 清空用户系统变量 USERS1(设为空字符串) doc.SendCommand('(setvar "USERS1" "")\n') # 2. 假设有一个字典 global_vars global_vars = { "my_int": 123, "my_float": 3.14, "my_str": 'Hello "world"', # 包含双引号 "my_none": None, } # 3. 注入全局变量(按类型生成 LISP 语句) for var_name, var_value in global_vars.items(): if var_value is None: doc.SendCommand(f'(setq {var_name} nil)\n') elif isinstance(var_value, (int, float)): doc.SendCommand(f'(setq {var_name} {var_value})\n') else: # 字符串:转义双引号 escaped = str(var_value).replace('"', '\\"') doc.SendCommand(f'(setq {var_name} "{escaped}")\n')3.关于SendCommand方法
1.使用pyautocad
from pyautocad import Autocad acad = Autocad() acad.SendCommand('(setvar "USERS1" "")\n') # ✅ 完全正确,官方推荐简写pyautocad的Autocad对象本身就封装了SendCommand方法,它内部会自动指向当前文档,所以可以直接写。
在某些需要明确指定文档的场景(比如有多个打开的图纸)我会写成doc.SendCommand。但如果用的是pyautocad且只操作当前文档,直接用acad.SendCommand更简洁。
2.直接使用win32com接口
import win32com.client acad = win32com.client.Dispatch("AutoCAD.Application") doc = acad.ActiveDocument doc.SendCommand('(setvar "USERS1" "")\n') # ✅ 必须用 doc.SendCommand # acad.SendCommand(...) # ❌ 这样写会报错(AttributeError)直接操作 COM 对象时,acad是 Application(应用程序)对象,它没有SendCommand方法,必须通过当前文档(ActiveDocument)来发送:
4.为什么我会使用到lisp以及好不好用
在稳定性和速度上确实远超python的com接口方法。但是对于我的电脑配置荣耀magicbook art14 U7-155H,32G,实测区别不大,速度体感差距很小。
在开发难度上:python面向对象的编程设计毋庸置疑非常方便,想实现代码模块化或者调用各种python库,尤其是添加数值运算逻辑非常方便。
lisp虽然也有vla方法提供面向对象,实际使用非常麻烦,数学运算更不用说了,非常不符合日常习惯。
我也是“人生苦短,我用python”的信徒,但现有项目是基于lisp开发的,加上有AI协助,所以大部分内容使用lisp完成。
注意,lisp如果想要模块化,需要先在AutoCAD中加载所有所需模块文件,才可以调用主函数运行。
所以我把项目拆成了若干lisp文件模块之后,就很少手动load或者appload加载了,直接使用python一次性打包加载完成所有模块。
综上: