Qt跨平台开发实战:从环境搭建到系统集成
1. Qt系统开发基础与环境搭建1.1 Qt框架概述与核心特性Qt作为一款跨平台的C应用程序开发框架其核心价值在于一次编写到处编译的能力。我使用Qt开发已有八年时间从嵌入式设备到桌面应用再到移动端这套框架始终保持着几个关键优势首先是信号槽机制Signal Slot这是Qt最革命性的设计。不同于传统的回调函数信号槽采用松耦合的方式连接对象。比如当按钮被点击时不需要直接调用处理函数而是发射clicked()信号任何槽函数都可以与之连接。这种机制在大型项目中尤其有用我曾在工业控制项目中用信号槽连接了200多个UI组件依然保持清晰的代码结构。其次是元对象系统Meta-Object System通过moc预处理器实现运行时类型信息。这使Qt能够实现动态属性、反射等高级特性。记得在开发一个动态表单系统时正是利用Q_PROPERTY和QMetaObject实现了字段的实时添加和校验。跨平台能力是Qt的立身之本。同一套代码可以在Windows、Linux、macOS甚至嵌入式系统上运行。去年我负责的一个项目需要同时部署到Windows工控机和Linux边缘计算设备Qt的qmake构建系统只需简单修改.pro文件配置就能生成不同平台的可执行文件。1.2 开发环境配置实战1.2.1 Qt安装避坑指南从Qt官网下载安装包时新手常犯的错误是选择在线安装器却未配置镜像源。国内用户建议使用清华或中科大的镜像否则下载速度可能只有几十KB/s。我通常使用以下命令配置./qt-unified-linux-x64-online.run --mirror https://mirrors.tuna.tsinghua.edu.cn/qt组件选择是另一个关键点。完整安装会占用20GB空间实际上根据项目需求选择即可MSVC版本需提前安装Visual StudioMinGW版本独立运行Android版本需配置JDK/NDK源码用于自定义编译重要提示安装路径不要包含中文或空格我曾遇到一个项目编译失败花了三小时才发现是安装路径中的空格导致qmake解析异常。1.2.2 多编译器环境配置在Windows平台Qt支持MSVC和MinGW两种编译器链。我的经验是MSVC适合大型项目调试信息丰富但需要安装Visual StudioMinGW轻量级生成exe可独立运行但调试能力较弱配置MSVC2019的示例.pro文件配置QT core gui CONFIG c17 QMAKE_CXXFLAGS /MP # 启用多核编译 win32 { QMAKE_CXX cl QMAKE_LINK link CONFIG debug_and_release }1.2.3 第三方模块集成遇到unknown module(s) in QT: xlsx错误时说明需要手动编译QtXlsx模块。具体步骤git clone https://github.com/dbzhang800/QtXlsxWriter.git cd QtXlsxWriter qmake make -j8 make install然后在.pro文件中添加QT xlsx2. Qt核心系统模块解析2.1 文件系统操作实战2.1.1 跨平台文件路径处理Qt使用QDir和QFileInfo处理文件路径时必须注意平台差异。Windows使用反斜杠()而Linux/macOS使用正斜杠(/)。最佳实践是始终使用Qt提供的路径分隔符QString path QDir::home().filePath(Documents/report.pdf); // 自动转换为适合当前系统的路径格式我曾在一个跨平台项目中遇到路径硬编码问题导致Linux下无法加载资源文件。解决方案是使用资源系统(qrc)或QStandardPathsQString configPath QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);2.1.2 文件监控与变更响应QFileSystemWatcher可以实现文件变更监听但在实际使用中有几个坑需要注意QFileSystemWatcher watcher; watcher.addPath(/var/log/app.log); // 必须保持watcher对象生命周期否则信号无法触发 QObject::connect(watcher, QFileSystemWatcher::fileChanged, [](const QString path){ qDebug() File modified: path; // 注意文件被删除后需要重新添加监控 });在Linux系统上inotify有监控数量限制默认8192可以通过修改/proc/sys/fs/inotify/max_user_watches调整。2.2 系统信息获取与交互2.2.1 硬件信息查询通过QSysInfo可以获取基本的系统信息但更详细的硬件信息需要平台特定API。Windows下使用WMI的示例#ifdef Q_OS_WIN #include wbemidl.h #include comdef.h QString getCPUInfo() { HRESULT hres; IWbemLocator *pLoc nullptr; IWbemServices *pSvc nullptr; hres CoInitializeEx(0, COINIT_MULTITHREADED); hres CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)pLoc); // 简化代码实际使用需添加错误处理和内存释放 return QString::fromWCharArray(_bstr_t(LIntel Core i7-10700K)); } #endif2.2.2 进程间通信(IPC)Qt提供多种IPC机制各有适用场景方式适用场景性能复杂度QSharedMemory大数据量共享高低QLocalSocket流式通信中中D-Bus系统级服务低高TCP/UDP跨主机可变高共享内存的典型用法QSharedMemory shared(AppSharedBuffer); if (!shared.create(1024)) { qDebug() Already running: shared.errorString(); return -1; } // 写入数据 shared.lock(); char *to (char*)shared.data(); strcpy(to, Hello from Process1); shared.unlock();3. 国际化与本地化实现3.1 多语言支持全流程3.1.1 翻译文件生成完整的国际化流程包括在代码中使用tr()标记可翻译文本QPushButton *btn new QPushButton(tr(Submit));在.pro文件中指定翻译文件TRANSLATIONS app_zh_CN.ts app_ja_JP.ts使用lupdate生成.ts文件lupdate project.pro使用Qt Linguist编辑翻译使用lrelease编译为.qm文件lrelease project.pro3.1.2 动态语言切换实现运行时语言切换需要注意void MainWindow::changeLanguage(const QString language) { static QTranslator *appTranslator nullptr; if(appTranslator) { qApp-removeTranslator(appTranslator); delete appTranslator; } appTranslator new QTranslator(this); if(appTranslator-load(:/translations/app_ language .qm)) { qApp-installTranslator(appTranslator); } else { qWarning() Failed to load translation file; } // 必须重新设置所有界面文本 ui-retranslateUi(this); }经验在大型项目中所有自定义控件也需要实现retranslateUi()函数否则切换语言时部分文本不会更新。3.2 本地化适配要点3.2.1 日期时间格式化不同地区的日期格式差异很大应始终使用QLocale进行格式化QLocale cnLocale(QLocale::Chinese); QString dateStr cnLocale.toString(QDate::currentDate(), QLocale::LongFormat); // 输出2023年8月15日 QLocale usLocale(QLocale::English, QLocale::UnitedStates); dateStr usLocale.toString(QDate::currentDate(), QLocale::LongFormat); // 输出August 15, 20233.2.2 数字与货币处理财务类软件要特别注意double value 1234567.89; QLocale deLocale(QLocale::German); QString deValue deLocale.toString(value, f, 2); // 输出1.234.567,89 QLocale jpLocale(QLocale::Japanese); QString jpCurrency jpLocale.toCurrencyString(value); // 输出1,234,568 (日元没有小数位)4. 系统集成高级技巧4.1 注册表与系统配置4.1.1 Windows注册表操作Qt没有直接提供注册表操作类但可以通过QSettings访问// 读写HKEY_CURRENT_USER QSettings reg(HKEY_CURRENT_USER\\Software\\MyApp, QSettings::NativeFormat); reg.setValue(LastUsed, QDateTime::currentDateTime()); QString version reg.value(Version, 1.0).toString(); // 32/64位兼容问题在64位系统上访问32位注册表分支 QSettings reg32(HKEY_LOCAL_MACHINE\\Software\\WOW6432Node\\MyApp, QSettings::NativeFormat);警告频繁读写注册表会影响性能建议缓存常用值。我曾遇到一个性能问题最终发现是每秒读取注册表上百次导致的。4.1.2 系统托盘实现QSystemTrayIcon的完整实现示例m_trayIcon new QSystemTrayIcon(QIcon(:/icons/app.png), this); QMenu *trayMenu new QMenu(this); trayMenu-addAction(tr(Show), this, MainWindow::showNormal); trayMenu-addAction(tr(Exit), qApp, QCoreApplication::quit); m_trayIcon-setContextMenu(trayMenu); connect(m_trayIcon, QSystemTrayIcon::activated, [this](QSystemTrayIcon::ActivationReason reason){ if(reason QSystemTrayIcon::DoubleClick) { this-showNormal(); } }); // macOS特殊处理必须调用setVisible(true)才会显示 #ifdef Q_OS_MAC m_trayIcon-setVisible(true); #endif4.2 系统服务集成4.2.1 Windows服务开发使用QtService实现Windows服务的核心结构class MyService : public QtServiceQCoreApplication { public: MyService(int argc, char **argv) : QtServiceQCoreApplication(argc, argv, MyService) { setServiceDescription(My background service); setServiceFlags(QtServiceBase::CanBeSuspended); } protected: void start() override { // 启动服务线程 m_thread new WorkerThread(this); connect(m_thread, WorkerThread::error, [](const QString msg){ logMessage(msg, Error); }); m_thread-start(); } void pause() override { m_thread-pause(); } void resume() override { m_thread-resume(); } private: WorkerThread *m_thread; };4.2.2 Linux守护进程在Linux系统下实现守护进程的关键步骤void daemonize() { pid_t pid fork(); if (pid 0) exit(EXIT_FAILURE); if (pid 0) exit(EXIT_SUCCESS); // 父进程退出 umask(0); // 重置文件权限掩码 // 创建新会话 if (setsid() 0) exit(EXIT_FAILURE); // 关闭标准文件描述符 close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); // 重定向输出到日志文件 freopen(/var/log/mydaemon.log, a, stdout); freopen(/var/log/mydaemon.err, a, stderr); }5. 调试与异常处理5.1 Qt错误处理机制5.1.1 信号槽错误捕获Qt5新增的连接语法支持编译时检查但运行时错误仍需处理// 旧式连接运行时才能发现错误 connect(btn, SIGNAL(clicked()), this, SLOT(onClick())); // 新式连接编译时检查 connect(btn, QPushButton::clicked, this, MainWindow::onClick); // 处理信号槽异常 try { QMetaObject::invokeMethod(obj, slotName, Qt::DirectConnection, Q_ARG(QString, test)); } catch (const std::exception e) { qCritical() Slot invocation failed: e.what(); }5.1.2 崩溃捕获与日志跨平台崩溃捕获方案#ifdef Q_OS_WIN #include windows.h #include dbghelp.h LONG WINAPI crashHandler(_EXCEPTION_POINTERS *pException) { // 生成dump文件 HANDLE hFile CreateFileW(Lcrash.dmp, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); MINIDUMP_EXCEPTION_INFORMATION mei; mei.ThreadId GetCurrentThreadId(); mei.ExceptionPointers pException; mei.ClientPointers FALSE; MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, mei, NULL, NULL); CloseHandle(hFile); return EXCEPTION_EXECUTE_HANDLER; } #endif void installCrashHandler() { #ifdef Q_OS_WIN SetUnhandledExceptionFilter(crashHandler); #else // Linux/macOS使用signal处理 #endif }5.2 性能分析与优化5.2.1 QElapsedTimer使用技巧精确测量代码执行时间QElapsedTimer timer; timer.start(); // 执行待测代码 heavyOperation(); qint64 ns timer.nsecsElapsed(); qDebug() Operation took ns/1e6 ms; // 统计多次执行 timer.restart(); for(int i0; i100; i) { lightOperation(); } qDebug() Average time: timer.elapsed()/100.0 ms per call;5.2.2 内存泄漏检测在.pro文件中启用内存检测CONFIG debug DEFINES QT_DEPRECATED_WARNINGS QT_NO_CAST_FROM_ASCII使用ValgrindLinux或VLDWindows进行内存分析。一个常见的内存泄漏场景void leakExample() { QListQString* list; for(int i0; i100; i) { list.append(new QString(item)); // 泄漏 } // 正确做法使用智能指针或手动释放 qDeleteAll(list); // 释放所有指针 list.clear(); }6. 部署与打包实战6.1 Windows平台打包6.1.1 依赖项收集使用windeployqt自动收集依赖windeployqt --compiler-runtime --no-translations MyApp.exe常见问题处理缺少VCRUNTIME140.dll安装VC Redistributable缺少特定插件手动复制platforms/qwindows.dll高DPI支持添加清单文件6.1.2 Inno Setup打包脚本典型安装脚本配置[Setup] AppNameMyApp AppVersion1.0 DefaultDirName{pf}\MyApp OutputDir.\Output OutputBaseFilenameMyAppSetup Compressionlzma2 SolidCompressionyes [Files] Source: Release\*; DestDir: {app}; Flags: ignoreversion recursesubdirs [Icons] Name: {commonprograms}\MyApp; Filename: {app}\MyApp.exe Name: {commondesktop}\MyApp; Filename: {app}\MyApp.exe [Run] Filename: {app}\MyApp.exe; Description: Run application now; Flags: postinstall nowait6.2 Linux平台部署6.2.1 AppImage打包创建AppImage的基本流程# 准备AppDir结构 mkdir -p MyApp.AppDir/usr/bin cp myapp MyApp.AppDir/usr/bin cp myapp.desktop MyApp.AppDir/ cp icon.png MyApp.AppDir/ # 下载linuxdeployqt wget https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage chmod x linuxdeployqt*.AppImage # 生成AppImage ./linuxdeployqt*.AppImage MyApp.AppDir/myapp.desktop -appimage6.2.2 解决依赖问题常见库依赖问题解决方案# 查看缺失的库 ldd myapp | grep not found # 解决方案1静态链接 # 在.pro文件中添加 QMAKE_LFLAGS -static # 解决方案2打包共享库 # 创建lib目录复制所有依赖库 mkdir -p package/lib cp /path/to/missing/lib*.so* package/lib/ # 使用LD_LIBRARY_PATH指定库路径 #!/bin/sh HERE$(dirname $(readlink -f $0)) export LD_LIBRARY_PATH$HERE/lib:$LD_LIBRARY_PATH exec $HERE/myapp $7. 跨平台开发经验7.1 平台特定代码处理7.1.1 条件编译实践处理平台差异的标准做法#ifdef Q_OS_WIN #include windows.h void beep() { MessageBeep(MB_ICONINFORMATION); } #elif defined(Q_OS_MAC) #include Carbon/Carbon.h void beep() { SysBeep(1); } #else #include unistd.h void beep() { write(STDERR_FILENO, \a, 1); } #endif.pro文件中的平台判断win32 { LIBS -luser32 DEFINES WIN32_LEAN_AND_MEAN } macx { LIBS -framework Carbon } unix:!macx { LIBS -lpthread }7.1.2 文件系统差异处理处理路径差异的实用类class PathUtils { public: static QString configPath() { #ifdef Q_OS_WIN return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); #else return QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); #endif } static QString tempFilePath(const QString name) { #ifdef Q_OS_LINUX return QDir::temp().filePath(name); #else return QStandardPaths::writableLocation(QStandardPaths::TempLocation) / name; #endif } };7.2 高DPI适配方案7.2.1 Qt高DPI支持配置现代Qt应用应该启用高DPI支持// 在main函数开头添加 QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);.pro文件配置# 启用高DPI缩放 QT gui CONFIG highdpi7.2.2 多屏DPI适配处理多显示器不同DPI的场景// 获取当前窗口所在屏幕的DPI qreal dpi window()-screen()-logicalDotsPerInch(); // 根据DPI缩放因子调整界面 qreal scaleFactor dpi / 96.0; // 96是标准DPI ui-label-setFont(QFont(Arial, 12 * scaleFactor));经验避免使用固定像素尺寸改用QSizePolicy和布局管理器。我在4K屏上调试时发现硬编码的按钮尺寸在1080p屏幕上显得过大。

相关新闻

Pygame-CE绘图系统详解:从Surface基础到动画实现

Pygame-CE绘图系统详解:从Surface基础到动画实现

1. 项目概述:为什么是pygame-ce,而不仅仅是pygame?如果你在搜索引擎里敲下“pygame 教程”,大概率会看到很多几年前甚至十几年前的教程。这些教程的代码,在最新的Python 3.11、3.12上运行时,可能会遇到各种…

2026/10/15 3:12:10 阅读更多 →
CentOS 7 安装配置JDK全攻略:OpenJDK选型、YUM与手动安装详解

CentOS 7 安装配置JDK全攻略:OpenJDK选型、YUM与手动安装详解

1. 项目概述:为什么在CentOS 7上安装JDK依然是关键一步? 如果你刚拿到一台崭新的CentOS 7服务器,或者接手了一个老旧的、需要Java环境支持的项目,那么配置JDK(Java Development Kit)几乎是你绕不开的第一步…

2026/10/16 11:36:47 阅读更多 →
AI智能体开发实战:从核心架构到工作流搭建的完整指南

AI智能体开发实战:从核心架构到工作流搭建的完整指南

1. 从“能对话”到“能办事”:AI智能体的范式跃迁最近圈子里聊得最火的,就是“国产大模型密集上新”和“AI智能体”这两个词。作为一名在AI应用层摸爬滚打了多年的从业者,我深切感受到,行业的风向真的变了。过去一年,我…

2026/10/17 5:04:52 阅读更多 →

最新新闻

Unity滑动方向判断全攻略:从坐标系到多点触控的工程实践

Unity滑动方向判断全攻略:从坐标系到多点触控的工程实践

做滑动类交互,方向判断永远是绕不开的一块。不管是手游里的手势出招、卡牌翻页、地图拖拽,还是某个内部工具的拖拽面板,都要回答同一个问题:鼠标或手指到底朝哪个方向拖了?Unity里判断鼠标上下左右拖动方向&#xff0c…

2026/10/17 16:19:30 阅读更多 →
存储卡0KB视频修复全解析:从文件系统原理到数据恢复实战

存储卡0KB视频修复全解析:从文件系统原理到数据恢复实战

1. 0KB视频文件到底是怎么“变”出来的 相机存储卡里的视频突然变成0KB、无字节,估计很多摄影爱好者都经历过这种瞬间血压升高的事故。明明拍摄的时候相机回放还好好的,拿读卡器插上电脑一看,文件名还在,后缀也是.mp4或.mov&#…

2026/10/17 16:19:30 阅读更多 →
Seedance 2.0提示词工程:结构化模板与本地校验脚本

Seedance 2.0提示词工程:结构化模板与本地校验脚本

简介:提示词工程是AI内容生成领域的基础能力,旨在通过结构化描述提升模型输出可控性。文生视频模型对提示词的解析机制与文本模型不同,存在语义权重衰减、动词优先、镜头术语敏感等特征。掌握这些原理,创作者能将模糊的创意转化为…

2026/10/17 16:19:30 阅读更多 →
React Native开发必知:JS、JSX、TS、TSX四者区别与选型指南

React Native开发必知:JS、JSX、TS、TSX四者区别与选型指南

最近在React Native社区里,经常看到有人把js、jsx、ts、tsx混着用,也看到不少刚入门的朋友问“这四个到底有啥区别?”。我最早接触RN的时候也走过弯路:项目里既有.js又有.jsx,后来升级TypeScript又冒出来.ts和.tsx&…

2026/10/17 16:19:30 阅读更多 →
Delphi屏幕找图实战:像素匹配与性能优化全解析

Delphi屏幕找图实战:像素匹配与性能优化全解析

简介:面向 Delphi 开发者的屏幕找图与图像匹配工具资源,重点解决自动化测试、游戏辅助、屏幕抓取等场景下“快速找出屏幕上匹配图片”的需求。内容系统梳理了图像处理库选型、屏幕抓取 API(如 BitBlt)、模板匹配、感知哈希及特征匹…

2026/10/17 16:19:30 阅读更多 →
Unity UI事件监听参数详解:Button与Toggle从Inspector到代码传参全攻略

Unity UI事件监听参数详解:Button与Toggle从Inspector到代码传参全攻略

做了这么多年Unity UI,我猜十有八九的新手都在Button的Inspector面板上卡过壳:明明写了一个带参数的方法,想在OnClick()的框里拖进去,结果列表里灰了一片,只能选那些不带参数的。等到换用代码AddListener,又…

2026/10/17 16:18:30 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 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/16 21:54:38 阅读更多 →
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/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →