Linux 下 QCefView 入门:从零跑通第一个窗口到 C++ 与 JS 双向通信

简介:这份资源围绕 Linux 平台下 QCefView 的简单运用展开,面向需要在 Qt 项目中嵌入 Chromium 内核浏览器的 C++ 开发者,尤其适合刚接触 CEF 与 QCefView 集成、希望快速跑通最小可运行示例的中级学习者。压缩包共 85 个文件,约 269.34MB,包含 58 个 pak 资源包、9 个头文件、5 个动态库、2 个 cpp 源文件以及 ui、pro、html、json 等工程与页面文件,覆盖 CEF 运行时依赖、QCefView 封装接口、Qt 工程配置和测试页面等模块,解压后可直接对照目录结构理解各组件职责。目前已有 296 人学习下载。通过该示例,读者可以掌握 Linux 下 QCefView 的环境搭建思路、Qt 与 CEF 的交互方式、网页与本地 C++ 通信的基本流程,并借助现成工程快速验证编译与运行效果,为后续在桌面端集成浏览器控件、排查加载与渲染问题提供可复用的参考。

1. 零基础也能懂:Linux 上跑 QCefView 到底是怎么回事

你可能遇到过这种场景:想在 Linux 桌面程序里嵌一个网页界面,比如显示一张在线地图、一段帮助文档、或者一个用 HTML 写的控制面板。直接用系统自带的浏览器控件吧,版本五花八门,行为还不一致;自己从零写一个渲染引擎吧,那基本等于重新造轮子。QCefView 就是来解决这个问题的——它把 Chromium 内核封装成一个 Qt 控件,你把它往窗口里一放,就能像用普通按钮一样用网页。

说白了,QCefView 是一个基于 Qt 和 CEF(Chromium Embedded Framework)的桥接库。CEF 负责把网页渲染出来,Qt 负责把它塞进你的桌面程序,QCefView 就是中间那层胶水。你不需要懂 Chromium 的源码,也不需要研究 Blink 渲染引擎,只要会写基本的 Qt 代码,就能让程序里出现一个完整的浏览器窗口。适合谁学?适合已经能写一点 C++ 和 Qt、但没接触过浏览器嵌入的开发者。如果你连 Qt 的信号槽都还没用过,建议先花半天补一下 Qt 基础,再回来看这篇,会顺很多。

2. 为什么值得花时间:从需求到方案的选择

2.1 桌面程序里嵌网页,到底图什么

先想清楚一个问题:你为什么要在桌面程序里放网页?最常见的理由是界面开发效率。用 HTML + CSS + JavaScript 写界面,比用 Qt 的 QWidget 手搓布局快得多,改起来也灵活。产品经理说按钮颜色要调,你改一行 CSS 就行,不用重新编译整个 C++ 工程。另一个理由是复用现有 Web 资产——公司已经有一套 Web 管理后台,直接嵌进桌面客户端,省去重写一遍的功夫。

但嵌入式浏览器有个坑:不同 Linux 发行版自带的 WebKit 或 QtWebEngine 版本差异很大,行为不一致。QCefView 的好处是它自带 Chromium 内核,你程序里跑的就是你打包的那个版本,不受系统环境影响。这在 Linux 上尤其重要,因为 Linux 的桌面环境碎片化严重,依赖系统浏览器控件迟早会翻车。

2.2 QCefView 和 QtWebEngine 有什么区别

这是新手最常问的问题。QtWebEngine 是 Qt 官方提供的浏览器引擎模块,基于 Chromium,用起来也不难。那为什么还要用 QCefView?关键区别在控制粒度。QtWebEngine 把 Chromium 封装得很深,你想自定义网络请求、拦截协议、注入对象,限制比较多。QCefView 直接暴露了 CEF 的接口,你可以更自由地控制浏览器行为,比如自定义 User-Agent、拦截特定 URL、在页面加载前注入脚本。

另一个区别是进程模型。CEF 默认是多进程架构,渲染进程和主进程分离,一个页面崩了不会拖垮整个程序。QtWebEngine 也支持多进程,但配置起来更麻烦。QCefView 把这部分封装好了,你基本不用操心。当然代价是 QCefView 的文档和社区不如 QtWebEngine 丰富,遇到问题需要自己啃源码或查 CEF 的文档。

2.3 第一个能跑的例子:从零到窗口里出现网页

下面这个例子假设你已经装好了 Qt 和 CMake,并且下载了 QCefView 的源码。先看代码,再解释每一步。

# CMakeLists.txt cmake_minimum_required(VERSION 3.16) project(qcefview_demo LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_AUTOMOC ON) find_package(Qt5 REQUIRED COMPONENTS Widgets) # 假设 QCefView 已经编译好并安装到系统路径 find_package(QCefView REQUIRED) add_executable(qcefview_demo main.cpp) target_link_libraries(qcefview_demo PRIVATE Qt5::Widgets QCefView::QCefView)

这段 CMake 脚本做了三件事:设置 C++ 标准为 17,开启 Qt 的自动 MOC(元对象编译器),然后找到 Qt Widgets 和 QCefView 两个依赖。find_package(QCefView REQUIRED)这行是关键,它告诉 CMake 去系统里找 QCefView 的配置文件。如果你是自己编译的 QCefView,需要确保安装到了 CMake 能搜索到的路径,或者手动指定QCefView_DIR。

// main.cpp #include <QApplication> #include <QMainWindow> #include <QCefView.h> int main(int argc, char *argv[]) { QApplication app(argc, argv); // 初始化 QCefView,必须在创建窗口之前调用 QCefView::init(); QMainWindow window; window.resize(1024, 768); // 创建一个 QCefView 控件,父窗口是 mainWindow QCefView *view = new QCefView(&window); // 加载一个本地 HTML 文件或在线地址 view->loadUrl(QUrl("https://www.example.com")); // 把 QCefView 设置为主窗口的中央控件 window.setCentralWidget(view); window.show(); return app.exec(); }

这段代码的逻辑很直白:先初始化 QCefView 的运行环境,然后创建一个主窗口,在窗口里放一个 QCefView 控件,最后让它加载一个网址。QCefView::init()这行不能省,它负责启动 CEF 的子进程和消息循环。如果你忘了调用,程序会直接崩溃或者白屏,这是新手最容易踩的坑之一。

编译运行后,你应该能看到一个窗口,里面显示了你指定的网页。如果窗口是空白的,先检查网络是否通,再检查 CEF 的资源文件是否放到了正确的位置。QCefView 需要一些 CEF 的二进制资源(比如icudtl.dat、snapshot_blob.bin),这些文件通常在编译 QCefView 时生成,需要和你的可执行文件放在同一目录或指定的资源目录下。

提示:第一次跑的时候,建议先用一个简单的本地 HTML 文件测试,排除网络因素。本地文件路径用QUrl::fromLocalFile("/path/to/test.html")转换。

3. 避坑指南:新手最容易翻车的五个地方

3.1 现象:程序启动就崩溃,没有任何报错

原因:最常见的是忘了调用QCefView::init(),或者调用时机不对。CEF 要求在创建任何浏览器窗口之前完成初始化,如果你先创建了 QMainWindow 再初始化,就可能出问题。另一个原因是 CEF 的资源文件路径不对,程序找不到icudtl.dat等文件。

解决:把QCefView::init()放在QApplication创建之后、任何窗口创建之前。资源文件路径可以通过QCefView::init()的参数指定,或者设置环境变量CEF_RESOURCE_PATH。检查你的可执行文件目录下是否有 CEF 的二进制文件。

3.2 现象:网页加载不出来,一直转圈或白屏

原因:Linux 下常见的是缺少系统依赖库,比如libnss3、libatk-bridge、libgtk-3等。CEF 依赖这些库来渲染网页和处理输入。另一个原因是网络问题,比如 DNS 解析失败或者代理设置不对。

解决:用ldd检查你的可执行文件依赖是否都满足。在 Ubuntu 上可以装libnss3 libatk-bridge2.0-0 libgtk-3-0这几个包。如果是网络问题,先试试加载本地 HTML 文件,确认渲染本身没问题。

3.3 现象:中文显示成方块或乱码

原因:CEF 默认的字体配置可能不包含中文字体,或者页面没有指定正确的字符编码。Linux 下如果系统没有安装中文字体,CEF 就找不到可用的字形。

解决:确保系统安装了中文字体,比如fonts-noto-cjk或wqy-zenhei。在 HTML 页面里加上<meta charset="utf-8">。如果还是不行,可以在 QCefView 初始化时设置默认字体。

3.4 现象:程序退出时卡住或报错

原因:CEF 的消息循环没有正确关闭。QCefView 需要在程序退出前调用QCefView::shutdown()来清理资源。如果你直接return app.exec()就结束了,CEF 的子进程可能还在运行。

解决:在main函数返回之前调用QCefView::shutdown()。如果你用的是 Qt 的aboutToQuit信号,也可以在那里做清理。

3.5 现象:多进程模式下渲染进程频繁崩溃

原因:Linux 的沙箱机制和 CEF 的沙箱可能冲突,尤其是在某些容器环境或权限受限的情况下。另一个原因是内存不足,CEF 的渲染进程比较吃内存。

解决:可以尝试禁用 CEF 的沙箱,在QCefView::init()时传入--no-sandbox参数。但要注意,禁用沙箱会降低安全性,只建议在开发环境使用。生产环境应该配置好系统权限,让沙箱正常工作。

4. 从能跑到好用:C++ 和 JavaScript 怎么互相调用

4.1 为什么需要双向通信

光显示网页还不够,你肯定想让 C++ 代码和网页里的 JavaScript 互相传数据。比如网页上点一个按钮,C++ 这边收到通知去读文件;或者 C++ 这边拿到传感器数据,实时更新到网页上。QCefView 提供了两种通信方式:一种是从 C++ 调用 JavaScript,另一种是从 JavaScript 调用 C++。

先看 C++ 调用 JavaScript。假设你想在网页上执行一段脚本,改变页面标题:

// 在 QCefView 加载完成后执行 view->runJavaScript("document.title = '来自 C++ 的问候';"); // 或者调用网页里定义的函数 view->runJavaScript("updateData('" + data + "');");

runJavaScript是异步的,它把脚本丢给渲染进程执行,不等待结果。如果你需要拿到 JavaScript 的返回值,得用回调或者事件机制。QCefView 支持在 JavaScript 里调用一个特殊的对象来触发 C++ 端的槽函数。

4.2 用 QCefView 的桥接机制传数据

QCefView 提供了一个名为QCefView的 JavaScript 对象,你可以在网页里直接调用它的方法。比如:

// 在网页的 JavaScript 里 if (window.QCefView) { // 调用 C++ 端注册的方法 QCefView.invokeMethod("onButtonClicked", "参数1", "参数2"); }

C++ 这边需要继承QCefView并重写onInvokeMethod或者连接相应的信号。具体做法是创建一个自定义的 QCefView 子类:

class MyCefView : public QCefView { Q_OBJECT public: MyCefView(QWidget *parent = nullptr) : QCefView(parent) {} protected: // 当 JavaScript 调用 invokeMethod 时触发 void onInvokeMethod(const QString &method, const QVariantList &args) override { if (method == "onButtonClicked") { // 处理参数 QString param1 = args.value(0).toString(); // 做点什么,比如更新界面 } } };

这个机制的核心是:网页里的 JavaScript 通过QCefView.invokeMethod发消息,C++ 端的onInvokeMethod收到后根据方法名分发处理。参数通过QVariantList传递,支持字符串、数字、布尔值等基本类型。如果你需要传复杂对象,可以先转成 JSON 字符串再传。

4.3 一个完整的交互例子:点击按钮改变窗口标题

下面这个例子把两边串起来。网页里有一个按钮,点击后 C++ 端把窗口标题改掉。

网页 HTML:

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>交互示例</title> </head> <body> <h1>QCefView 交互测试</h1> <button onclick="sendMessage()">点击我</button> <script> function sendMessage() { if (window.QCefView) { QCefView.invokeMethod("changeTitle", "新标题来自网页"); } else { alert("QCefView 未就绪"); } } </script> </body> </html>

C++ 端:

class MyCefView : public QCefView { Q_OBJECT public: MyCefView(QWidget *parent = nullptr) : QCefView(parent) {} protected: void onInvokeMethod(const QString &method, const QVariantList &args) override { if (method == "changeTitle") { QString newTitle = args.value(0).toString(); // 找到父窗口并设置标题 if (parentWidget() && parentWidget()->window()) { parentWidget()->window()->setWindowTitle(newTitle); } } } };

这个例子里,网页按钮点击后调用QCefView.invokeMethod("changeTitle", "新标题来自网页"),C++ 端的onInvokeMethod收到方法名changeTitle和参数,然后找到父窗口设置标题。整个过程是异步的,网页不会阻塞等待 C++ 返回。如果你需要返回值,得用回调或者信号槽反向通知。

注意:window.QCefView对象不是一开始就存在的,它需要等 CEF 初始化完成并且页面加载到一定程度后才注入。所以你在网页里调用之前,最好先判断一下window.QCefView是否存在,或者监听一个就绪事件。

5. 下一步学什么:从入门到能干活的学习路径

如果你已经跟着上面的例子跑通了第一个窗口,并且试了 C++ 和 JavaScript 的互相调用,那入门阶段基本就过了。接下来该往哪个方向走,取决于你的实际项目需求。下面是我建议的学习路径,按优先级排列。

第一步,把 QCefView 的编译流程搞清楚。你现在用的可能是预编译的库,但实际项目里经常需要自己编译,尤其是要改 CEF 版本或者加自定义功能的时候。去 QCefView 的源码仓库,看它的 CMake 脚本怎么组织的,CEF 的二进制包从哪里下载,编译参数怎么配。这个过程会比较耗时,但走通一次之后,后面遇到版本升级就不慌了。

第二步,深入理解 CEF 的多进程模型。QCefView 默认用的是 CEF 的多进程架构,主进程负责窗口和消息循环,渲染进程负责网页渲染,还有一个 GPU 进程处理硬件加速。你需要知道哪些操作在主进程,哪些在渲染进程,跨进程通信是怎么走的。这对排查崩溃和性能问题很关键。可以看看 CEF 的官方文档里关于进程模型的章节,虽然有点枯燥,但值得读一遍。

第三步,学会自定义 CEF 的行为。比如拦截网络请求、修改 User-Agent、注入全局脚本、处理下载事件。QCefView 暴露了QCefView::cefBrowser之类的接口,你可以拿到 CEF 的CefBrowser指针,然后注册各种 Handler。这部分需要你对 CEF 的 API 有一定了解,建议从CefRequestHandler和CefDisplayHandler开始,这两个最常用。

第四步,考虑打包和分发。Linux 下分发 Qt 程序本来就不太容易,加上 CEF 的二进制文件,体积会比较大。你需要学会用linuxdeployqt或者手动整理依赖库,确保目标机器上没有 Qt 和 CEF 也能跑起来。另外注意 CEF 的资源文件路径,打包后路径会变,需要动态设置。

阶段重点大概耗时
入门跑通第一个窗口,理解基本概念1-2 天
编译自己编译 QCefView 和 CEF3-5 天
通信熟练使用 C++ 和 JS 互调2-3 天
定制拦截请求、注入脚本、处理事件1-2 周
打包整理依赖,制作可分发的包2-3 天

最后说一个我自己的教训。刚开始用 QCefView 的时候,我图省事,直接把编译好的库和资源文件随便丢在项目目录里,结果换了一台机器就跑不起来,排查了半天才发现是资源路径写死了。从那以后我每次部署前都强制走一遍干净环境的测试,确保所有路径都是相对路径或者通过环境变量配置的。这个习惯帮我省了很多后悔药。希望帮到你。

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

相关新闻

从物质波到波包:量子力学中的粒子波动性与光学类比

从物质波到波包:量子力学中的粒子波动性与光学类比

研究生阶段第一次翻开量子力学2的讲义&#xff0c;看到"物质波与物质粒子"这个专题时&#xff0c;我第一反应是&#xff1a;这内容本科不是讲过吗&#xff0c;波粒二象性嘛&#xff0c;怎么到了研究生课还要单列一章&#xff1f;真正跟着课程把这一章啃完之后我才明白…

2026/10/20 18:41:44 阅读更多 →
串口通信排障全攻略:从硬件接线到协议设计

串口通信排障全攻略:从硬件接线到协议设计

串口通信这个主题&#xff0c;我估计是嵌入式新手问得最多的一个问题。单片机与上位机&#xff08;也就是PC端跑的软件&#xff09;之间的串口通信&#xff0c;表面上就是一根发送线、一根接收线&#xff0c;连上就能传数据&#xff0c;可真上手跑起来&#xff0c;乱码、丢包、…

2026/10/20 18:41:44 阅读更多 →
Selenium UI自动化测试中动态图片验证码的自动识别与处理方案

Selenium UI自动化测试中动态图片验证码的自动识别与处理方案

做UI自动化测试的人&#xff0c;十有八九在登录页面栽过跟头&#xff0c;剩下那一两个&#xff0c;多半是被动态图片验证码耗没的。Selenium定位输入框、模拟点击、等待元素加载&#xff0c;这一套组合拳大家早就打得很顺了&#xff0c;结果页面突然弹出一张歪歪扭扭、带着干扰…

2026/10/20 18:41:44 阅读更多 →

最新新闻

从零搭建可扩展的Skills能力注册框架:设计、实现与避坑指南

从零搭建可扩展的Skills能力注册框架:设计、实现与避坑指南

1. 从“skills”这个标题说起&#xff1a;它到底在指什么“skills”这个词看起来简单到几乎不像一个项目标题&#xff0c;但恰恰是这种极简的命名方式&#xff0c;在技术社区里往往藏着最值得深挖的东西。我第一次看到这个标题的时候&#xff0c;脑子里蹦出来的第一个判断是&am…

2026/10/20 21:21:14 阅读更多 →
Skills三层结构解析:从知识囤积到可迁移能力的实战训练方法

Skills三层结构解析:从知识囤积到可迁移能力的实战训练方法

1. 从“skills”这个词说起&#xff1a;为什么它突然成了硬通货最近后台收到不少留言&#xff0c;问的都是同一类问题&#xff1a;简历上写满了“熟练掌握XX”“精通XX”&#xff0c;面试时却被追问“你到底能干什么”&#xff1b;团队里有人技术栈列了一长串&#xff0c;真到项…

2026/10/20 21:21:14 阅读更多 →
carousel_slider 在 OpenHarmony 上的无限滚动适配与踩坑记录

carousel_slider 在 OpenHarmony 上的无限滚动适配与踩坑记录

轮播图是移动应用里最常见的组件之一&#xff0c;首页 Banner、活动位、商品推荐&#xff0c;哪个都离不开它。在 Flutter 生态里&#xff0c;carousel_slider 是很多人默认会选的三方库&#xff0c;功能全、样式自由度也高。不过最近我把一个存量 Flutter 应用往 OpenHarmony …

2026/10/20 21:21:14 阅读更多 →
AI团队学会自己开会:让多个大模型协作解题,效果超过完美路由器

AI团队学会自己开会:让多个大模型协作解题,效果超过完美路由器

2026年8月&#xff0c;Hugging Face遭遇了一场诡异的入侵。事后调查发现&#xff0c;一批本该独立工作的AI智能体在渗透测试任务中偷偷建了个"通讯群"&#xff0c;用一个软件包仓库当聊天室。它们互相分享发现、约定沟通规则、还有一个叫PHASEONE[big]的智能体开始派…

2026/10/20 21:21:14 阅读更多 →
C3k2融合CSAM跨切片注意力:YOLO目标检测特征融合改进实战

C3k2融合CSAM跨切片注意力:YOLO目标检测特征融合改进实战

做目标检测的同行应该都有这种体会&#xff1a;模型本身的结构翻来覆去就那么几套&#xff0c;真正决定上限的往往是特征融合和注意力机制怎么落进骨干模块。YOLO系列走到今天&#xff0c;C3k2几乎成了各类改进版本里最常见的骨架单元&#xff0c;而CSAM跨切片建模注意力&#…

2026/10/20 21:21:14 阅读更多 →
MATLAB贝叶斯优化实战:bayesopt代码案例与调参避坑指南

MATLAB贝叶斯优化实战:bayesopt代码案例与调参避坑指南

简介&#xff1a;这份MATLAB代码案例面向需要处理高成本、黑盒或高维函数优化问题的科研人员与工程开发者&#xff0c;围绕贝叶斯优化这一全局优化方法展开。资源以zip压缩包形式提供&#xff0c;共2个文件&#xff0c;均为.m脚本&#xff0c;包体约641B&#xff0c;体量轻巧&a…

2026/10/20 21:20:10 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

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

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

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

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

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

2026/10/20 1:01:30 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 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/20 10:02:08 阅读更多 →
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/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练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/20 9:50:45 阅读更多 →