QGC二次开发:从自定义面板到MAVLink消息
简介这是一份面向初学者的QGCQGroundControl二次开发入门项目代码适合具备少量C与QT基础、希望掌握无人机地面站定制与功能扩展的开发者。压缩包内共3个文件包含inscode项目配置、html说明页面及gitignore文件整体仅5KB虽体积小巧但聚焦核心源码与配置。教程内容覆盖软件名称与图标修改、官方中文版BUG修复与汉化、商标更换、信号与槽添加、QML与C交互、MAVLINK解析与发送、地图更换及自定义MAVLINK消息等关键环节同时涉及多机轨迹显示与地图画点功能。通过这份资料读者可以了解QGC二次开发的整体流程获得可直接运行的修改后QGC源码、开发环境配置虚拟机以及独家汉化版QGC的下载指引特别适合用于快速验证开发思路或作为项目起步参考。目前已有432人浏览学习也印证了其对入门者有切实的参考价值。1. QGC二次开发到底在改什么值不值得动这个手手里那台自研载荷通过串口把数据喂给了飞控链路里数据明明是通的可打开地面站界面上只有电池、卫星数和飞行模式这些通用仪表属于我们自己的传感器读数一个都看不到。QGC二次开发解决的就是这一类需求在开源的 QGC 地面站基础上往界面里加自己的面板往通信里加自己的协议让地面站真正贴合你手上的设备而不是套一个通用模板。它适合懂一点 MAVLink、想掌握全链路的开发者也适合要交付定制地面站的团队。这篇文章会从工程结构、编译跑通、挂自定义面板、排查踩坑到扩展自定义消息把一次完整二开的路子走通。真心建议先花半天跑通一个能编译的 QGC后面所有的改动都建立在它之上别在半成品上反复抠。2. 读懂QGC工程布局二开最该先认准的几块源码2.1 Qt与QML混合架构为什么这个结构对二次开发格外友好QGC 是一个典型的 Qt 应用界面层大量使用 QML底层逻辑用 C 实现。这个混合架构对二次开发的价值在于改界面的时候你不需要跟重型 C 编译流程死磕QML 文件写错了通常只是界面显示异常不会让整个程序直接崩溃而消息收发、设备状态管理这些核心逻辑放在 C 侧性能和可靠性有保障。两层之间通过注册机制和模型/视图方式连接界面层拿到的是 C 暴露出来的属性不会直接把底层数据铺到 QML 里。一开始扎进这个工程很多人会觉得自己在黑匣子里转圈这很正常。你先记住一条主线界面找 QML逻辑找 C两者通过“注册 QML 类型”和“属性绑定”沟通。入门阶段你的大部分改动只需要落在 QML 层C 层能看懂信号槽和属性注册就够了。我见过不少开发者一上来就啃 Vehicle 的通信代码结果界面还没跑通就先被消息流劝退这个顺序是反的。2.2 四个高频修改区UI、Vehicle、MAVLink 与 Utilities源码目录很多但二开真正高频进出的是四个区域。第一块是 ui 目录以及资源文件里的 QML地面站的工具栏、飞行仪表、参数设置界面全在这你想加按钮、改布局、塞新面板主要跟这里打交道。第二块是 src/Vehicle它差不多是飞行器在软件里的投影所有 MAVLink 消息进来之后的第一站就是这里收发消息相关的行为改动都集中在它身上。第三块是 MAVLink 协议库它负责消息的编解码。如果你只是调用现有协议可以不碰它可一旦要自定义消息就必须从这里下手后面第 6 章会专门展开。第四块是 Utilities 目录日志、坐标转换、格式化这类通用能力都在这。我通常会在二开第一天就把日志工具摸清楚因为排查消息丢失、面板不显示这类问题时日志是第一个要开的门。这四个区域的边界并不绝对但按这个地图去找代码比漫无目的地全库搜“voltage”省时间得多。2.3 启动链路从 main 函数到你的第一个 QML 窗口弄明白程序怎么启动能帮你判断自己的改动该放在哪一步。QGC 的启动流程去掉平台分支后可以简化为下面这个样子// QGC 启动流程精简示意省略了平台初始化和单例清理 int main(int argc, char *argv[]) { QGCApplication* app new QGCApplication(argc, argv); // toolBox 负责集中创建各类 Manager // 自定义 QML 类型的注册也放在这个阶段完成 app-toolBox()-load(); // 主窗口本体是 QMLC 只负责窗口外壳和生命周期 app-showMainWindow(); return app-exec(); }这段代码说明了两个关键点。一是 toolBox 是全局管理器的集中注册点你要注册一个新的 C 服务或一个新的 QML 类型通常要在它完成加载之前准备好二是主窗口本质是一个 QML 界面C 只提供窗口壳子。找到那个被资源文件引用的主窗口 QML你就找到了几乎所有界面的挂载点。后续往里加自定义面板本质上都是在这个 QML 组件树上找一个合适的位置插进去。3. 从拉源码到首跑把QGC编译环境的全过程走一遍3.1 环境准备两条主流路线的版本对齐建议二开最忌讳的就是环境没对齐就开跑结果编译报错全跟版本有关。Ubuntu 和 Windows 是两条主流路线。Ubuntu 下我习惯用 Qt 官方安装器装完整开发套件再配 GCC 和必要的构建工具Windows 下常见的组合是 MSVC 编译器加 Qt 对应位的开发库也可以用 MinGW 组合但迭代时注意别混用。无论哪条路线位数必须和编译目标一致64 位环境配 64 位 Qt这个不齐后面链接阶段全是怪错。QGC 对 Qt 版本有明确要求以官方仓库说明为准我个人会选 Qt 5 的稳定分支并锁住版本因为二开团队一般不希望地面站的界面框架频繁迁移。安装完成后记得确认 qmake 命令指向的是你刚装的那套 Qt而不是系统自带的旧版本这一步用qmake -v验证就行。很多人的编译翻车根子就出在 PATH 里混进了两个 Qt 版本。3.2 拉取源码、同步子模块与编译命令源码一定要带着子模块一起拉。QGC 依赖 MAVLink 库等子模块只克隆主仓不拉子模块后续生成代码时必报错。推荐的做法是第一次克隆就直接带子模块# 从官方仓库或团队内部分发渠道获取源码务必携带子模块 git clone --recurse-submodules 你的QGC源码仓库地址 cd qgroundcontrol # 如果克隆时没带子模块执行下面这条补拉 git submodule update --init --recursive # 使用传统 qmake 方式构建 mkdir -p build cd build qmake ../qgroundcontrol.pro make -j4参数说明--recurse-submodules让 git 在克隆主仓时一并拉取所有子模块省去后续手动同步qmake ../qgroundcontrol.pro是让 Qt 的构建系统解析工程文件并生成 Makefilemake -j4表示用 4 个并行任务编译机器内存不够时可以降到-j2。第一次编译会消耗比较久的时间源码体积大、QML 资源多半小时到一小时都是正常的看到滚动刷屏不要慌。3.3 首次编译的三个高频报错与对策第一个高频报错是“缺少 Qt 头文件”现象是 make 打印 fatal error 然后找不到某个 QtXXX 头文件。原因基本是装了精简版 Qt 或者没装对应开发模块解决方法是回到安装器补全开发套件而不是手动下载头文件硬塞。第二个是子模块相关报错比如找不到 mavlink 头文件这通常是克隆时漏了子模块回到仓库根目录执行一次git submodule update --init --recursive就能解决。第三个比较隐蔽发生在 MAVLink 头文件生成环节报错指向 Python 环境。QGC 构建过程会用 Python 脚本预先生成部分协议代码系统缺 Python 或缺相关库就会失败。解决方法是补装 Python 3 并安装依赖库然后在命令行里确认python3 --version可执行。每次遇到这类报错先把完整报错信息读一遍再搜很多新人栽在只看最后一行实际根因在中间几行。编译这东西没有太多玄学报错信息基本都指到了该改的位置。4. 第一个实战把一个自定义状态面板挂进QGC主界面4.1 选挂载点主界面布局与自定义组件的插入位置主界面是一个由多个 QML 组件拼起来的布局树侧边栏、工具栏、仪表区各有各的容器。二开最常见的两种做法一种是在侧边栏区域预留一个容器把自定义面板放进去好处是常驻可见坏处是改动原有布局结构出错时影响面大另一种是注册一个新的工具按钮点击后以对话框或抽屉方式弹出面板好处是不碰原有界面出问题时只删掉自己加的东西就恢复原样。对第一次做二开的人来说我强烈建议走第二种。先做一个能独立弹出的小面板验证“QML 能加载、数据能绑定”再考虑常驻布局。这个顺序能帮你把“我的代码有问题”和“主布局被破坏了”这两个问题分开排查。挂载点确定之后下一步就是真正把面板画出来这不需要改动 C 侧第一步可以先做纯静态版本。4.2 用QML把状态面板画出来下面这个面板示例用来展示自定义载荷的在线状态和电池电压纯 QML 实现不依赖复杂控件放在哪个 QGC 版本上都能跑// PayloadStatusPanel.qml // 自定义载荷状态面板显示在线状态与电池电压 import QtQuick 2.15 import QtQuick.Controls 2.15 Item { id: payloadPanel width: 220 height: 140 // 对外暴露属性由父级组件或 C 侧赋值绑定 property real batteryVolt: 0.0 property bool payloadOk: false Rectangle { anchors.fill: parent radius: 6 color: payloadOk ? #f4f7f4 : #fdecea border.color: #bbbbbb } Column { anchors { left: parent.left; top: parent.top; margins: 10 } spacing: 6 Text { text: 载荷状态 font.pixelSize: 16 font.bold: true } Text { text: 电压 batteryVolt.toFixed(1) V } Text { text: 链路 (payloadOk ? 正常 : 异常) } } }逻辑说明面板本身是一个 Item尺寸固定为 220 乘 140背景 Rectangle 的颜色会根据payloadOk切换在线是浅灰绿离线是浅红用于快速目检Column 里放了三个 Text分别显示标题、电压和链路状态。batteryVolt和payloadOk是暴露出来的属性父级组件或 C 逻辑可以直接赋值面板自身不关心数据来源。这个设计符合 QGC 界面层只做展示、数据由上层注入的习惯。4.3 从C把实时数据接进来QML 面板本身不会产生数据真正的数据来源是飞控或载荷设备。二开里常见的做法是写一个 C 管理类持有实时状态通过信号槽把数据推给 QML。下面这个 PayloadStateManager 是一个最小可用的状态容器// PayloadStateManager.h #pragma once #include QObject class PayloadStateManager : public QObject { Q_OBJECT public: explicit PayloadStateManager(QObject* parent nullptr) : QObject(parent), _batteryVolt(0.0), _payloadOk(false) { } Q_INVOKABLE double batteryVolt() const { return _batteryVolt; } Q_INVOKABLE bool payloadOk() const { return _payloadOk; } public slots: void updateState(double volt, bool ok) { _batteryVolt volt; _payloadOk ok; emit stateChanged(); } signals: void stateChanged(); private: double _batteryVolt; bool _payloadOk; };这个类暴露了两个只读接口给 QML更新数据走updateState这个槽。要让 QML 能识别它需要在启动阶段注册类型// main.cpp 或 toolBox 初始化阶段 qmlRegisterTypePayloadStateManager(QGroundControl, 1, 0, PayloadStateManager);注册之后在 QML 里就能用PayloadStateManager这个类型创建实例把它的属性绑定到面板上。实践中的常见做法是收到 MAVLink 消息后在接收处理分支里调用updateStateQML 面板立即刷新。这里的关键在于 C 只管更新状态UI 只做展示中间靠信号stateChanged触发刷新这是 Qt/QML 混合开发的经典套路。5. 二次开发避坑指南最容易翻车的五个位置5.1 QML改了没反应资源缓存和路径不一致现象修改了一个 QML 文件重新编译启动界面还是老样子仿佛改了个寂寞。原因通常是两个一是 QML 文件被打到了 Qt 的资源系统里增量编译时资源可能没有重新打包二是改错了文件编辑器里打开的是工作副本而工程资源加载的是另一个路径下的同名文件。解决方式先搜索确认你改的文件确实在加载路径里然后清空 build 目录重新 qmake 再编译。别第一时间怀疑缓存玄学先排除路径问题这一条我说过太多次几乎占了界面改动问题的六成。5.2 自定义面板不显示先怀疑尺寸和挂载点现象代码没有报错运行起来界面却找不到自己加的面板。原因排下来最常见的是宽高为 0尤其当面板被塞进 Column 或 Row 时如果没有设置Layout.fillWidth或明确宽度组件可能被压缩成不可见的一根线其次是挂载点本身不在当前显示的页面中比如放进了某个默认不打开的 Tab 页里。解决办法是先给面板一个固定宽高和一条高亮边框背景让它强制可见再逐步调整挂载位置。这个排错方法能帮你快速把“面板代码的问题”和“布局容器的问题”分开。5.3 自定义MAVLink消息收不到dialect没对上现象飞控端明明在发自定义消息地面站里绑定的槽函数一直不触发。原因九成出在协议方言不一致上QGC 编译时用的是某一组 MAVLink 方言生成的头文件你的自定义消息 ID 不在其中消息进入分发模块后没有对应的处理分支直接被丢弃。解决步骤是先抓包确认消息确实到达了地面站端口这一步我习惯看 UDP 14550 端口上的报文再检查生成头文件时用的 XML 里是否包含了你的消息定义最后在接收分发处补上处理 case。顺序一定不能反先链路后代码否则容易白改一通。5.4 中文显示乱码Windows下的编码问题现象界面或代码注释里的中文变成乱码和方块Linux 上少见Windows 上经常出现。原因是 MSVC 编译器对源文件编码的处理和 UTF-8 不一致Qt 5 的字符串又默认按 UTF-8 解析两边一冲突就乱。解决办法是给 MSVC 编译选项加上/utf-8同时把 QML 文件统一保存为 UTF-8 格式字符串内容习惯性套上tr()或QStringLiteral。这个坑不难绕但第一次踩到会怀疑是自己代码写错实际上就是编码约定问题属于二开团队的默认必修课。5.5 版本漂移升级后老代码编译不过现象QGC 源码从旧版本切到新版本之前的二开代码报出一堆类型错误和找不到成员。原因很直接QGC 本身迭代很快C 类名、QML 模块路径、接口签名都可能变化二次开发代码还没来得及同步就是这副样子。解决方式分两层团队层面建立一个二开分支并锁定基线版本不随便追官方 release个人层面每次改动记录清单升级前先跑一遍全量编译看哪些是纯 API 改名、哪些是结构性变化。这个坑我栽过不止一次后来养成了“版本锁死 变更清单”的习惯二开工程量越大这两个习惯越值钱。6. 进阶让自定义MAVLink消息从定义到上机验证走通走到这一步你已经能在 QGC 里加自己的界面了。真正的二开项目通常还需要自定义消息把载荷数据从飞控链路里捞出来。常见做法是新增一个自定义 dialect XML在里面描述消息格式再通过 MAVLink 工具链重新生成 C 头文件让 QGC 的协议层认识这条消息。!-- 自定义消息示例仅演示结构ID 取自定义区间 -- message id42000 namePAYLOAD_STATE description自定义载荷状态上报/description field typeuint8_t namestate0离线 1在线/field field typefloat namebattery_volt载荷电池电压/field /message生成头文件后把新的协议文件放回 MAVLink 库目录重新编译工程然后在消息分发处为PAYLOAD_STATE增加一个处理分支调用你自定义状态管理类的updateState接口。验证时先开模拟器或直接连飞控用抓包工具确认报文抵达再看程序日志里是否打印了解析结果。整个链路里最容易出错的是生成头文件的 XML 和你实际发送的字节结构不一致代码没问题数据也对不上。二开做到这个深度真正考验你的已经不是某个具体类怎么调而是能不能把“链路、协议、界面、状态管理”这几层串起来排查问题。我自己的习惯是每改一步都保留一个能回滚的提交点尤其涉及自定义消息时前后端都留好版本对应关系否则线上跑半个小时后客户端闪退连回滚都找不到后悔药吃。希望这篇教程能帮你少走几段弯路也把你从只会改界面的入门段位往上推一截。本文还有配套的精品资源点击获取

相关新闻

BrowserSkill:Chrome 浏览器原生 CLI 编程工具

BrowserSkill:Chrome 浏览器原生 CLI 编程工具

1. 项目概述:BrowserSkill 是什么,它解决的不是“浏览器自动化”,而是开发者工作流的物理层重构BrowserSkill(常缩写为 bsk)不是又一个 Selenium 封装库,也不是 Puppeteer 的 CLI 包装器。我第一次在内部工…

2026/10/18 16:53:47 阅读更多 →
智能风控AI决策引擎架构实战:从0到1搭建实时拦截系统

智能风控AI决策引擎架构实战:从0到1搭建实时拦截系统

智能风控AI决策引擎的架构实战:拆解一个实时拦截系统的设计与落地做风控架构这行久了,你会发现一个很残酷的现实:市面上讲风控模型算法的文章车载斗量,但真正把“决策引擎”从0到1搭起来、还能稳定扛住高并发和业务迭代的实战经验…

2026/10/18 16:53:47 阅读更多 →
飞控计算机硬实时操作系统核心机制与工程选型全解析

飞控计算机硬实时操作系统核心机制与工程选型全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 16:53:47 阅读更多 →

最新新闻

OpenHarmony奖品详情页开发:从数据模型到状态管理全解析

OpenHarmony奖品详情页开发:从数据模型到状态管理全解析

我先把前面五篇的进度简单对一下,免得有读者是从这一篇才跟上来的:目前这个幸运大转盘项目已经完成了转盘主界面、抽奖动画、奖品管理、中奖记录和中奖弹窗这几个核心模块。这一篇要补上的奖品详情,其实是把"用户点开某个奖品图标之后看…

2026/10/18 21:47:12 阅读更多 →
React Native键盘避让在OpenHarmony上的适配实践

React Native键盘避让在OpenHarmony上的适配实践

1. 为什么键盘避让在 OpenHarmony 上会成为问题?做跨端开发的人最头疼的一类问题,莫过于软键盘弹起来之后,输入框被死死挡住。在 React Native 项目里,大家的第一反应就是用官方提供的 KeyboardAvoidingView 组件;可一…

2026/10/18 21:47:12 阅读更多 →
量化投研遇上大模型:研究记忆的风险边界与工程实践

量化投研遇上大模型:研究记忆的风险边界与工程实践

1. 当量化投研遇上大模型:一个正在被重新定义的边界做量化投研的人这两年应该都有一个共同的感受:数据获取的门槛在肉眼可见地降低,但真正拉开差距的东西,反而越来越模糊了。以前我们拼的是谁的因子库更全、谁的行情清洗更干净、谁…

2026/10/18 21:47:12 阅读更多 →
PL/SQL Developer导出表全攻略:从选项拆解到踩坑排查

PL/SQL Developer导出表全攻略:从选项拆解到踩坑排查

很多PL/SQL Developer用户都会卡在“导出表”这一步。明明菜单里有个Export Tables,可真点进去又不知道该勾哪些选项,导出来的SQL文件拿去别的库一执行就报错,或者数据缺了一半。这问题特别常见,尤其在Oracle DBA和开发人员之间来…

2026/10/18 21:47:12 阅读更多 →
降低AIGC率有哪些方法?AIGC检测里摘要、综述AI率高分别怎么改!

降低AIGC率有哪些方法?AIGC检测里摘要、综述AI率高分别怎么改!

降低AIGC率有哪些方法?AIGC检测里摘要、综述AI率高分别怎么改! 拿到知网AIGC检测报告,你可能会看到这样的情况:全文AI率不算夸张,可摘要那一页的AIGC疑似度超过50%,第二章文献综述连着三四页大段标红&…

2026/10/18 21:47:12 阅读更多 →
Select模型详解:Windows Socket TCP服务端实现与优化

Select模型详解:Windows Socket TCP服务端实现与优化

聊到Windows下的网络编程,“五种I/O模型”绝对是一个绕不开的话题。很多人在面试或者做项目的时候第一次接触这个概念,当场就被Select、WSAAsyncSelect、WSAEventSelect、重叠I/O、完成端口这一串名词搞懵了。说实话,这五个模型确实容易让人望…

2026/10/18 21:46:11 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →