news 2026/10/19 4:13:32

ObjectARX自定义实体实例:从AcDbEntity派生到CAD原生图元

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ObjectARX自定义实体实例:从AcDbEntity派生到CAD原生图元

简介:ObjectARX自定义实体是AutoCAD二次开发中的核心环节,这份实例以创建简单直线实体为切入点,面向熟悉C++和VC++的CAD开发者,完整展示从项目初始化到实体注册的编码流程。资源压缩包共含70个文件,以头文件h、源文件cpp、工程文件dsp/dsw、中间文件obj及调试文件pdb为主,总大小5.08MB,工程目录结构清晰,可直接用Visual Studio打开并编译加载。实例覆盖了关键步骤:搭建ARX项目框架、从AcDbLine派生自定义实体类、重写dwgInFields/dwgOutFields与worldDraw实现数据存取和图形绘制、通过acedRegApp注册实体、编写命令函数创建实体,以及调试技巧。通过研读这份工程,读者能掌握ObjectARX自定义实体的骨架和API用法,理解DWG/DXF序列化与实体交互机制,并为开发形状更复杂、带属性与行为的高级自定义实体打下基础。已有1162人学习,证明该实例对初学者具有不错的参考价值。

1. 自定义实体的真正价值:摆脱“炸开才能编辑”的魔咒

做过CAD二次开发的人几乎都遇到过同一个场景:用LISP或普通ARX画出来的图形,本质上是一堆Line、Arc、Circle拼在一起,用户一框选就碎成了几十个对象,想要整体改尺寸只能重新生成一遍。这套“假图元”方案在给排水管道、电气桥架、建筑轴网这类重复性极高的专业对象上尤其难受。而这个ObjectARX自定义实体实例解决的正是一类需求:让程序在AutoCAD内注册一种全新的原生图元,它像Line一样被选中、被夹点拖动、被Osnap捕捉、被Undo记住,整个对象却是“一个整体”,内部属性随便封装成C++类的成员变量。它的本质就是派生AcDbEntity并实现一组虚函数,用VC++编译成.arx在CAD里加载。适合的人很明确:手里有专业图元要实现的CAD二次开发工程师,以及刚接触ARX、想看一个最小可运行例子的C++开发者。这套实例代码不算复杂,但把自定义实体最核心的框架完整搭了出来,看懂后可以直接往里面装自己的业务逻辑。

2. 技术底座与工程骨架:ARX、VC++、AcDbEntity三者如何协作

2.1 为什么选ObjectARX而不是别的二次开发接口

AutoCAD的二次开发接口经历过AutoLISP、VBA、.NET、ObjectARX几个阶段,如果目标是“定义一个新的图元类型”,ObjectARX几乎是唯一正路。LISP调用的是ActiveX接口,只能操作已有图元;.NET虽然也能派生自定义实体,但部署依赖和版本匹配问题比ARX更麻烦。ObjectARX直接以C++动态库形式加载进CAD进程,与AcDbEntity共享同一套虚函数表,CAD的数据库、图形引擎、Undo栈都把你写的类当成“原生图元”,这是其他接口做不到的深度。

这个实例的编译环境是VC++加上对应版本的ObjectARX SDK。ARX的版本必须和AutoCAD主版本严格对应,比如某个版本的SDK只支持某一个主版本号的CAD,混用基本会崩溃。工具链上还需要一个能生成arx项目模板的向导,或者直接手动配置头文件目录、库目录和链接器输入项,实例里用的是最常见的“新建VC++项目,然后手工填依赖”的路线。

2.2 自定义实体实例的目录结构与文件职责

整个实例工程的文件数量很少,但每个文件都踩在ARX的固定套路上。我拆过的这套代码大致长这样:

CustomEntity/ ├── StdAfx.h // 预编译头,集中引入ARX的头文件 ├── AcDbCustomEntity.h // 实体类声明,派生自AcDbEntity ├── AcDbCustomEntity.cpp // 实体类实现,核心的绘制和序列化逻辑 ├── Main.cpp // ARX入口,命令注册和卸载逻辑 └── CustomEntity.def // 模块定义文件,声明入口函数

AcDbCustomEntity.h是核心,里面声明了类名、成员变量、构造析构函数,以及一组override的虚函数。Main.cpp是ARX的模块入口,负责在CAD加载arx时调用acedRegCmds注册命令,卸载时移除命令。StdAfx.h里通常要包含rxdefine.h、dbents.h、acdb.h、acutmacro.h这类基础头文件,ARX的编译对包含顺序有要求,提前编译能省掉大量“头文件找不到”的麻烦。

2.3 一套完整的ARX程序生命周期

ARX程序的运行不是从main函数开始的。CAD加载arx文件时,操作系统会调用DLL的入口点,ARX框架再回调你提供的acrxEntryPoint函数。这个函数接收一系列枚举消息,其中最重要的是AcRx::kInitAppMsg和AcRx::kUnloadAppMsg,分别在加载和卸载时触发。实例里在kInitAppMsg分支中注册命令,在kUnloadAppMsg分支中移除命令并释放资源。

命令注册用的是acedRegCmds->addCommand,它需要三个关键参数:全局命令前缀、全局命令名、本地化命令名。比如注册一个叫“MakeEnt”的命令,用户在命令行输入MakeEnt就能触发。实际操作时一个经典翻车点是命令名带中英文混排,或者注册了相同的命令名,后者会导致第二次加载arx时命令重复注册,命令行直接报错。实例在卸载分支里对应调用removeCommand,把注册过的命令逐一清掉,这是很多初版ARX程序最容易漏的一步。

3. 核心代码逐段拆解:从类声明到图元被画在屏幕上

3.1 实体类的诞生:继承AcDbEntity并挂上运行时类信息

自定义实体的第一步是写出一个继承AcDbEntity的类。实例里的类声明如下(我做了一处典型简化,但结构完全对应原工程):

// AcDbCustomEntity.h #pragma once #include "dbmain.h" class AcDbCustomEntity : public AcDbEntity { public: ACDB_DECLARE_MEMBERS(AcDbCustomEntity); AcDbCustomEntity(); virtual ~AcDbCustomEntity(); // 设置和获取业务数据 void setCenter(const AcGePoint3d& pt); AcGePoint3d center() const; void setRadius(double dRadius); double radius() const; // 必须重写的虚函数 virtual Adesk::Boolean worldDraw(AcGiWorldDraw* mode) override; virtual Adesk::Boolean subGetGripPoints( AcGePoint3dArray& gripPoints, AcGiViewport* vp = nullptr, double curViewGridSize = 0.0, double gripSize = 0.0) const override; virtual Acad::ErrorStatus subTransformBy(const AcGeMatrix3d& xform) override; virtual Acad::ErrorStatus dxfIn(AcDbDwgFiler* filer) override; virtual Acad::ErrorStatus dxfOut(AcDbDwgFiler* filer) const override; private: AcGePoint3d m_center; double m_radius; };

ACDB_DECLARE_MEMBERS这个宏很关键,它给类挂上了ARX运行时类型识别所需的静态成员,没有它,CAD没法把你的对象识别成“一个实体类”,后面所有工作都无从谈起。setCenter和setRadius是业务方法,用来给图元注入数据,实操中这里往往还会放管道直径、设备型号、楼层编号之类的属性。worldDraw负责把对象画出来,subGetGripPoints负责提供夹点,subTransformBy处理拖动、移动、旋转时的几何变换,dxfIn/dxfOut负责存档和读档。

3.2 worldDraw:实体如何被真正画出来

worldDraw是整个自定义实体里最直观也最需要小心的函数。CAD的图形引擎在需要重绘时会调用它,参数是一个AcGiWorldDraw指针,它提供了一组“画线、画面、画文字”的接口。实例里画的是一个最简单的圆,代码相当短:

Adesk::Boolean AcDbCustomEntity::worldDraw(AcGiWorldDraw* mode) { // 画一个以m_center为圆心、m_radius为半径的圆 mode->geometry().circle(m_center, m_radius); return Adesk::kTrue; }

这里有个容易被误解的点:worldDraw返回kTrue表示“我已经把图形画完了”,CAD不再调用后续的视图相关绘制;返回kFalse则会让CAD继续调用viewportDraw来做更精细的绘制。实例里直接返回kTrue就够了。另一个细节是,worldDraw里不要做任何耗时操作,比如读数据库、算复杂曲面,因为图形缩放、平移、刷新时它会被频繁调用。如果确实需要缓存,常见的做法是把计算结果存在实体的私有成员里,用脏标记判断是否需要重算。

参数方面,AcGiWorldDraw的核心是geometry()返回的AcGiGeometry对象,circle这个重载接收圆心和半径。要画线段就用polyline或moveTo/lineTo,要画文字则要构造AcGiTextStyle。这个接口的坐标系是世界坐标系,不需要手动做视图变换,但如果实体内部数据用的不是世界坐标,记得在这层转换掉。

3.3 夹点与变换:用户拖拽时发生了什么

自定义实体如果不重写夹点和变换函数,它在CAD里的行为会很奇怪——能选中,但一拖动就消失或者变形。原因很简单:CAD默认按实体的包围盒做变换,而包围盒对圆、圆弧这类对象完全无效。实例里处理了这两个函数:

Acad::ErrorStatus AcDbCustomEntity::subGetGripPoints( AcGePoint3dArray& gripPoints, AcGiViewport* vp, double curViewGridSize, double gripSize) const { // 把圆心作为唯一夹点 gripPoints.append(m_center); return Acad::eOk; } Acad::ErrorStatus AcDbCustomEntity::subTransformBy(const AcGeMatrix3d& xform) { // 让圆心跟随变换矩阵移动 m_center.transformBy(xform); return Acad::eOk; }

subGetGripPoints的第一个参数gripPoints是输出参数,调用方传入一个AcGePoint3dArray,你往里append多少个点,CAD就显示多少个夹点。实例里只放了一个圆心夹点,所以用户拖这个圆时只能抓住圆心。如果你做的是矩形,应该把四个角点都放进来;做管线的话,要放起点、终点、转弯点。

subTransformBy接收一个AcGeMatrix3d矩阵,这是CAD内部所有变换的统一表示,平移、旋转、缩放最终都是这个矩阵。AcGePoint3d自带transformBy方法,直接用矩阵去更新圆心坐标。注意这个方法只负责几何变换,不负责更新自定义的业务属性。如果实体的某些属性也跟位置相关,比如管线的两端标高,你需要在这里一并变换,否则会出现“图形移动了,属性还是老坐标”的奇怪错位。

3.4 dxfOut与dxfIn:存档与打开的完整链路

实体画在屏幕上之后要能存盘、能重新打开。ARX的机制是:CAD存档时调用dxfOut写数据,打开时调用dxfIn读数据。实例里的两个函数加起来大概二十行,但写错了会出现“存完盘再打开,实体全部丢失”的严重事故:

Acad::ErrorStatus AcDbCustomEntity::dxfOut(AcDbDwgFiler* filer) const { // 先让基类写公共数据 AcDbEntity::dxfOut(filer); // 再写业务数据 filer->writeItem(m_center); filer->writeItem(m_radius); return Acad::eOk; } Acad::ErrorStatus AcDbCustomEntity::dxfIn(AcDbDwgFiler* filer) { AcDbEntity::dxfIn(filer); filer->readItem(&m_center); filer->readItem(&m_radius); return Acad::eOk; }

很多初学者会漏掉第一行,也就是先调用基类的dxfOut/dxfIn。AcDbEntity自己有一堆内部数据要读写,比如图层、颜色、线型、扩展数据,不调用基类版本会导致实体的层和颜色丢失。writeItem和readItem是成对出现的,写入顺序必须和读取顺序完全一致。如果后面在类里加了新成员,必须同时改这两个函数,而且只能追加在最后面,否则老图纸打开时数据错位。

关于dxfOut还有一个高频踩点:除了DWG存取要用dxfIn/dxfOut之外,如果用户执行“另存为DXF文件”或通过DXF文件交换数据,还需要单独处理dxfInFields和dxfOutFields这对函数。实例没有涉及,但二次开发时经常需要补上。字段写法的格式是既定的(比如用AcDb::kDxfXdBeginAsciiString这类标记),自己发明序号会导致DXF文件无法被其他CAD识别。

3.5 业务数据如何驱动绘制:以圆的半径变化为例

实体不应该是一个画死的图形,而是要跟业务数据联动。实例里setRadius这个函数就是一个典型的联动入口:

void AcDbCustomEntity::setRadius(double dRadius) { m_radius = dRadius; // 标记实体数据已变化 assertOpenForWrite(); recordGraphicsModified(); }

assertOpenForWrite是ARX里一个很重要的概念——实体在数据库里是被“打开读”还是“打开写”是有状态的。修改任何成员变量之前,必须确保当前是写状态,否则会抛出异常。recordGraphicsModified则通知CAD图形引擎这个实体的显示需要刷新,缺少这步,你改了半径但屏幕上还显示旧图形,必须手动regenerate才能看到变化。从二次开发的角度,这两个调用几乎是所有数据修改方法的固定起手式,建议直接形成肌肉记忆。

4. 从编译到在CAD里看到实体的完整流程

4.1 工程配置里最容易“玄学”的三处设置

拿到实例代码后,第一步不是看代码,而是检查VC++工程的配置。ARX项目的配置有三个经典坑点:SDK版本与CAD版本不匹配、字符集设置不一致、链接器没有指定arx入口。我一般会按下面的顺序核对:

项目属性 -> C/C++ -> 常规 -> 附加包含目录:指向ObjectARX SDK的inc目录 项目属性 -> 链接器 -> 常规 -> 附加库目录:指向ObjectARX SDK的lib目录 项目属性 -> 链接器 -> 输入 -> 附加依赖项:rxapi.lib、acdb.lib、acge.lib、acad.lib 项目属性 -> 链接器 -> 高级 -> 入口点:acrxEntryPoint 项目属性 -> 常规 -> 字符集:使用多字节字符集

入口点这个东西最容易被忽略。ARX的DLL不使用经典的DllMain作为入口,而是要求链接器把入口点设置为acrxEntryPoint。如果这栏没填或者填错,arx文件虽然能生成,但CAD加载时会直接提示“无法找到入口点”,然后拒绝加载。字符集则是因为老版本SDK的类型定义大量使用char而不是wchar_t,如果工程默认是Unicode字符集,编译时会冒出海量类型不匹配的错误。

4.2 命令注册与加载:用一条ARXLOAD命令开始调试

编译成功之后,启动CAD,在命令行输入ARXLOAD(或者用appload工具),选择生成的.arx文件。加载成功后命令行会显示一段由acrxEntryPoint打印出来的初始化文本,实例里通常会在kInitAppMsg分支用acutPrintf打一句话。接着输入实例里注册的命令名,比如MakeEnt,屏幕上就该出现一个圆。

这个“出现一个圆”的过程背后走了一整条链路:命令被CAD解析,分派到对应的全局函数,函数里创建一个AcDbCustomEntity对象,调用setCenter和setRadius填入数据,然后用postToDatabase把实体加进当前图形数据库,最后调用实体自身的close方法。postToDatabase是AcDbEntity提供的方法,内部会把它加入块表记录并分配ObjectId。如果函数里忘了调用close,实体对象会一直处于“打开”状态,内存泄漏还是小事,严重时会导致CAD在操作这个实体时直接崩溃——这是ARX新手最常见的崩溃来源之一。

4.3 调试技巧:断点怎么打才能在崩溃前拦下来

ARX程序跑在CAD进程里,调试方式和普通应用程序不一样。我一直习惯的做法是把CAD设为“启动调试的程序”,然后在VC++工程属性的“调试 -> 命令”里填上CAD安装目录里的acad.exe路径,这样按F5就会启动CAD并自动附加调试器。断点可以打在命令函数的入口、worldDraw、dxfOut这些关键位置。

有一个实操技巧特别值得说:worldDraw这类被CAD频繁回调的函数不要设置条件断点,否则会在图形刷新时断到你怀疑人生。我自己的习惯是先在命令函数里打断点,确认实体创建和数据填充没问题后,再在worldDraw里打一个不断执行的断点观察首次绘制。另外,如果不小心把CAD搞崩溃了,在VC++的“调试 -> 异常”里勾选“Access Violation”,崩溃时调试器会停在出问题的那一行,这是ARX跑飞之后最大的后悔药。

提示:每次修改代码并重新编译后,需要先把旧的arx卸载(ARXUNLOAD命令或直接关闭CAD),再加载新的arx。 Windows下的DLL文件在被CAD占用时,编译器的链接环节会报“file in use”错误,这时候关掉CAD再重编译是唯一解。

5. 避坑与排查:ARX自定义实体最常见的六种翻车现场

5.1 “arx文件加载后命令不存在”的排查思路

现象:ARXLOAD加载显示成功,但输入命令提示“未知命令”。原因有三类:一是命令注册函数只写了全局名和本地名中的一个,或者本地名和全局名设成了完全相同的字符串;二是注册命令的代码被放在了acrxEntryPoint的某个错误分支里,典型的是放在kUnloadAppMsg里,加载等于注册了个寂寞;三是removeCommand的参数跟addCommand的参数不一致,导致命令被“误删”。解决方法是回到Main.cpp的acrxEntryPoint,打印kInitAppMsg分支处的所有命令注册代码,确认addCommand的参数没问题后,再用ACAD命令行里的(vlax-vbarun)之类命令辅助排查。我更推荐在addCommand前后各加一句acutPrintf,把命令名打印出来,加载时一眼就能看出命令是否注册成功。

5.2 实体画出来了,但选中后一拖动就“飘走”

现象:实体在屏幕上正常显示,点选也能选中,但用夹点拖动时图形跟鼠标脱节,松开后位置错乱。原因基本是subTransformBy没有被触发,或者触发了但只变换了一部分数据。实例里圆的变换只有圆心,所以很安全;换成矩形之类的多顶点图形,如果你只变换了其中一个顶点、忘了变换其他顶点或内部属性,拖动后图形就会撕裂。解决方法是把变换逻辑拆成两步:先把所有跟位置相关的成员都transformBy,再用变换后的数据重新计算包围盒和相关缓存。这个函数是排查重点,建议在subTransformBy里加断点,拖拽时观察xform矩阵的数值变化。

5.3 存档后重新打开,自定义实体消失不见

现象:保存DWG后关闭文件,再打开时实体没了,或者实体还在但属性全丢。原因多半是dxfIn/dxfOut没有成对写、某个成员只写了出没写进,或者忘了在最前面调用基类的dxfIn/dxfOut。解决方法是逐个成员核对读写顺序,确认写出的顺序和读入的顺序完全一致。一个常见误用是使用自定义宏或辅助函数去序列化数据而不是直接用writeItem/readItem,这会让顺序维护变得很困难。应对办法是把实体的所有成员变量列一张表,按照表顺序编好编号,dxfOut按编号写、dxfIn按编号读,以后每增加一个成员就追加一个编号,永不改动已有编号。

5.4 程序崩溃在close函数附近且报错信息不明

现象:实体创建后调用close(),CAD直接崩溃,或者报一个带地址的纯虚函数调用错误。原因通常是实体类的构造函数里没有正确初始化成员变量,比如AcGePoint3d这种对象构造函数会自动初始化,但double类型的m_radius如果没有在初始化列表中赋初值,就会是一个随机值,某些内部校验会因这个随机值出问题。另一个常见原因是类声明里遗漏了ACDB_DECLARE_MEMBERS宏,或者类的实现里遗漏了ACDB_IMPLEMENT_MEMBERS宏的定义。处理方法是给所有成员变量在构造函数中用初始化列表赋值,同时检查类的实现文件里是否写了ACDB_IMPLEMENT_MEMBERS(AcDbCustomEntity)这行关键宏。这个宏生成运行时类型信息的具体实现,缺了它,CAD没法对这个类做安全的向下转换,很多内存错误都源于此。

5.5 夹点数量、位置完全不对

现象:选中实体后,期望出现一个或两个夹点,结果出现十几个甚至夹点位置全跑到原点。原因在于subGetGripPoints的override写得不完整——比如函数签名里的参数个数写错,会导致这个函数根本没被调用,CAD退回到默认的包围盒夹点;或者gripPoints数组没有清空,往一个已经包含旧数据的数组里append,夹点就会越来越膨胀。解决方法是确认override关键字匹配父类签名,并在函数一进来就执行gripPoints.setLogicalLength(0)清空数组,然后只添加你想暴露的夹点。还有一个容易忽略的问题:如果实体是动态块或包含嵌套图元,夹点不一定只由这个函数决定,但自定义实体时它就是唯一入口。

5.6 版本升级后编译通过但加载即崩溃

现象:换了一个新版本的ObjectARX SDK后,代码编译顺利通过,但在CAD里一加载就崩溃。原因是ObjectARX的某些类布局在不同版本间发生了变化,旧版本编译的arx虽然接口函数名没变,但内部虚函数表偏移已经对不上。另一个原因是SDK升级后,某些废弃的接口虽然在头文件里还能找到,但实现已经改变,调用后直接踩到非法内存。解决方法是升级SDK后不要只编译一次就完事,要对照SDK自带的迁移文档逐项检查:基类的虚函数声明、构造函数中的成员初始化顺序、以及任何涉及AcDbObjectId和AcDbHandle的操作。这一步没有捷径可走,但至少要把所有包含“Obsolete”或“Deprecated”注释的头文件扫一遍。

6. 二次开发:把圆的实例改造成你自己的业务图元

这个实例最值钱的地方在于它的可扩展骨架。你已经看完了类声明、绘制、变换、存取、夹点这五件事,现在试着把它换成自己的业务模型。做法很简单:先在你的类里加业务成员,比如管径、材质、埋深;然后改worldDraw,把原来画圆改成画一段管道(截面线加两条边线);再改subGetGripPoints,把夹点从圆心改为管道起点和终点;最后改subTransformBy,让起点终点随矩阵更新。这一套流程就是实例本身示范过的,唯一要新写的是业务成员之间的约束,比如管道长度不小于管径。

我自己在改造这种实例时踩过一个印象很深的坑:往实体里塞了一个指向另一个对象的指针成员,这个指针不是AcDbObjectId而是裸指针。当时存盘功能正常,但一旦图形被复制、镜像或者进入块编辑器,全新对象的指针指向的是老对象地址,CAD崩得毫无预兆。后来改成用AcDbObjectId保存引用关系,并实现subDeepClone和subWblockClone这两个克隆相关函数,问题才彻底解决。如果你也在做带关联关系的自定义实体,这一步早晚会遇到,不如在改造时就把引用设计成ObjectId,而不是等出了事再补。

验证改造结果的方式,除了反复Open/Close、缩放平移之外,还有一个很有效的套路:用CAD的“图形审计(AUDIT)”命令跑一遍图形文件,再执行一次“清理(PURGE)”。审计能发现实体内部数据不一致,清理能暴露对象引用关系错误。这两关都过了,至少说明你的序列化和对象生命周期没有大毛病。至于视觉表现和交互手感,只能靠反复拖拽和实际打印样张来验证。

从那以后,我每次改完这种自定义实体都要强制走一遍AUDIT、复制粘贴、块插入、另存DXF这四步操作,哪一个环节出问题都说明序列化或克隆逻辑有遗漏。这个习惯帮我挡掉了好几次上线现场的“灵异崩溃”。希望这个实例和这篇拆解能帮你把ARX自定义实体的门槛再降低一截。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/19 4:12:46

开源代码审查工具:基于AST与规则引擎的自动化实践

刚接手团队里代码审查这件麻烦事的时候,我最大的感受就是:人肉 review 太累了。每天十几个 MR 堆在那里,每个都要逐行看,等看完后面又堆上来一批,更别提那些反复出现的低级问题——硬编码密钥、SQL 拼接、空指针隐患—…

作者头像 李华
网站建设 2026/10/19 4:12:30

2026毕设实战:SSM+Vue酒店管理系统设计与实现全攻略

1. 项目概述与选题价值又到了一年一度的毕业设计季节,每年这个时候,后台总有不少同学来问我:"酒店管理系统这个题目还能不能做?""SSM Vue 是不是已经过时了?" 我的答案一直是:能做&am…

作者头像 李华
网站建设 2026/10/19 4:12:12

模拟前端芯片HI3593实战:集成PGA+16位ADC的信号链设计要点

简介:面向航空电子与嵌入式系统开发者,这是一份HI3593模块的使用说明与驱动例程合集,专注于解决微控制器SPI接口与ARINC 429总线之间的通信问题。该模块基于CMOS工艺设计,片内集成协议处理单元,开发者仅需通过SPI读写寄…

作者头像 李华
网站建设 2026/10/19 4:09:55

JavaFX集成VLC播放器全攻略:缓冲优化与RTSP流实战

JavaFX 里播放视频,做到后面基本都会碰壁。自带的 MediaPlayer 能播 MP4、这种规规矩矩的格式,可一旦遇到 H.265、TS 流、RTSP 监控流,或者某些摄像头的私有封装,它是真的处理不了。这时候把 VLC Media Player 嵌进来,…

作者头像 李华
网站建设 2026/10/19 4:09:38

自然语言日期解析利器:adapt-parser核心用法与避坑指南

做日程提醒系统的时候,我被“自然语言日期解析”折磨得不轻。用户根本不按ISO标准输入,张口就是“下周二下午三点前提醒我”,这种话让正则表达式很难收场。后来换了Python的adapt-parser包,一套专注于自然语言日期时间解析的开源方…

作者头像 李华
网站建设 2026/10/19 4:09:26

开源代码审查工作流open-code-review:从静态检查到AI辅助的PR实战

每次开PR之前,你是不是也有这种感觉:本地测试全绿,代码写得自觉挺干净,结果一丢到PR里,心里就开始打鼓——会不会漏了什么边界条件?某个写法是不是会让同事看得一头雾水?更现实的是,…

作者头像 李华