拓冰建站拓冰建站
首页 / 资讯中心 / 正文

ObjectARX视图窗口操作:缩放、截屏与局部DWG导出实战解析

简介面向CAD二次开发人员的objectARX工程示例围绕AutoCAD视图操作展开提供窗口缩放、范围缩放、比例缩放、获取当前视图信息、视图窗口截屏保存为图片以及将选中实体写入独立DWG文件等功能的完整实现。资源包含35个文件以cpp源文件13个、h头文件12个和props工程配置3个为主另有sln解决方案、rc资源文件及vcxproj工程文件等整体仅50KB便于快速阅读和复用。目前已有432人学习下载。通过该示例读者可掌握基于ObjectARX的视图缩放调用方式、视图几何信息获取流程、屏幕截屏的位图保存方法以及利用数据库API输出选中实体到DWG文件的实现思路适合正在学习AutoCAD二次开发或需要在项目中集成视图管理功能的开发者参考。1. 视图窗口操作是 objectARX 工具的“起手式”在 AutoCAD 二次开发里视图窗口相关操作几乎是所有批处理工具的必经之路批量出图要先按窗口缩放再导出图片图纸巡查要记录当前视野范围归档时要把镜头对准的图形区域单独存成一份 DWG。objectARX 不像 LISP 那样一条 ZOOM 命令就能解决它需要同时处理 AutoCAD 数据库对象、文档状态和 Windows 窗口句柄但把这四块拆成可复用函数后后续写两万行插件也不慌。这篇只讲最实用的路径获取当前视图参数、缩放视图窗口、截屏保存图片、把当前视图范围里的对象写进新的 DWG 文件。代码都是能直接放进已有 ObjectARX 工程里的适合已经配好环境开始写功能的人不涉及系统变量以外的玄学。2. 获取视图窗口信息的两种可靠方式2.1 视图窗口信息在 AutoCAD 中藏在哪里先把概念对齐在模型空间里“当前视图窗口”不是一个独立实体更像是一组被 AutoCAD 实时维护的参数集合。这些参数包括视图中心点、视图高度、视口宽高比、视图方向、目标点等。它们被分别存储在系统变量里对应的是VIEWCTR、VIEWSIZE、VIEWDIR、TARGET等。同时在数据库的AcDbViewTable中用AcDbViewTableRecord保存所有命名视图的定义当前渲染中的视图也会被 AutoCAD 临时复制成一份 ViewTableRecord 交给 ObjectARX 程序。所以获取视图信息有两条路线一条是走系统变量读取快、写法简单另一条是拿AcDbViewTableRecord对象能一次性拿到完整的视锥参数。实际项目中如果只是想知道“现在看到多大范围”优先用系统变量如果要做精确的相机位置还原或视图比例计算必须用对象方式。下面两种我都给出可抄的代码。2.2 用 acedGetVar 读取当前视图中心与高度最轻量的办法是直接读两个系统变量VIEWCTR和VIEWSIZE。VIEWCTR是当前视口中心的世界坐标VIEWSIZE是当前视口在垂直方向显示的画面高度单位与当前模型空间单位一致。用 resbuf 接收返回值避免类型歧义。// 读取当前视图中心和高度 resbuf rbCtr, rbSize; if (acedGetVar(_T(VIEWCTR), rbCtr) RTNORM acedGetVar(_T(VIEWSIZE), rbSize) RTNORM) { AcGePoint3d center(rbCtr.resval.rpoint[X], rbCtr.resval.rpoint[Y], rbCtr.resval.rpoint[Z]); double height rbSize.resval.rreal; // 调用方在这里使用 center 和 height }acedGetVar第一个参数是系统变量名第二个参数传入 resbuf返回值RTNORM表示读取成功。rbCtr.resval.rpoint是长度为 3 的 double 数组坐标顺序是 X、Y、Z模型空间下 Z 通常就是当前视图平面的 Z 值。这样拿到的center和height足够给后面的缩放函数用。2.3 用 AcDbViewTableRecord 获取完整视图定义如果需要同时拿到视图方向、目标点、透视开关和缩放比例系统变量就零散了。用 ObjectARX 提供的acdbCurViewTableRecord直接取当前视图的完整记录对象一次读完所有参数。AcDbViewTableRecord *pViewRec NULL; Acad::ErrorStatus es acdbCurViewTableRecord(pViewRec); if (es Acad::eOk pViewRec ! NULL) { AcGePoint3d center pViewRec-center(); // 视图中心点 double height pViewRec-height(); // 视图高度 double width pViewRec-width(); // 视图宽度 AcGePoint3d target pViewRec-target(); // 相机目标点 AcGeVector3d dir pViewRec-viewDirection();// 视图方向向量 // 这里根据 width/height 可以算出当前视口宽高比 double aspect width / height; pViewRec-close(); // 释放数据库对象 }注意acdbCurViewTableRecord返回的是一条新建的数据库记录用完必须调用close()否则会留下未关闭对象轻则内存泄漏重则引发 AutoCAD 崩溃。width()并不是任意时候都可靠它是由视口宽高比和height()换算出来的如果当前处于透视投影模式还需要额外调用perspectiveEnabled()判断。对于大多数批处理场景中心点和高度已经能满足“按窗口缩放”和“按中心缩放”的需求。数据来源获取内容复杂度适用场景acedGetVarVIEWCTR、VIEWSIZE低快速知道当前视野大小和位置ViewTableRecordcenter、height、width、dir、target、透视开关中完整保存视图状态、还原相机3. 视图窗口缩放用 ZOOM 命令实现窗口与中心缩放3.1 缩放视图不改变模型只改变视锥体很多人第一次写缩放时容易把它理解成“移动实体”其实缩放操作改变的是观察者的视锥体参数。在 AutoCAD 内部一次 ZOOM 操作就是修改当前视图记录的center和height然后重新计算显示矩阵。ObjectARX 开发者完全可以拿到AcDbViewTableRecord后自己改参数再放回视图表但这条路线要额外处理视图锁定、显示更新、撤销栈非常容易翻车。所以我只推荐用acedCommandS发送 ZOOM 命令。AutoCAD 的 ZOOM 命令已经把所有边界情况都处理了包括窗口范围、图纸空间切换、模型空间界限检查执行完也会自动清理临时视图。相信标准命令而不是自己再造轮子。3.2 用 acedCommandS 发送 ZOOM 命令acedCommandS是 ObjectARX 中执行命令的安全接口参数以0结尾。下面的函数把两个角点转成ads_point后发送_ZOOM _W实现窗口缩放。// 窗口缩放把 pt1 和 pt2 围成的矩形区域放大到最大 void zoomToWindow(const AcGePoint3d pt1, const AcGePoint3d pt2) { ads_point p1, p2; p1[X] pt1.x; p1[Y] pt1.y; p1[Z] pt1.z; p2[X] pt2.x; p2[Y] pt2.y; p2[Z] pt2.z; acedCommandS(ACRX_T(_ZOOM), // 命令名 ACRX_T(_W), // 窗口 Window p1, p2, // 两个角点 0); // 结束标记 }这里_ZOOM是带下划线的命令名确保不会因为本地化语言环境而变化_W是短横线加字母表示使用 Window 选项。两个ads_point是 double 数组直接传指针。最后必须有一个0否则函数不知道参数结束可能在 debug 模式下断言失败。类似的按中心缩放也很常用// 按中心缩放以 center 为中心把视图高度设为指定值 void zoomToCenter(const AcGePoint3d center, double height) { ads_point pt; pt[X] center.x; pt[Y] center.y; pt[Z] center.z; acedCommandS(ACRX_T(_ZOOM), ACRX_T(_C), pt, // 中心点 height, // 视图高度 0); }_C后面的height会被 AutoCAD 视为新视图的垂直高度单位是当前图形单位。如果 height 值太大当前图形看起来会缩小如果 height 太小画面只显示一小块。这里要特别注意CAD 的 ZOOM 命令要求height必须大于 0传 0 会触发Zoom cancelled错误。所以调用前最好做一次合法性判断或者把 height 交给用户输入后进行 fabs。3.3 常用 ZOOM 选项对应的 ARX 参数在批处理脚本里除了窗口缩放和中心缩放还经常用到全部缩放和上一视图。集中整理成下表方便直接查阅。命令行选项参数形式作用ARX 示例Window_W 两个点将指定窗口区域放大到全屏ACRX_T(_ZOOM), ACRX_T(_W), p1, p2, 0Center_C 中心点 高度指定新视图中心和高度ACRX_T(_ZOOM), ACRX_T(_C), pt, height, 0Scale_S 比例值相对于当前视图或图纸空间的缩放比例ACRX_T(_ZOOM), ACRX_T(_S), 2.0, 0Extents_E把所有图形对象显示到最大范围ACRX_T(_ZOOM), ACRX_T(_E), 0Previous_P恢复上一个视图ACRX_T(_ZOOM), ACRX_T(_P), 0使用_S时比例值大于 1 是放大小于 1 是缩小。但这里的“放大”取决于当前视图与图纸空间的比例关系和人类直觉不完全一致。我通常只用_W和_C它们更直观尤其在脚本里坐标已经算好的情况下。3.4 在非命令环境里排队缩放有一种情况不能直接调用acedCommandS命令回调、文档锁定回调、数据库 reactor 中。这些上下文里 AutoCAD 可能正在处理其他命令立即发送命令会触发“命令不合法”错误。常见做法是用acdbPostCommand或者把参数记录进一个队列等到空闲时再执行。简单的处理是把缩放动作放进一个自定义命令函数中用户通过输入命令触发这样最稳。4. 截屏从视图窗口句柄到 PNG 文件4.1 三种截屏路线为什么选 BitBltObjectARX 里截屏有三条路线一是发送JPGOUT、PNGOUT这类 AutoCAD 标准输出命令代码短但会弹出文件选择对话框不适合无人值守二是使用 AcGsView 图形系统后台渲染可以在不显示窗口的情况下生成高清图片但 AcGs 的初始化和场景管理非常复杂普通业务工具不值得投入三是用 Windows GDI 的 BitBlt 直接捕获视图窗口所见即所得代码量适中不会触发 AutoCAD 自身 UI 流程。对于“当前视图截屏保存 PNG”这个需求BitBlt 是性价比最高的路线。这种方案的局限是要窗口可见如果视图窗口被其他窗口遮住或者最小化截到的是空白。因此批处理脚本在截屏前要先调用BringWindowToTop把文档窗口提到最前面必要时还要WaitForInputIdle等待界面刷新。4.2 找到当前视图窗口的 HWNDAutoCAD 主窗口下面有一个类名是AcDbViewport的子窗口这才是真正绘制模型区域的控件。通过文档对象拿到顶层句柄再枚举子窗口找到它。// 回调函数查找类名为 AcDbViewport 的子窗口 static BOOL CALLBACK FindViewport(HWND hwnd, LPARAM lParam) { TCHAR cls[256]; GetClassName(hwnd, cls, 256); if (_tcsicmp(cls, _T(AcDbViewport)) 0) { *reinterpret_castHWND*(lParam) hwnd; return FALSE; // 找到后终止枚举 } return TRUE; } // 在文档窗口中查找视图窗口 HWND findViewportHwnd() { HWND docHwnd acdocManager()-curDocument()-windowHandle(); HWND viewHwnd NULL; EnumChildWindows(docHwnd, FindViewport, reinterpret_castLPARAM(viewHwnd)); return viewHwnd; }windowHandle()返回的是当前 AutoCAD 文档对应的 MDI 子窗口。EnumChildWindows会递归遍历所有孙窗口所以在不同 AutoCAD 版本里都能找到深层的视图窗口。找到的viewHwnd就是后面 BitBlt 的复制源。4.3 BitBlt 截屏核心函数下面这个函数把视图窗口的客户区复制到位图并保存为 PNG。为了读代码方便我把保存部分用 CImage 完成。// 捕获视图窗口客户区并保存为 PNG BOOL captureViewToFile(HWND hwnd, LPCTSTR filePath) { RECT rc; GetClientRect(hwnd, rc); int w rc.right - rc.left; int h rc.bottom - rc.top; if (w 0 || h 0) return FALSE; // 创建屏幕兼容的内存 DC 和位图 HDC hdcScreen GetDC(NULL); HDC hdcWnd GetDC(hwnd); HDC hdcMem CreateCompatibleDC(hdcScreen); HBITMAP hbm CreateCompatibleBitmap(hdcScreen, w, h); HBITMAP hbmOld (HBITMAP)SelectObject(hdcMem, hbm); // 从视图窗口复制像素 BOOL ok BitBlt(hdcMem, 0, 0, w, h, hdcWnd, 0, 0, SRCCOPY); if (ok) { CImage img; img.Attach(hbm); // 需要 GDI 初始化如果工程已初始化可直接使用 HRESULT hr img.Save(filePath, Gdiplus::ImageFormatPNG); ok SUCCEEDED(hr); img.Detach(); } // 清理资源 SelectObject(hdcMem, hbmOld); DeleteObject(hbm); DeleteDC(hdcMem); ReleaseDC(hwnd, hdcWnd); ReleaseDC(NULL, hdcScreen); return ok; }CreateCompatibleDC创建的内存 DC 和屏幕 DC 颜色格式一致CreateCompatibleBitmap也按屏幕格式生成位图这样BitBlt才不会有尺寸错位。SRCCOPY表示直接复制源像素。保存前必须Attach到 CImage否则位图句柄无法交给 GDI保存完成后必须Detach避免 CImage 析构时二次释放。4.4 保存位图时要注意的 DPI 与显示状态在高 DPI 显示器上AutoCAD 可能启用系统缩放导致GetClientRect返回的是逻辑像素而CreateCompatibleBitmap使用的是物理像素两者不一致时截出的图片会偏小或只截取一角。解决办法是调用SetProcessDPIAware应用启动时设置或者在截屏前临时关闭 DPI 虚拟化。另外如果视图窗口开启了硬件加速某些显卡驱动下 BitBlt 可能捕获到黑屏此时需要把GRAPHICSCONFIG系统变量调整为软件加速模式或者用GDI兼容模式重绘一次。这些是实际部署中比较容易踩的坑批量导出前最好在目标机器上跑一次自检。5. 保存视图 dwg 文件用 wblock 写出窗口内对象5.1 WBLOCK 命令和 AcDbDatabase::wblock 的选择把当前视图范围里的对象保存成独立 DWG最常见的技术方案是 WBLOCK。命令本身能复制块、图层、线型等依赖不需要开发者自己处理克隆关系。在 ObjectARX 里可以直接发送_WBLOCK命令串但命令交互路径长对话框和文件名很难完全程序化控制。更可取的是调用AcDbDatabase::wblock静态方法它等价于 WBLOCK 的底层实现由我们传入一个AcDbObjectIdArray返回一个全新的AcDbDatabase对象最后用saveAs落盘。整个过程不开新文档不弹对话框适合批处理。5.2 用窗口选择把当前视图范围内对象收集起来先算出当前视图的左下角和右上角再调用acedSSGet用窗口选择方式收集对象。窗口选择能保证只在当前视野范围内的对象被写入新文件。// 根据视图中心、宽度和高度计算矩形角点 AcGePoint3d center(10.0, 10.0, 0.0); double height 25.0; double aspect 16.0 / 9.0; // 由视图信息计算得到的宽高比 double width height * aspect; ads_point pt1, pt2; pt1[X] center.x - width * 0.5; pt1[Y] center.y - height * 0.5; pt1[Z] center.z; pt2[X] center.x width * 0.5; pt2[Y] center.y height * 0.5; pt2[Z] center.z; // 交叉选择 (_C) 选中窗口内以及与窗口相交的对象 ads_name ss; if (acedSSGet(_T(_C), pt1, pt2, NULL, ss) ! RTNORM) return; // 把选择集转换成 ObjectId 数组 AcDbObjectIdArray ids; Adesk::Int32 nLen 0; acedSSLength(ss, nLen); for (Adesk::Int32 i 0; i nLen; i) { ads_name ent; AcDbObjectId id; acedSSName(ss, i, ent); acdbGetObjectId(id, ent); ids.append(id); } acedSSFree(ss); // 用完必须释放选择集这里_C是交叉选择比_W窗口选择更宽松会将部分落在窗口外的对象也选中。出图场景中很多块会超出裁剪边界用交叉选择能避免缺块。如果你希望严格只保存完全在窗口内的对象把_C改成_W即可。acedSSFree很容易忘放在代码里醒目位置提醒自己每个acedSSGet都要配对释放。5.3 调用 wblock 生成新数据库并落盘拿到对象数组后一行核心调用生成新数据库然后设置文件名保存。AcDbDatabase *pNewDb NULL; AcGePoint3d basePt(0.0, 0.0, 0.0); // 新图原点也可取视图中心 Acad::ErrorStatus es AcDbDatabase::wblock(pNewDb, ids, basePt); if (es ! Acad::eOk || pNewDb NULL) { // 写入日志或弹提示 return; } // 保存为 AutoCAD 2018 格式 es pNewDb-saveAs(_T(D:\\export_views\\view1.dwg)); if (es ! Acad::eOk) { // 处理保存失败 } delete pNewDb; // 新数据库由 wblock 创建必须由调用方删除AcDbDatabase::wblock的第一个参数是输出参数调用前不用初始化函数内部会分配一个新的数据库。第二个参数是AcDbObjectIdArray里面可以是模型空间实体也可以是其他数据库对象但普通场景下传实体 id 就够。第三个参数basePt是新 DWG 的插入基点如果传视图中心新图中的对象相对原点偏移会比较小便于后续编辑。保存格式由saveAs自动判断也可以显式指定AcDbDatabase::kDwg2018等枚举。5.4 保存后要主动处理的对象pNewDb是堆对象delete之后新文件已经保存在磁盘上内存中的临时数据库会被释放。但要注意如果ids数组里的对象来自当前数据库wblock内部会深度克隆不会修改原对象所以原来的图不会因为保存视图而改变。另外如果视图范围内包含代理对象或外部参照wblock可能会跳过部分不可分解对象输出新图会缺内容。这类对象最好在截图后通过日志记录至少让用户知道哪些内容被忽略了。6. 批量导出前的一个关键技巧先保存原视图再还原把获取信息、缩放、截屏、保存 DWG 四个功能串成批量工具时最容易被忽视的是“把视图还原回原位”。用户运行你的导出命令后如果发现 AutoCAD 当前视野被改得乱七八糟下次绝不愿意再跑。所以在执行缩放前先把当前视图的中心和高度保存到局部变量所有步骤结束后再调用zoomToCenter还原。6.1 封装一个完整导出流程我一般在命令函数里像下面这样安排void exportCurrentView(const CString name) { // 1. 记录原视图 resbuf rbCtr, rbSize; acedGetVar(_T(VIEWCTR), rbCtr); acedGetVar(_T(VIEWSIZE), rbSize); AcGePoint3d lastCenter(rbCtr.resval.rpoint[X], rbCtr.resval.rpoint[Y], rbCtr.resval.rpoint[Z]); double lastHeight rbSize.resval.rreal; // 2. 按预设范围缩放例如图纸图框到视图窗口 zoomToWindow(exportPt1, exportPt2); // 3. 获取完视图参数后截屏 HWND hwnd findViewportHwnd(); if (hwnd ! NULL) { CString pngPath; pngPath.Format(_T(D:\\exports\\%s.png), name); captureViewToFile(hwnd, pngPath); } // 4. 保存当前视图范围对象为 DWG AcDbObjectIdArray ids selectEntitiesInView(); AcDbDatabase *pNewDb NULL; if (AcDbDatabase::wblock(pNewDb, ids, centerOfExport) Acad::eOk) { CString dwgPath; dwgPath.Format(_T(D:\\exports\\%s.dwg), name); pNewDb-saveAs(dwgPath); delete pNewDb; } // 5. 还原视图 zoomToCenter(lastCenter, lastHeight); }6.2 还原失败时的兜底方式如果中间某一步抛出异常视图可能没法正常还原。稳妥做法是用 C RAII 封装一个视图保护类在构造函数里记录视图参数析构函数里调用zoomToCenter。这样即使函数中间return析构时也会恢复视野。另外批量处理多个文件时每个文档处理前后最好acedCommandS发送一次_REGEN强制刷新显示避免硬件加速下的残影影响 BitBlt 截屏。6.3 导出结果的验证导出完成后不要急着走打开生成的 DWG 文件检查一步用acedCommandS打开另一个 AutoCAD 文档执行_ZOOM _E如果显示的内容和导出前视图一致说明窗口范围计算正确。也可以把 PNG 文件加载到CImage检查位图尺寸是否等于视图窗口客户区尺寸。这两个检查能在开发阶段发现大部分参数错误比盯着代码看半天有效得多。整个流程跑通后你手里就有一套稳定、可复用的 objectARX 视图处理工具。后续再遇到批量出图、图纸预览、局部导出需求往这套框架里加参数就行。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门