当前位置: 首页 > news >正文

中望CAD二次开发实战:ObjectARX迁移与QT界面集成

简介:这是一份非常简单但完整可用的中望CAD二次开发示例工程,基于C++与QT对话框技术实现,核心功能是读取扩展字典,适合已经具备C++基础、想了解CAD插件开发流程的开发者。工程针对VS2015、QT 5.12.9和ZWCAD 2022做了完整配置,代码结构清晰,既可以在中望CAD中加载运行,也容易迁移到AutoCAD中使用。压缩包共273个文件,约为206MB,主要文件类型包括dll动态库、lib依赖库、h/cpp源码、ui界面设计文件以及工程配置文档,同时附带了pdb调试信息、中间编译产物和构建日志,便于对照排查和逐步学习。示例项目虽不复杂,却涉及QT界面创建、命令注册、扩展字典读写等关键环节,配合目录中明确的模块划分,适合初学者拆解真实插件工程,读者可从零开始对照目录结构与工程配置进行学习。已有2804人浏览学习,适用于快速上手和作为后续开发的参考模板。 中望CAD二次开发这块,圈子里聊的人不少,但真正把方案落到日常生产里、每天都在用的其实不多。年前我们团队接到一个任务:把原来基于ObjectARX的一批自动化工具迁移到中望CAD环境,同时要新补一套操作界面。选型一开始就定了C++和QT——原因很简单,中望CAD底层的ZRX接口就是C++的,性能和可控性没得挑,而QT做参数面板、文件选择、进度提示这类界面工作,开发效率明显高于手写MFC。项目本身不大,也没有上什么花哨的架构,但发布到现在稳定跑了大半年,可以总结成一句话:简单,但有效,可用。

1. 方案选型:为什么落在这条技术栈上

1.1 国产CAD插件开发的主流选择

中望CAD目前提供好几条二次开发路线:LISP脚本、.NET接口、还有底层的ZRX(ZWCAD Runtime eXtension)。每条路都有各自擅长的场景,但做工程自动化工具时,选择往往不是看哪条路最潮,而是看现有团队资产和性能需求。

LISP上手最快,适合写几十行的批处理脚本,但做不了复杂交互界面,对实体数据的大规模遍历和计算也偏慢。.NET开发效率高,程序集部署也方便,不过CAD从启动到加载托管程序集的过程中,版本绑定和依赖冲突出现得比较频繁,尤其做底层图形数据操作时,性能还是隔了一层。最后剩下C++配合ZRX,这套接口和经典ARX高度兼容,很多以前为ARX写的代码几乎可以平移过来,底层数据库操作、实体遍历、事务控制都很直接。

这个项目之所以选择C++路线,最重要的一点是团队里有现成的ObjectARX代码遗产。你不必把每条业务逻辑重新构思一遍,重点只是适配中望CAD的接口差异,这比从零开始省太多时间。另外,标题里强调“简单但有效”,说明需求收敛得非常好:我们不去做一个CAD平台级框架,只实现最核心的几个功能点,然后把稳定性做到位。对生产工具来说,功能全不如跑得稳。

1.2 QT界面在整个方案中的定位

界面这一层,常见的做法是用CAD自己的对话框或者MFC实现。但我们的场景需要频繁选择外部文件、录入参数、展示导入导出进度,用QT来做这类表单界面非常顺手,信号槽机制让界面和业务逻辑的耦合度很低。更重要的是,QT的布局控件和样式表能快速做出一个专业级的参数面板,不用花大量时间在控件绘制上。

在这个项目里,QT界面只做三件事:选择文件、填参数、看进度。不追求把所有功能塞进CAD的Ribbon面板或者停靠窗口,因为做可停靠面板需要和CAD的窗口系统深度集成,维护成本立刻上一个台阶。独立的对话框方案对用户来说操作路径短,对我们来说也更容易控制边界。我们最终采用的是一个可重复弹出的非模态对话框,CAD命令行保持可交互,配合后面的定时轮询逻辑来驱动QT事件循环,整体效果相当顺手。

有一点必须在方案阶段就明确:QT运行在CAD进程中,本质上是在别人的消息循环里跑自己的事件系统。这个组合天生有一个“坑”,后面我会专门展开讲,也是这个项目里最值得记录的一段排查经验。

2. 环境搭建与核心开发细节

2.1 ZRX SDK和QT的版本匹配

开发环境的第一步是拿到匹配的SDK。中望CAD的ZRX开发包通常随安装目录提供,也可以在官网开发者专区下载到对应版本的SDK。用的时候注意两点:一是SDK版本和要加载的CAD版本必须严格对应,用2024的SDK编出来的插件大概率加载不到2022里;二是编译平台要和安装的CAD位数一致,现在主流是64位,那就一律编x64。

QT版本建议选用5.15 LTS或者更新的长期支持版本,编译器使用MSVC。编译插件时,需要把头文件路径分别指向ZRX SDK的inc目录和QT的include目录,链接器里的附加依赖项加上ZRX的lib文件(rxapi.lib、acdb.lib这些),同时确保QT的lib路径在搜索范围内。运行库建议统一使用多线程DLL(/MD),不要混用静态和动态运行时,否则很容易在加载时出现运行时库冲突。

这里值得多说一句:中望CAD本身可能会带上一些第三方动态库,如果QT的DLL命名冲突,插件运行时可能加载到CAD自带的版本,导致莫名其妙的崩溃。最稳的做法是把QT以静态库方式编进插件,但这会显著增大文件体积且编译时间变长。我们实践下来,动态链接QT然后把所有依赖DLL放在插件所在目录,并在代码里主动指定插件目录为QT库搜索路径,这套方案简单可靠,至少半年来没出过加载问题。

2.2 在CAD线程中驱动QT界面

这是整个项目最核心的坑,也是让很多人放弃在中望CAD里用QT做界面的原因。一开始我们的代码很直接:在命令回调里new一个QDialog然后调用exec(),结果发现对话框虽然能弹出来,但CAD的整个命令行状态直接卡住,任何后续CAD操作都无法响应,甚至有时候对话框上按钮点了没反应。

原因其实不复杂。CAD的命令回调是同步执行的,回调函数不返回,CAD就不会继续处理自己的消息和命令队列。而QDialog::exec()内部创建了一个嵌套事件循环,它自己在这个循环里不断处理QT事件,但CAD的消息没有得到同等照顾,两边互相等,表现出来就是界面卡死或者操作无响应。

我们后来采用了一个比较稳定的模式:在CAD主线程中显示非模态对话框,然后在一个局部事件循环里用定时器持续调用QCoreApplication::processEvents(),同时也处理CAD的Windows消息,这样两边的事件都能被及时处理。核心代码大致是这样:

void zxBatchImportCmd() { MyDialog dlg; QEventLoop loop; QObject::connect(&dlg, &QDialog::finished, &loop, &QEventLoop::quit); QTimer timer; QObject::connect(&timer, &QTimer::timeout, []() { MSG msg; while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } QCoreApplication::processEvents(); }); timer.start(50); dlg.show(); loop.exec(); timer.stop(); }

这段代码实际用了很久,稳定性很高。需要注意的细节是对话框关闭后一定要把timer停下来,否则它还会一直触发消息处理,白白消耗CPU。

2.3 QString、TCHAR和CAD字符串的转换

中望CAD的ZRX接口在Windows下默认使用TCHAR字符类型,和QT的QString是两套体系。中文环境下最典型的坑就是命令行输出或文件名路径出现乱码。我们封装了一个专门处理QStringTCHAR*互转的小函数:

static QString TcharToQString(const TCHAR* str) { #ifdef UNICODE return QString::fromWCharArray(str); #else return QString::fromLocal8Bit(str); #endif } static std::basic_string<TCHAR> QStringToTchar(const QString& str) { #ifdef UNICODE return str.toStdWString(); #else return str.toLocal8Bit().toStdString(); #endif }

这套封装后来被大量复用。凡是涉及acutPrintf输出、读取实体文字内容、或者向CAD命令行传递路径的地方,都统一走这两个函数,没有再出现乱码问题。

3. 实操过程与核心环节实现

3.1 最小ZRX插件骨架

ZRX插件的基本结构和传统ARX几乎一致,核心是继承AcRxArxApp的应用类,加上入口宏。一个最小可加载的插件骨架大致如下:

#include "zrx.h" #include "dbents.h" #include "aced.h" class ZxApp : public AcRxArxApp { public: ZxApp() : AcRxArxApp() {} virtual AcRx::AppRetCode On_kInitAppMsg(void* pkt) { AcRx::AppRetCode ret = AcRxArxApp::On_kInitAppMsg(pkt); return ret; } virtual AcRx::AppRetCode On_kUnloadAppMsg(void* pkt) { return AcRxArxApp::On_kUnloadAppMsg(pkt); } }; IMPLEMENT_ARX_ENTRYPOINT(ZxApp) static void zxBatchImportCmd() { // 弹出QT界面并驱动事件循环 QDialog dlg; // ... }

命令zxBatchImportCmd编译后会注册成CAD命令ZXBATCHIMPORT,用户在命令行输入ZXBATCHIMPORT就能触发。中望CAD里用APPLOAD加载生成的.zrx文件,出现“加载成功”提示后即可运行。整个骨架非常轻,适合作为后续所有二次开发命令的通用模板。

3.2 核心业务:批量导入数据并生成实体

这个项目里最常用的一个业务是从外部CSV文件里读取坐标和半径,批量生成圆,同时在圆心处加一个指定图层的小标记。整体流程说白了就三步:选文件、解析数据、写入模型空间。

选文件直接用QT的QFileDialog,没有任何额外的CAD接口依赖:

QString filePath = QFileDialog::getOpenFileName( nullptr, QStringLiteral("选择数据文件"), QStringLiteral("D:/data"), QStringLiteral("文本文件 (*.txt *.csv)"));

拿到文件路径后按行解析,逐行读出x、y、r三个数值,校验合法性后创建AcDbCircle实体。写入模型空间的固定写法如下:

AcDbCircle* pCircle = new AcDbCircle( AcGePoint3d(x, y, 0.0), AcGeVector3d::kZAxis, r); AcDbBlockTable* pTable = nullptr; acdbHostApplicationServices()->workingDatabase() ->getSymbolTable(pTable, AcDb::kForRead); AcDbBlockTableRecord* pRecord = nullptr; pTable->getAt(ACDB_MODEL_SPACE, pRecord, AcDb::kForWrite); pRecord->appendAcDbEntity(pCircle); pCircle->close(); pRecord->close(); pTable->close();

有个非常实际的性能经验:批量生成上千个实体时,不要在循环里每次重新查一次块表记录,正确的做法是循环开始前打开一次模型空间块表记录并保持写入状态,循环里只做new实体、appendAcDbEntityclose这三件事,最后再统一关闭块表记录。实测同样导入5000个圆,优化后耗时只有原来的不到三分之一。

3.3 从图纸中批量提取数据

有导入就有导出。另一条常用业务是把当前图纸里所有指定图层的圆导出成CSV,方便后续在外部程序里做数据统计。这个场景用到选择集过滤,只选圆实体:

ads_name ssname; resbuf* rbFilter = acutNewRb(RTDXF0); rbFilter->resval.rstring = _T("CIRCLE"); acedSSGet(_T("X"), nullptr, nullptr, rbFilter, ssname); acutRelRb(rbFilter);

然后遍历选择集,用acdbOpenAcDbObject打开实体,通过AcDbCircle::center()radius()取数据,最后用QT的QTextStream逐行写文件。过程中给用户一个进度提示,每处理100个实体输出一条进度信息到命令行。这里有个小经验:遍历结束后记得用acedSSFree释放选择集,很多刚上手的同学会忽略,长时间运行后会产生对象句柄泄漏。

4. 常见问题与排查技巧实录

4.1 对话框能打开但CAD整个卡住

这个前面已经提过,核心是不要直接QDialog::exec()。我们遇到过用户反馈“点命令后整个CAD像死了一样”,其实就是命令回调没返回、事件循环互相嵌套导致的。换成定时器+processEvents()的方案后问题彻底消失。如果项目确实需要模态效果,可以在命令回调里先用acedCommand把CAD的命令行状态机“挂起”,但这样处理起来要小心每个分支都恢复状态,复杂度高,非必要不推荐。

4.2 插件加载失败:版本、位数和依赖DLL

插件加载失败是排查最多的问题,集中在这几类:

现象可能原因解决办法
加载提示版本不匹配SDK和CAD版本不一致换成完全对应的ZRX SDK
加载后立刻崩溃编译位数和CAD位数不一致统一x64或x86
提示找不到QT DLL运行环境的QT依赖缺失把QT的DLL和plugins目录放一起
提示缺少platform插件缺qwindows.dll拷贝Qt的platforms目录并设置路径

这条特别提醒:QT程序在Windows下需要platforms目录里的qwindows.dll,否则连QDialog都创建不了。发布时要把这个目录整体拷到插件旁边,并在代码启动早期调用QCoreApplication::addLibraryPath()指向插件所在目录,可以避免很多环境问题。

4.3 生成实体后图形不刷新

有时候数据明明写进去了,但屏幕上看不到新实体。这通常不是逻辑错误,而是视口没有主动刷新。调用一次acedUpdateDisplay()或者用acedCommand执行一次REGEN就能解决。还有个小技巧:批量操作结束后统一刷新一次,不要每生成一个实体就刷一次,否则大图会卡得非常明显。

4.4 交付前一定要做的事情

这个项目发布阶段我踩过不少坑,最值得说的三点。一是日志不能省:在插件目录写一个简单的运行日志,记录每次导入导出的文件路径、实体数量和耗时,用户报问题的时候能快速定位。二是做一次“干净环境验证”:找一台没有安装开发工具的机器,把插件和QT依赖一起拷过去,跑一遍完整流程。这一步能发现大量隐性问题。三是小步验证:先拿100条数据跑通全流程,再放全量数据,不要一上来就处理几万个实体,出了问题很难判断是解析逻辑还是CAD接口调用导致的。

这个项目真正让我有体会的是,二次开发很多时候不是拼技术难度,而是拼怎么在复杂环境里把一条路径彻底打通。C++、ZRX、QT这三样东西单拎出来都不新鲜,但放到中望CAD的环境里,每一个环节都可能踩到意想不到的坑。尤其那个QT事件循环的问题,网上资料很少,我们也是反复试错才找到了稳定的模式。最后分享一个发布阶段的小技巧:所有依赖DLL不要直接放到CAD安装目录,而是放在插件自己的子目录里,然后用相对路径去加载,这样即使用户后续升级CAD版本或者清理安装目录,插件也不会因为依赖项被误删而失效。

本文还有配套的精品资源,点击获取

http://www.cnnetsun.cn/news/4341302.html

相关文章:

  • 运动模仿下的肌肉骨骼模型控制:Python实现从PD控制到静态优化
  • 货拉拉2018秋招Android笔试复盘:知识底盘与高频考点拆解
  • 基于STM32的上拉式磁悬浮:原理、控制与调试
  • 基于深度学习的图像烟雾检测:从数据构建到边缘端部署实战
  • HP DL388 G7驱动折腾全攻略:阵列卡注入与固件升级避坑指南
  • 五大技术热点板块前瞻:云原生、大模型与湖仓一体等方向详解
  • 任务调度中的关键节点管理:从识别到告警的工程实践
  • 电商Agent评测基准CommerceAgentBench:任务设计、指标解读与工程实践
  • 蔚来测试开发岗秋招笔试复盘:考点分析与学习路线
  • AI代理交易:从意图驱动到安全重构,百倍交易量下的风险与机遇
  • Unity实战:事件驱动的组合条件检测系统设计与实现
  • 基于SpringBoot的电动汽车租赁管理系统(源码+讲解视频+LW)
  • 基于STC89C52的智能自适应调光台灯设计:从硬件到代码
  • 构建本地化代码演示环境:从功能定位到部署实践
  • 10个AI协作开发《堡垒之夜》:多智能体工程化实践与接口治理
  • Claude Code v2.1.251新能力:模型切换钩子与远程流式输出实战
  • 用PyTorch从零手写Transformer并跑通训练全流程
  • OPC到BACnet协议转换网关:楼宇自控与工业数据集成实战指南
  • AI应用丝滑体验的工程密码:Agent链路核心模块拆解与实战
  • 基于大语言模型构建实时视频字幕翻译工具:从原理到实践
  • Win10下NDK r22编译FFmpeg arm64-v8a动静态库完整实践
  • 电赛E题满分视频制作指南:把视频当工程交付物
  • 热成像与可见光双模态融合:从配准到检测的完整工程实践
  • 智能保险箱技术拆解:办公场景选型、安装验收与故障排查指南
  • IAR下LPC1768工程RAM.icf链接脚本深度解析与实战指南
  • 单片机毕业设计-基于 STM32 或 51 单片机的语音播报距离检测报警系统设计 基于 STM32 或 51 单片机的 LCD1602 显示超声波报警设备开发(022905)
  • 基于FPGA读写MT25QL SPI NOR Flash的工程实现与验证
  • WebLogic 12.2.1.4 PSU 36805124 安装实战与回滚指南
  • 永磁同步电机四参数辨识:基于递推最小二乘的Simulink仿真详解
  • 用Python脚本批量整理PPT模板:从散乱文件到可检索资产库