Ubuntu20.04 下 Qt 项目使用 SDL2 最小原型教程
Ubuntu20.04 下 Qt 项目使用 SDL2 最小原型教程本文档用于指导初学者在 Ubuntu20.04 虚拟机中将已经安装好的 SDL2 接入 Qt 项目并完成一个最小可运行原型。本教程适合以下场景已经在 Ubuntu20.04 中安装 SDL2终端执行sdl2-config --version可以看到 SDL2 版本希望在 Qt 项目中调用 SDL2后续计划结合 FFmpeg 做视频显示、视频监控、播放器等项目。1. 当前环境说明假设当前工作目录如下szdubuntu:~/Desktop/jiankong$lsffmpeg LICENSE mediamtx mediamtx_v1.8.4_linux_amd64.tar.gz mediamtx.yml mpp qt-ffmpeg SDL2-2.30.3 SDL2-2.30.3.tar.gz其中目录或文件作用SDL2-2.30.3SDL2 源码目录SDL2-2.30.3.tar.gzSDL2 源码压缩包ffmpegFFmpeg 相关源码或库mediamtx流媒体服务器qt-ffmpegQt 和 FFmpeg 相关工程mppRockchip MPP 硬件编解码相关内容当前已经验证 SDL2 安装成功sdl2-config--version输出2.30.3这说明 SDL2 已经安装好并且当前系统能找到sdl2-config。2. Qt 和 SDL2 的关系在实际视频项目中Qt 和 SDL2 可以这样分工模块主要作用Qt做主界面、按钮、菜单、状态显示、配置界面SDL2创建视频显示窗口、渲染图像、播放音频、处理底层输入事件FFmpeg解封装、解码、格式转换MediaMTXRTSP、RTMP、HLS 等流媒体服务MPPRK3588 等平台的硬件编解码一个典型的视频显示流程如下网络视频流 / 本地视频文件 ↓ FFmpeg 解封装 ↓ FFmpeg 解码得到视频帧 ↓ 像素格式转换例如 YUV420P / RGB ↓ SDL2 创建纹理并显示画面 ↓ Qt 提供界面控制本教程先不接 FFmpeg只完成最小原型Qt 主窗口 ↓ 点击按钮 ↓ 打开 SDL2 窗口 ↓ SDL2 渲染一个纯色画面只要这个原型能跑通就说明 Qt 项目已经可以正确使用 SDL2。3. 安装 Qt 开发工具如果系统中还没有 Qt 开发环境先安装sudoaptupdatesudoaptinstall-yqt5-qmake qtbase5-dev qtbase5-dev-tools安装完成后检查qmake-v正常会看到类似输出QMake version3.1Using Qt version5.x.x如果能看到 Qt 版本说明 Qt 编译环境正常。4. 检查 SDL2 是否可用执行sdl2-config--version正常输出2.30.3再执行pkg-config--modversionsdl2正常输出2.30.3查看 SDL2 的编译参数pkg-config--cflags--libssdl2可能输出类似-I/usr/local/include/SDL2-D_REENTRANT-L/usr/local/lib-lSDL2这些参数表示参数含义-I/usr/local/include/SDL2SDL2 头文件路径-D_REENTRANT启用线程安全相关宏-L/usr/local/libSDL2 库文件路径-lSDL2链接 SDL2 动态库5. 创建最小 Qt SDL2 工程进入工作目录cd~/Desktop/jiankong创建工程目录mkdirqt_sdl_mincdqt_sdl_min创建两个文件touchqt_sdl_min.pro main.cpp工程目录结构如下qt_sdl_min/ ├── main.cpp └── qt_sdl_min.pro6. 编写 Qt 工程文件.pro编辑工程文件vimqt_sdl_min.pro写入以下内容QT core gui widgets CONFIG c11 CONFIG link_pkgconfig PKGCONFIG sdl2 SOURCES main.cpp关键说明QT core gui widgets表示当前项目使用 Qt 的核心模块、图形界面模块和控件模块。CONFIG c11表示使用 C11 标准。CONFIG link_pkgconfig表示让 qmake 使用pkg-config来管理第三方库。PKGCONFIG sdl2表示项目需要链接 SDL2。SOURCES main.cpp表示当前项目的源码文件是main.cpp。7. 编写main.cpp编辑源码文件vimmain.cpp写入以下完整代码#includeQApplication#includeQWidget#includeQPushButton#includeQVBoxLayout#includeQTimer#includeQMessageBox#includeSDL.hintmain(intargc,char*argv[]){QApplicationapp(argc,argv);QWidget qtWindow;qtWindow.setWindowTitle(Qt SDL2 Minimal Demo);qtWindow.resize(300,120);QPushButton*btnOpenSDLnewQPushButton(打开 SDL2 窗口);QPushButton*btnCloseSDLnewQPushButton(关闭 SDL2 窗口);QVBoxLayout*layoutnewQVBoxLayout;layout-addWidget(btnOpenSDL);layout-addWidget(btnCloseSDL);qtWindow.setLayout(layout);SDL_Window*sdlWindownullptr;SDL_Renderer*sdlRenderernullptr;if(SDL_Init(SDL_INIT_VIDEO)!0){QMessageBox::critical(qtWindow,SDL Init Error,QString(SDL_Init failed: %1).arg(SDL_GetError()));return-1;}QObject::connect(btnOpenSDL,QPushButton::clicked,[](){if(sdlWindow!nullptr){return;}sdlWindowSDL_CreateWindow(SDL2 Window,SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,640,480,SDL_WINDOW_SHOWN);if(sdlWindownullptr){QMessageBox::critical(qtWindow,SDL Window Error,QString(SDL_CreateWindow failed: %1).arg(SDL_GetError()));return;}sdlRendererSDL_CreateRenderer(sdlWindow,-1,SDL_RENDERER_ACCELERATED);if(sdlRenderernullptr){QMessageBox::critical(qtWindow,SDL Renderer Error,QString(SDL_CreateRenderer failed: %1).arg(SDL_GetError()));SDL_DestroyWindow(sdlWindow);sdlWindownullptr;return;}});QObject::connect(btnCloseSDL,QPushButton::clicked,[](){if(sdlRenderer!nullptr){SDL_DestroyRenderer(sdlRenderer);sdlRenderernullptr;}if(sdlWindow!nullptr){SDL_DestroyWindow(sdlWindow);sdlWindownullptr;}});QTimer timer;QObject::connect(timer,QTimer::timeout,[](){SDL_Event event;while(SDL_PollEvent(event)){if(event.typeSDL_QUIT){if(sdlRenderer!nullptr){SDL_DestroyRenderer(sdlRenderer);sdlRenderernullptr;}if(sdlWindow!nullptr){SDL_DestroyWindow(sdlWindow);sdlWindownullptr;}}}if(sdlRenderer!nullptr){SDL_SetRenderDrawColor(sdlRenderer,30,120,200,255);SDL_RenderClear(sdlRenderer);SDL_RenderPresent(sdlRenderer);}});timer.start(16);qtWindow.show();intretapp.exec();if(sdlRenderer!nullptr){SDL_DestroyRenderer(sdlRenderer);sdlRenderernullptr;}if(sdlWindow!nullptr){SDL_DestroyWindow(sdlWindow);sdlWindownullptr;}SDL_Quit();returnret;}8. 代码结构解释8.1 Qt 主程序QApplicationapp(argc,argv);这是 Qt GUI 程序的入口对象。只要是 Qt 窗口程序一般都需要创建QApplication。8.2 创建 Qt 主窗口QWidget qtWindow;qtWindow.setWindowTitle(Qt SDL2 Minimal Demo);qtWindow.resize(300,120);这几行代码创建了一个简单的 Qt 窗口。窗口标题是Qt SDL2 Minimal Demo窗口大小是300 x 1208.3 创建两个按钮QPushButton*btnOpenSDLnewQPushButton(打开 SDL2 窗口);QPushButton*btnCloseSDLnewQPushButton(关闭 SDL2 窗口);这两个按钮的作用分别是按钮作用打开 SDL2 窗口创建 SDL2 窗口和 SDL2 渲染器关闭 SDL2 窗口销毁 SDL2 渲染器和窗口8.4 初始化 SDL2if(SDL_Init(SDL_INIT_VIDEO)!0){QMessageBox::critical(qtWindow,SDL Init Error,QString(SDL_Init failed: %1).arg(SDL_GetError()));return-1;}这段代码用于初始化 SDL2 的视频模块。如果初始化失败会弹出 Qt 错误提示框。8.5 创建 SDL2 窗口sdlWindowSDL_CreateWindow(SDL2 Window,SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,640,480,SDL_WINDOW_SHOWN);这段代码会创建一个 SDL2 窗口。窗口参数如下参数含义SDL2 WindowSDL2 窗口标题SDL_WINDOWPOS_CENTERED窗口水平居中SDL_WINDOWPOS_CENTERED窗口垂直居中640窗口宽度480窗口高度SDL_WINDOW_SHOWN创建后立即显示8.6 创建 SDL2 渲染器sdlRendererSDL_CreateRenderer(sdlWindow,-1,SDL_RENDERER_ACCELERATED);SDL2 渲染器用于向窗口绘制内容。其中参数含义sdlWindow要绑定的 SDL2 窗口-1自动选择渲染驱动SDL_RENDERER_ACCELERATED尽量使用硬件加速8.7 使用 QTimer 驱动 SDL2 刷新QTimer timer;QObject::connect(timer,QTimer::timeout,[](){...});timer.start(16);这里使用 Qt 的定时器周期性调用 SDL2 的事件处理和画面刷新。timer.start(16)表示大约每 16 ms 执行一次。大约对应1000 ms / 16 ms ≈ 60 FPS也就是接近 60 帧每秒。8.8 处理 SDL2 窗口关闭事件while(SDL_PollEvent(event)){if(event.typeSDL_QUIT){...}}这段代码用于处理 SDL2 事件。如果用户点击 SDL2 窗口右上角关闭按钮就会收到SDL_QUIT事件。收到这个事件后程序会销毁 SDL2 渲染器和窗口。8.9 SDL2 渲染纯色背景SDL_SetRenderDrawColor(sdlRenderer,30,120,200,255);SDL_RenderClear(sdlRenderer);SDL_RenderPresent(sdlRenderer);这三行代码的含义是函数作用SDL_SetRenderDrawColor()设置绘制颜色SDL_RenderClear()使用当前颜色清空窗口SDL_RenderPresent()将绘制结果显示到窗口当前设置的颜色是蓝色系30,120,200,255四个参数分别表示R, G, B, A也就是参数含义R红色分量G绿色分量B蓝色分量A透明度9. 编译运行项目在工程目录中执行cd~/Desktop/jiankong/qt_sdl_min生成 Makefileqmake qt_sdl_min.pro编译make运行./qt_sdl_min10. 正常运行现象运行后首先会出现一个 Qt 窗口Qt SDL2 Minimal Demo [打开 SDL2 窗口] [关闭 SDL2 窗口]点击打开 SDL2 窗口会弹出一个 SDL2 窗口SDL2 WindowSDL2 窗口大小为640 x 480窗口背景显示为蓝色。如果能看到这个现象说明以下内容全部正常Qt 工程可以正常编译Qt 窗口可以正常显示SDL2 可以正常初始化SDL2 窗口可以正常创建SDL2 渲染器可以正常创建Qt 项目已经成功链接 SDL2SDL2 可以在 Qt 项目中正常使用。11. 验证程序是否真的链接 SDL2编译成功后在工程目录执行ldd ./qt_sdl_min|grepSDL正常会看到类似输出libSDL2-2.0.so.0/usr/local/lib/libSDL2-2.0.so.0如果看到libSDL2-2.0.so.0说明程序运行时确实链接到了 SDL2 动态库。12. 常见问题和解决方法12.1 找不到 qmake如果执行qmake-v提示qmake: command not found说明没有安装 Qt 开发工具。解决方法sudoaptupdatesudoaptinstall-yqt5-qmake qtbase5-dev qtbase5-dev-tools12.2 找不到 SDL.h如果编译时报错SDL.h: No such file or directory说明 qmake 没有找到 SDL2 的头文件。先检查pkg-config--modversionsdl2如果没有输出版本号可能是PKG_CONFIG_PATH没有包含 SDL2 的路径。可以执行exportPKG_CONFIG_PATH/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH然后重新编译qmake qt_sdl_min.promakecleanmake12.3 运行时报找不到 libSDL2如果运行时报错error while loading shared libraries: libSDL2-2.0.so.0说明运行时找不到 SDL2 动态库。先执行sudoldconfig然后重新运行./qt_sdl_min如果仍然不行可以临时指定动态库路径exportLD_LIBRARY_PATH/usr/local/lib:$LD_LIBRARY_PATH./qt_sdl_min12.4 虚拟机中不弹出窗口如果程序运行了但是没有弹出 Qt 或 SDL 窗口先检查当前环境是否有图形显示echo$DISPLAY正常应该输出类似:0如果没有输出说明当前终端没有图形显示环境。常见原因通过 SSH 登录没有开启 X11 转发当前系统是纯命令行模式虚拟机图形界面没有正常启动没有在桌面终端里运行程序。建议在 Ubuntu 图形桌面里的终端中运行程序。12.5 SDL 窗口一闪而过如果 SDL 窗口一闪而过通常是程序直接退出了。本教程代码中 Qt 主事件循环由下面代码维持intretapp.exec();只要 Qt 主窗口没有关闭程序不会直接退出。如果你自己写代码时没有事件循环窗口可能会一闪而过。13. 后续如何接 FFmpeg 视频显示当前原型只是显示纯色背景SDL_SetRenderDrawColor(sdlRenderer,30,120,200,255);SDL_RenderClear(sdlRenderer);SDL_RenderPresent(sdlRenderer);后续如果要显示 FFmpeg 解码出来的视频画面就需要改成FFmpeg 解码得到 AVFrame ↓ 将 AVFrame 转成 SDL 支持的像素格式 ↓ 创建 SDL_Texture ↓ 调用 SDL_UpdateTexture 更新画面数据 ↓ 调用 SDL_RenderCopy 复制纹理到窗口 ↓ 调用 SDL_RenderPresent 显示画面核心 SDL2 显示流程会变成SDL_UpdateTexture(texture,nullptr,videoData,lineSize);SDL_RenderClear(renderer);SDL_RenderCopy(renderer,texture,nullptr,nullptr);SDL_RenderPresent(renderer);所以本教程的最小原型主要是为了验证工程基础Qt 能不能正常运行 SDL2 能不能正常初始化 Qt 能不能链接 SDL2 SDL2 能不能创建窗口 SDL2 能不能渲染画面确认这些没问题之后再接 FFmpeg 会更稳。14. 小白学习路线建议建议按下面顺序学习第一步只学 SDL2 窗口先单独写 SDL2 程序完成创建窗口渲染纯色处理关闭事件。第二步只学 Qt 窗口单独写 Qt 程序完成创建主窗口添加按钮响应按钮点击。第三步Qt 调 SDL2也就是本教程的内容Qt 创建主界面SDL2 创建独立显示窗口Qt 定时器驱动 SDL2 事件处理和刷新。第四步SDL2 显示图片或模拟视频帧先不用 FFmpeg可以手动生成 RGB/YUV 数据送给 SDL2 显示。第五步FFmpeg 解码视频先在命令行或纯 C/C 中使用 FFmpeg 解码本地视频文件。第六步FFmpeg SDL2把 FFmpeg 解码后的视频帧交给 SDL2 显示。第七步Qt FFmpeg SDL2最后整合成完整播放器或视频监控客户端Qt 做界面 FFmpeg 解码 SDL2 显示 MediaMTX 提供视频流15. 总结本文完成了一个最小 Qt SDL2 原型。通过这个原型可以确认SDL2 已经正确安装Qt 项目可以通过pkg-config找到 SDL2Qt 工程可以正常编译 SDL2 代码程序可以创建 Qt 主窗口程序可以点击按钮创建 SDL2 窗口SDL2 可以正常渲染画面后续可以在这个基础上接入 FFmpeg 视频帧显示。最小工程结构如下qt_sdl_min/ ├── main.cpp └── qt_sdl_min.pro核心编译命令如下qmake qt_sdl_min.promake./qt_sdl_min验证 SDL2 链接命令如下ldd ./qt_sdl_min|grepSDL如果运行后能看到 Qt 窗口并且点击按钮后能打开 SDL2 蓝色窗口就说明 Qt SDL2 最小原型已经成功。

相关新闻

数字电子技术核心:从逻辑门到FPGA的硬件设计实战指南

数字电子技术核心:从逻辑门到FPGA的硬件设计实战指南

1. 项目概述:为什么数电是数字世界的基石 最近在论坛和社群里,看到不少刚入行电子、计算机或者自动化领域的朋友在问:“计算机专业真的需要学数电模电吗?”、“数电模电到底有什么用?”。作为一个在硬件和嵌入式系统里…

2026/10/11 8:22:52 阅读更多 →
IT疑难杂症诊疗室:从“救火”到“治本”的系统化排障实战指南

IT疑难杂症诊疗室:从“救火”到“治本”的系统化排障实战指南

引言:当IT系统“生病”时 深夜,报警短信的警报声划破寂静。数据库连接池耗尽,核心交易系统响应时间飙升,用户投诉如潮水般涌来。这并非科幻电影场景,而是每一位IT从业者都可能经历的“午夜惊魂”。在复杂的现代IT架构中,系统故障如同人体疾病——症状千变万化,病因错综…

2026/10/11 0:53:51 阅读更多 →
企业微信外部群自动化系统,到底该选哪个技术路线?

企业微信外部群自动化系统,到底该选哪个技术路线?

一、 摆在开发者眼前的两条路 在做企业微信私域流量自动化(尤其是外部群的机器人主动管理)时,技术团队通常会面临两种完全不同的技术选型: 原生 Webhook / 官方 API 路线: 安全稳定,但针对外部群的能力几乎…

2026/10/11 14:04:38 阅读更多 →

最新新闻

LSTM多变量预测实战:用Python搭建成绩预测模型

LSTM多变量预测实战:用Python搭建成绩预测模型

简介:面向希望掌握LSTM多变量预测的Python开发者,这份资源以成绩预测等场景为主线,系统覆盖时间序列转监督数据、数据预处理、模型定义与训练、评估优化等关键环节;同时按单变量、多变量和多步预测拆分为多个模块,并配…

2026/10/11 14:04:17 阅读更多 →
环保公益活动管理与宣传系统源码 Java+SpringBoot+Vue 前后分离

环保公益活动管理与宣传系统源码 Java+SpringBoot+Vue 前后分离

一、关键词环保公益活动管理与宣传系统,环保公益活动宣传管理系统,环保志愿公益活动管理平台二、作品包含源码数据库全套环境和工具资源本地部署教程三、项目技术前端技术:Html、Css、Js、Vue2、Element-ui后端技术:Java、SpringB…

2026/10/11 14:04:17 阅读更多 →
道路动物目标检测数据集实战:VOC转YOLO与训练避坑指南

道路动物目标检测数据集实战:VOC转YOLO与训练避坑指南

简介:道路及野外动物目标检测数据集是一套面向自动驾驶安全、生态监测、智能交通与安防系统等场景的专业标注数据包。数据来自真实道路与野外环境,包含昼夜不同光照条件,涵盖车辆行驶视角、固定监控视角等多角度画面,覆盖鸟、猫、…

2026/10/11 14:04:17 阅读更多 →
工控安全如何不掉队:新型工业化下的PLC与协议级防护实践

工控安全如何不掉队:新型工业化下的PLC与协议级防护实践

1. 项目概述:一张图背后的真实战场“一图读懂丨电子四院:新型工业化,工控安全如何不掉队”——这个标题乍看是张信息图的导语,但实际指向的是当前制造业数字化转型中一个极其具体、紧迫且常被轻描淡写的现实问题:当产线…

2026/10/11 14:04:17 阅读更多 →
大文件处理与流式编程:从内存溢出到背压实战

大文件处理与流式编程:从内存溢出到背压实战

1. 从一次内存溢出的“事故现场”说起 先讲一次让我印象极深的经历。某天凌晨,公司一个定时任务挂了,负责清理一批历史日志文件并生成汇总报表。脚本不复杂,核心就是读文件、做清洗、统计、再写出去。问题是这批日志加起来有好几个GB&#xf…

2026/10/11 14:04:17 阅读更多 →
基于Android的信息化医疗服务系统:架构设计与弱网优化实战

基于Android的信息化医疗服务系统:架构设计与弱网优化实战

简介:这是一套面向高校安卓课程设计与移动医疗开发学习者的完整项目资料,源自大三学期课程作业,由两人协作约两个月完成,涵盖Android客户端、后端数据接口与简易Web管理后台,适合想了解SpringBoot、jFinal与安卓端联调…

2026/10/11 14:03:16 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

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