简介:LibreCAD 是一款基于 Qt 框架、以 C++11 编写的跨平台开源 2D CAD 程序,支持 Windows、Linux 与 macOS,可读写 DXF、DWG 文件并导出 DXF、PDF、SVG。该资源面向设计师、工程师、学生及需要免费 CAD 替代方案的个人与企业用户,解决二维绘图、精确测量、图层管理、块与外部参照等日常设计需求,同时兼顾与 AutoCAD 文件的兼容性。压缩包为 zip 格式,大小约 16.46MB,上游未提供文件总数与类型明细,故不展开具体构成。目前已有 2769 人浏览学习,具备一定社区关注度。软件界面高度可定制,支持数十种语言翻译,并内置 LISP 接口,便于编写宏或插件扩展功能;初学者可借直观界面快速上手,资深用户也能利用专业级绘图工具完成复杂二维设计,适合作为跨平台 2D 制图的实用工具包。
1. 从一张改不动的图纸说起:LibreCAD 到底能接住什么活
上周有个做非标设备的朋友发来一张 DXF,说客户只给了图纸,要他在上面改两处孔位、补一圈标注,再导成 PDF 发回去。他电脑上没装任何商业 CAD,问我有没有能直接打开、改完还能存回 DXF 的东西。我让他试了 LibreCAD——Qt 框架、C++11 写的跨平台 2D CAD,读 DXF/DWG,写 DXF/PDF/SVG,界面能自己改,翻译有几十种。这类“只改二维、不碰三维、不想装几个 G 的套件”的场景,它接得住。这篇就把这份源码包拆开讲:怎么编、DXF 读写走哪条路、参数在哪调、哪些坑我踩过。适合想拿它当二次开发底座,或者只想找个轻量 2D 绘图工具落地的从业者。
2. 把源码跑起来:Qt 版本、编译链与第一次构建
拿到源码包第一件事不是急着点编译,而是先确认工具链对不对。LibreCAD 是 Qt 系的重度使用者,界面、绘图事件、文件对话框全挂在 Qt 上,Qt 大版本选错,后面报的错会非常分散,排查成本高。我的习惯是先看工程文件里声明的 Qt 模块,再决定装哪套。
2.1 选型理由:为什么是 Qt5 而不是随手装最新
源码里大量使用QWidget、QPainter、QAction这类 QtWidgets 体系,绘图区是自绘的,事件循环靠 Qt 的信号槽驱动。Qt6 把不少旧接口挪走或改了签名,直接拿 Qt6 编老代码,常见结果是编译期一堆deprecated警告混着少量硬错误,改起来没完。稳妥做法是锁定 Qt5 的一个较新小版本,配合同一代的 qmake 或 CMake。判断依据很简单:工程根目录有.pro就走 qmake,有CMakeLists.txt就走 CMake,两者都在就优先跟仓库里 CI 配置用的那套,别自己发明。
依赖上还需要这些:C++11 及以上的编译器(GCC 7+、Clang 6+、MSVC 2017+ 都行)、make 或 ninja、以及可选的 Boost(部分几何/工具代码会用到)。缺 Boost 时编译会在链接阶段报未定义符号,而不是在配置阶段,这点要心里有数。
2.2 一次完整的构建流程
下面这套是我在 Linux 上跑通的顺序,Windows 下把包管理器换成对应的安装方式即可,命令主体不变。
# 1. 装依赖(以 Debian 系为例,Qt5 开发包 + 构建工具) sudo apt install build-essential qtbase5-dev qttools5-dev-tools \ libqt5svg5-dev libboost-dev cmake ninja-build # 2. 进源码目录,先看用哪套构建系统 ls | grep -E "CMakeLists.txt|\.pro" # 3a. 有 CMakeLists.txt 时 cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release cmake --build build -j"$(nproc)" # 3b. 只有 .pro 时 qmake CONFIG+=release make -j"$(nproc)"逻辑说明:-S . -B build把源码和产物分开,避免污染工作区;-DCMAKE_BUILD_TYPE=Release关掉调试符号并开优化,2D 绘图在 Debug 下拖拽会明显发涩;-j$(nproc)按核数并行,Qt 项目文件多,串行编要等很久。qmake 路线里CONFIG+=release同理。编完产物一般在build/下的可执行文件或unix/之类子目录,用find build -name "librecad*" -type f -executable定位,别靠猜。
参数上真正需要你动的只有三处:Qt 的安装前缀(多版本共存时用-DCMAKE_PREFIX_PATH=/path/to/Qt5指定)、构建类型、以及是否开单元测试(-DBUILD_TESTING=ON,首次构建建议关掉,省时间)。其余保持默认,改多了反而引入变量。
2.3 第一次启动要确认的三件事
编出来能启动只是第一步。打开后先做三个动作验证环境是否真的健康:新建图纸画一条线再存成 DXF,确认写路径通;打开一个现成 DXF,确认读路径通;导出一次 PDF,确认打印/导出后端没缺库。三步都过,说明 Qt 的绘图、文件、打印模块都挂上了。哪一步崩,错误信息基本能直接指向缺的模块,比盲猜快得多。
提示:首次构建如果卡在某个
moc_*.cpp报错,九成是 Qt 版本和工程声明不一致,先统一版本再谈别的。
3. DXF 读写这条链路:实体、图层与导出格式的取舍
LibreCAD 的核心价值不在界面,在它对 DXF 的读写实现。DXF 是文本格式的组码流,每行一个组码加一个值,实体、图层、线型、块都靠组码区分。它内部把 DXF 实体映射成自己的图形对象,画布上操作的是内部对象,存盘时再序列化回 DXF。理解这条链路,你才知道哪些东西改完能存回去、哪些会在往返中丢。
3.1 读进来之后,什么被保留、什么被压平
DXF 里常见的实体有 LINE、CIRCLE、ARC、LWPOLYLINE、TEXT、DIMENSION、INSERT(块引用)。LibreCAD 对基础几何实体支持完整,块引用会展开或保留为块,取决于导入设置。真正容易出问题的是两类:一是复杂实体(如带样条的控制点、某些填充),二是自定义对象(第三方软件塞进去的扩展数据)。前者可能被近似成折线,后者基本被忽略。所以拿到一张别人给的图,先看图层和实体类型清单,再决定要不要在 LibreCAD 里改,别一上来就动刀。
判断方法很土但有效:导入后打开图层管理器,看图层数量对不对;再用选择工具框选,看状态栏报的实体数是否和原图接近。数量差太多,说明有实体被丢弃或合并了,这时候改图就是给自己埋雷。
3.2 写出去:DXF、PDF、SVG 各自适合什么
三种输出格式定位完全不同,选错会白忙。DXF 是给下游 CAD 继续编辑用的,要保证实体语义完整;PDF 是给打印和归档用的,讲究版面和线宽;SVG 是给网页或矢量排版用的,讲究路径干净。同一个图导出三种,参数关注点不一样。
| 输出格式 | 主要用途 | 关键参数 | 常见翻车点 |
|---|---|---|---|
| DXF | 下游继续编辑 | 版本(R12/R14/2000+)、是否展开块 | 版本选太低丢实体属性 |
| 打印、归档 | 纸张、比例、线宽映射 | 比例没设对,图框对不上纸 | |
| SVG | 网页、矢量排版 | 路径精度、是否内嵌字体 | 文字转曲缺失导致字体丢失 |
导出 DXF 时版本选择是第一个决策点。老版本兼容性好但表达能力弱,新版本能带更多属性但下游老软件可能读不了。我的默认是选一个中间代版本,先保证对方能打开,再谈属性完整。导出 PDF 前一定先设好图纸尺寸和比例,LibreCAD 的打印设置里比例是独立于绘图单位的,两个都设错会得到一张“看着对、量着不对”的图,这种图发出去最坑人。
3.3 用脚本批量验证往返一致性
单张图靠眼睛看,批量就得靠脚本。下面这段用 Python 粗查 DXF 里的实体计数,导入导出前后各跑一次,数字对不上就人工介入。它不解析几何,只看组码层面的实体数量,够用来做第一道筛子。
# 统计 DXF 中常见实体出现次数,用于导入/导出前后对比 import re from collections import Counter ENTITY_TYPES = ("LINE", "CIRCLE", "ARC", "LWPOLYLINE", "TEXT", "INSERT") def count_entities(path): counter = Counter() with open(path, "r", errors="ignore") as f: lines = [ln.strip() for ln in f] # DXF 组码成对出现:偶数行是组码,奇数行是值 for i in range(0, len(lines) - 1, 2): if lines[i] == "0" and lines[i + 1] in ENTITY_TYPES: counter[lines[i + 1]] += 1 return counter before = count_entities("input.dxf") after = count_entities("output.dxf") print("导入前:", dict(before)) print("导出后:", dict(after)) print("差异:", {k: after[k] - before[k] for k in set(before) | set(after)})逻辑说明:DXF 的组码是“组码行 + 值行”交替,实体起始用组码0标记,紧跟的值就是实体类型名。按步长 2 遍历,命中0且值在目标集合里就计数。参数上ENTITY_TYPES按你实际图纸里出现的类型增删,别一股脑全塞,否则统计噪音大。差异字典里正数表示导出后变多(可能块被展开),负数表示丢失(要重点查)。这套脚本我一般挂在批量转换前后,跑一遍心里有底。
注意:这个脚本只做数量级校验,不校验几何精度。坐标被近似、圆弧变折线这类问题它看不出来,需要另做几何比对。
4. 界面定制与二次开发:从改一个工具栏到接自己的命令
LibreCAD 的界面是高度可定制的,这不是宣传语,是它的架构决定的:菜单、工具栏、快捷键都从配置和资源里读,动作(action)和命令是解耦的。想加一个自己的绘图命令,不用改核心绘图代码,挂一个 action 再注册命令即可。对想拿它当底座做行业工具的人来说,这是最值钱的部分。
4.1 定制从哪一层下手
定制分三个层次,成本递增:第一层是改界面布局和快捷键,纯配置,不动代码;第二层是加自定义命令,写一个继承自命令基类的类,注册进命令表;第三层是改绘图内核,比如加新的实体类型,这就碰到底层数据结构了。多数需求停在第二层就够。判断标准:如果你的功能是“对已有实体做操作”或“生成标准实体”,第二层;如果要“表达一种 DXF 里没有的新图元”,才考虑第三层。
第一层的落地方式是找界面配置文件(工具栏、菜单的定义通常在资源或配置目录里),按现有条目格式加一行,重启生效。改之前先备份,格式错一个字段可能导致整个工具栏不显示,这种玄学问题排查起来很费时间。
4.2 挂一个自定义命令的最小骨架
下面是一个命令类的骨架,展示注册和执行的接口形态。具体基类名和注册函数以你手上源码版本为准,思路是通用的:定义类、实现执行入口、在初始化处注册。
// 自定义命令骨架:继承命令基类,实现执行逻辑 #include "rs_command.h" // 基类头文件,按实际路径调整 class MyOffsetCommand : public RS_Command { public: MyOffsetCommand() : RS_Command() {} // 命令执行入口,交互流程在这里组织 void activate() override { // 1. 提示用户选择实体 // 2. 读取偏移距离参数 // 3. 调用几何工具生成新实体并加入图形容器 // 4. 刷新视图 } // 命令名,供菜单/快捷键绑定 QString getName() const override { return "MyOffset"; } }; // 在命令初始化处注册(伪代码,按实际注册表调整) // commandRegistry->addCommand(new MyOffsetCommand());逻辑说明:activate()是命令被触发后的入口,交互式命令一般在这里分步取用户输入,而不是一次性要齐所有参数;getName()返回的字符串是绑定菜单项和快捷键的钥匙,命名要唯一且语义清楚。参数上真正要你定的是交互顺序——先选实体还是先输距离,直接影响操作手感,建议跟主流 CAD 的习惯对齐,别自创。注册那一步是全局副作用,放错初始化时机(比如在图形容器还没建好时注册)会导致命令能点但执行崩,这是二次开发里最常见的翻车点之一。
4.3 翻译与本地化怎么接
界面有几十种翻译,说明它的字符串都走了翻译机制。加自己的命令时,界面文案别硬编码中文,走同一套翻译流程,否则以后想给别人用还得回头改。做法是把文案放进翻译源文件,用工具生成对应语言的翻译文件,再在启动时加载。这一步不难,但容易被跳过,跳过的代价是后期返工。
5. 避坑清单:编译、导入导出、定制里最容易翻车的五处
这一节是我自己踩过、也见别人踩过的坑,按“现象 → 原因 → 解决”写,照着对号入座能省不少时间。
坑一:编译到一半报undefined reference,指向某个几何函数。现象是配置阶段全绿,链接阶段炸。原因是可选依赖没装全,部分工具代码在缺依赖时被条件编译跳过,但调用方没跳过。解决是先看报错符号属于哪个模块,回配置日志里找该模块是否被标记为未找到,补装对应开发包后清空构建目录重来,别在旧缓存上增量编。
坑二:DXF 导入后图“看着对”,但尺寸量出来不对。现象是图形比例整体偏移。原因是图纸单位($INSUNITS)和绘图单位没对齐,导入时按默认单位解释。解决是导入前确认源图单位,导入后在图纸设置里核对单位与比例,别信“看起来差不多”。
坑三:导出 PDF 图框对不上纸。现象是内容被裁或留白巨大。原因是打印比例和纸张尺寸是两套独立参数,只设了一个。解决是先把纸张定死,再按图框实际尺寸反算比例,导出前用打印预览确认,预览对不上就别导。
坑四:自定义命令能点,一点就崩。现象是菜单项正常显示,执行即退出。原因是命令注册时机早于图形容器初始化,activate()里访问了空容器。解决是把注册挪到主窗口和文档对象都就绪之后,或在activate()里先判空再操作。
坑五:改完界面配置,工具栏整个消失。现象是重启后界面缺一块。原因是配置文件格式错一个字段,解析失败后整段被丢弃。解决是改前备份,改后逐字段比对,出问题直接回滚备份,别试图在坏配置上修。
提示:这五条里,坑一和坑四属于“环境/时序”类,靠日志定位;坑二、三、五属于“参数/配置”类,靠预览和备份兜底。两类问题的排查手法不一样,别混着来。
6. 进阶:用几何校验脚本给批量转换上保险
到这一步,工具能编、能读、能写、能定制,剩下的是“怎么确认它没骗你”。前面那个实体计数脚本只能查数量,查不出坐标漂移。真正要上批量生产,得加一层几何校验:对关键实体(比如图框四角、基准孔圆心)在转换前后取坐标,算偏差,超过阈值就拦下来。这套思路不限于 LibreCAD,任何 CAD 格式转换都适用。
具体做法是:先用 LibreCAD 导出一份“基准 DXF”,再用脚本从基准和待检文件里各抽若干特征点,逐点算欧氏距离。阈值按你的加工精度定,二维图纸一般取到小数点后三到四位就够。下面这段是抽点比对的骨架,extract_points按你的实体类型补实现。
# 几何校验骨架:抽取特征点并比对偏差 import math def extract_points(path, entity_type="CIRCLE"): """从 DXF 抽取指定实体的特征点(示例:圆心)""" points = [] with open(path, "r", errors="ignore") as f: lines = [ln.strip() for ln in f] i = 0 while i < len(lines) - 1: if lines[i] == "0" and lines[i + 1] == entity_type: # 向后找组码 10/20 作为坐标,实际需按实体类型解析 x = y = None j = i + 2 while j < len(lines) - 1 and lines[j] != "0": if lines[j] == "10": x = float(lines[j + 1]) if lines[j] == "20": y = float(lines[j + 1]) j += 2 if x is not None and y is not None: points.append((x, y)) i = j else: i += 2 return points def max_deviation(ref, test, tol=1e-3): """按最近点匹配算最大偏差,超阈值返回失败""" worst = 0.0 for px, py in ref: d = min(math.hypot(px - qx, py - qy) for qx, qy in test) worst = max(worst, d) return worst, worst <= tol ref_pts = extract_points("baseline.dxf") test_pts = extract_points("converted.dxf") dev, ok = max_deviation(ref_pts, test_pts) print(f"最大偏差 {dev:.6f},判定 {'通过' if ok else '超限'}")逻辑说明:extract_points按组码扫描,命中目标实体后在其内部找坐标组码,遇到下一个0就结束当前实体,这样不会串到别的实体上。max_deviation用最近点匹配而不是按顺序配对,因为转换后实体顺序可能变,按序配会误报。参数tol是判定阈值,按图纸精度定,设太松等于没校验,设太紧会把正常近似判成失败。这套脚本我一般和前面的计数脚本串起来跑:先数量对,再几何对,两道都过才放行。
血泪经验是,早期我只查数量,结果一批图里圆弧被近似成折线,数量没变、形状变了,下游加工直接报废。从那以后我每次做批量格式转换,都强制走一遍“计数 + 几何”双校验,宁可多花几分钟,也不吃后悔药。这套习惯搬到别的 CAD 工具上也一样成立,校验脚本才是转换流程里真正的黑匣子。希望帮到你。
本文还有配套的精品资源,点击获取