Qt数据库综合应用组件源码:统一管理ODBC/SQLite/MySQL连接与SQL方言适配
1. 为什么我需要一套“Qt数据库综合应用组件源码”先说个场景。我维护过一个模拟项目X界面层在Windows上报文要落到ODBC数据源里同时内部结果又要复制一份到SQLite本地缓存里供离线分析。后来又加了Linux服务器上的MySQL入库。刚开始项目小每个功能模块里直接new一个QSqlDatabase自己拼连接串结果不到三个月代码里全是数据库分支今天改一处查询明天发现另一个模块没同步改。那一刻我才意识到必须把数据库访问整理成一个独立的组件源码统一管理连接、查询、类型转换和错误处理才能真正解放业务代码。这套组件源码的核心任务只有一句话同时支持odbc、sqlite、mysql等多种数据库让业务模块感知不到后端驱动差异。换句话说我写的数据库访问层的调用方永远面对同一套接口至于底下连的是本地文件还是远程服务组件源码内部自动判断。当时我的设计目标列得很清楚业务模块不直接接触QSqlDatabase全局实例所有连接都走组件内部统一入口。每种驱动有独立的连接工厂但对外暴露同样的初始化参数结构。SQL语句要经过一个轻量方言适配层尽量减少SQLite与MySQL之间的语法迁移成本。所有查询结果统一返回QVariant列表调用方不关心底层驱动的类型差异。驱动加载失败、连接失败、查询失败都要有统一的错误码和日志输出不能靠QMessageBox弹窗硬扛。这套思路不复杂但把它拆成看得见摸得着的源码结构需要做不少细节设计。下面我把整个组件的模块划分、核心实现和实操接入过程按我自己整理源码时的步骤逐一展开。2. 架构分层与模块职责拆解2.1 三层架构与数据流向组件源码我最终分成了三层先看整体数据流向业务Dao层 ↓ 统一调用接口 组件核心层连接管理、查询引擎、方言适配、类型转换 ↓ QtSql驱动插件 具体数据库SQLite / MySQL / ODBC数据源 / PostgreSQL实际工程里组件核心层又进一步拆成五个子模块连接管理模块、数据访问引擎模块、SQL方言适配模块、结果集处理模块、日志与状态模块。每个模块只做一件事模块之间通过明确的数据结构交互。业务层不会拿到QSqlDatabase和QSqlQuery的原始指针所有操作都收敛到组件对外暴露的DBManager类上。2.2 连接管理模块连接管理模块的核心任务是维护所有连接实例的生命周期。Qt的QSqlDatabase本身是引用计数对象同一连接名如果被addDatabase多次后一次的设置会覆盖前一次容易造成连接配置错乱。所以组件里我对连接名做了统一规划每个连接名由业务标识和驱动类型拼接而成比如cache_sqlite、history_odbc、remote_mysql。这样多个连接并存时互不干扰。同时组件内部维护了一张连接配置表记录驱动名、连接串、超时时间、编码方式。业务层操作数据库时只需要传入一个逻辑名称组件内部自动判断应该从哪个连接取数据。这套设计的价值在于后期如果某个业务模块要换数据库只需要改配置文件业务代码一行不动。2.3 数据访问引擎模块数据访问引擎模块负责执行SQL并返回统一的结果集。我封装了三种典型操作执行不带返回值的操作、查询单条记录、查询多条记录。查询结果统一保存为QList 结构每条记录是一个Map字段名做Key。这样调用方读取数据时用字段名取值不会因为数据库返回顺序不同而出错。引擎内部还做了基础防护SQL执行前统一记录日志执行失败时捕获lastError信息并根据错误类型映射为组件自定义错误码。例如“driver not loaded”映射为错误码1001“connection failed”映射为1002“sql syntax error”映射为1003。业务层拿到错误码就能快速定位问题不用刷屏式逐条打印。2.4 SQL方言适配模块这个模块是整套组件里工作量最大的。SQLite、MySQL、ODBC虽然都遵循SQL标准但细节差异非常多。比如MySQL常用的LIMIT语句在SQL Server里要改成TOPODBC数据源如果是较老的系统分页写法又不一致。再比如MySQL支持REPLACE INTOSQLite也支持但某些ODBC驱动不直接支持需要拆成DELETE加INSERT。组件里的做法是维护一个SQL关键词映射表再配合正则替换。业务层写SQL时使用统一风格比如分页统一写LIMIT offset, size组件内部根据实际驱动的方言规则做转换。遇到实在无法转换的复杂语句组件允许业务层手动指定方言版本类似于ORM框架里的dialect配置。这一层设计到位之后数据迁移类需求的开发效率至少翻一倍。2.5 日志与状态模块日志模块统计每次连接建立耗时、查询耗时、失败次数、成功次数。我之所以单独拆出这个模块是因为数据库问题往往不是一次性出现的而是某条查询偶尔慢、某个连接偶尔断开。有了耗时统计和失败计数排查问题时就有一个明确的方向而不是满屏乱抓。组件启动时日志模块会把所有数据库驱动的加载情况记录一遍。如果发现QMYSQL驱动不存在直接在启动日志里给出提示而不是等到业务代码第一次执行SQL时才冒出“driver not loaded”的异常。这样用户提前就能知道当前发布包里缺了哪个驱动插件。3. 核心细节解析与关键实现3.1 驱动可用性检查Qt数据库驱动是插件机制编译安装Qt时可以选择哪些驱动被编译。发布程序时如果漏掉sqldrivers目录里的动态库程序运行到数据库操作就会崩溃。所以组件初始化时第一步应该做全量驱动扫描代码如下// 初始化阶段检查驱动是否可用 bool DbManager::checkDrivers() { QStringList drivers QSqlDatabase::drivers(); m_driverStatus.clear(); QStringList needDrivers {QSQLITE, QMYSQL, QODBC}; foreach (const QString driver, needDrivers) { bool ok drivers.contains(driver) QSqlDatabase::isDriverAvailable(driver); m_driverStatus.insert(driver, ok); if (!ok) { qWarning() [DbManager] Driver not available: driver; } } return !m_driverStatus.values().contains(false); }这段代码我在模拟项目X里验证得很稳。QSqlDatabase::drivers返回编译期内已经加载的驱动插件列表isDriverAvailable则负责确认当前运行时能不能直接使用。两个条件都满足时才算真正的驱动可用。3.2 连接注册与参数设置驱动可用之后下一步就是注册连接。这里有个常见误操作拿到QSqlDatabase实例就直接open忽略连接参数设置。尤其是SQLite的只读模式、MySQL的编码方式、ODBC的连接超时这些都需要通过setConnectOptions设置。组件内部我封装了一个createConnection方法核心逻辑如下bool DbManager::createConnection(const DbConfig cfg) { if (m_connections.contains(cfg.name)) { return true; } QSqlDatabase db QSqlDatabase::addDatabase(cfg.driver, cfg.name); db.setDatabaseName(cfg.databaseName); if (!cfg.host.isEmpty()) { db.setHostName(cfg.host); db.setPort(cfg.port); } if (!cfg.user.isEmpty()) { db.setUserName(cfg.user); db.setPassword(cfg.password); } // 连接选项按驱动区分 QString options; if (cfg.driver QSQLITE) { options QSQLITE_OPEN_READONLY0;QSQLITE_BUSY_TIMEOUT5000; } else if (cfg.driver QMYSQL) { options MYSQL_OPT_RECONNECT1;MYSQL_OPT_CONNECT_TIMEOUT5; } else if (cfg.driver QODBC) { options SQL_ATTR_LOGIN_TIMEOUT5; } db.setConnectOptions(options); if (!db.open()) { qWarning() [DbManager] Open failed: db.lastError().text(); return false; } m_connections.insert(cfg.name, db); return true; }SQLite的连接选项很关键。默认情况下SQLite并发写同一个文件时可能返回database is locked通过QSQLITE_BUSY_TIMEOUT设置等待时间可以显著缓解这个报错。MySQL则建议开启自动重连因为远程数据库连接在网络抖动后很容易断开。3.3 SQL方言转换细节方言适配模块的规则不复杂但需要一个个积累。我这里把平时最容易踩坑的差异点整理如下功能点SQLiteMySQL常见ODBC数据源字符串拼接||CONCAT() 或 ||分页LIMIT offset, sizeLIMIT offset, size视驱动而定常用ROW_NUMBER自增主键AUTOINCREMENTAUTO_INCREMENTIDENTITY布尔类型INTEGER 0/1BOOL/BOOLEANBIT修改表结构重新建表方式ALTER TABLEALTER TABLE日期函数datetime()NOW()GETDATE()我在组件里维护了一个规则链到达查询模块的SQL会先经过规则链处理。简单的规则用字符串替换复杂的方言差异单独写处理函数。比如分页语句组件先检测SQL中是否包含LIMIT关键字再判断当前驱动是否支持LIMIT不支持的话就改写查询语句结构。这里提醒一下方言转换不是万能的业务层写SQL时应该尽量靠标准语法能不用方言特性就不用。组件里的方言适配层只解决80%的常见情景真正遇到特殊语法时还是要业务层自己保证兼容性。3.4 数据类型与NULL值处理不同数据库返回同一业务字段时数据类型可能完全不同。比如说seq_no字段MySQL里是BIGINTSQLite里是INTEGERODBC某些数据源里却是VARCHAR。组件处理结果集时统一调用QVariant::toString或者QVariant::toLongLong转换绝对不直接利用底层返回的原生类型做计算。NULL值处理更要谨慎。SQLite里NULL是一个独立类型MySQL里NULL和空字符串、0并不等价。组件查询结果采用QVariantMap时如果数据库返回NULLQt默认给出一个无效QVariant。业务代码取出来之后如果用toString()会得到空串用toInt()会得到0容易误判业务逻辑。我的做法是在结果集处理层增加一个选项默认把NULL统一转换为指定值。例如数字字段转成0字符串字段转成空串日期字段转成minDate。调用方可以按模块选择是否启用这个转换。这样做的代价是会丢失NULL与空值的语义区分但换来了业务代码的极大简化在报表类、统计类需求里非常划算。4. 实操过程在一个具体项目中接入组件4.1 环境准备与文件放置我在模拟项目X里使用的环境是Qt 5.15版本编译器是MSVC2019 x64Linux端使用gcc。组件源码由三部分组成头文件目录、源文件目录、配置文件模板。在Qt工程里只需要把组件的pri文件include一下再在pro文件里追加一行QT sql include($$PWD/3rdparty/dbcomponent/dbcomponent.pri)发布时需要注意sqldrivers插件目录。Windows下MySQL驱动依赖libmysql.dll或者libmariadb.dllODBC驱动依赖系统ODBC管理器。组件启动时会打印驱动加载状态发布包验证时第一眼先看这个日志。4.2 统一配置结构的定义与使用组件对外暴露的第一个数据结构是DbConfig在头文件里定义如下struct DbConfig { QString name; // 连接逻辑名同一组件内唯一 QString driver; // 驱动类型QSQLITE/QMYSQL/QODBC/QPSQL QString host; // 主机仅网络数据库需要 int port 0; // 端口 QString databaseName; // SQLite为文件路径其他为库名 QString user; QString password; bool readOnly false; };配置文件则采用简单的INI风格。我在组件里内置了一个默认加载函数能自动读取config/db.ini[db_sqlite] driverQSQLITE databaseName./data/local.db [db_mysql] driverQMYSQL host192.168.x.x port3306 databaseNameapp_db userapp_user password****** [db_odbc] driverQODBC databaseNamedsnmydsn;uidadmin;pwd123456注意ODBC的databaseName字段不是库名而是完整的DSN连接串。如果使用文件DSN路径要写绝对路径如果使用机器数据源直接写数据源名称即可。组件里不区分DSN和连接串的差异全部当作QSqlDatabase::setDatabaseName的参数传入。4.3 SQLite接入的详细操作SQLite接入是最简单的。配置文件里指向一个本地文件组件调用createConnection之后业务层只需要执行execQuery就能完成建表和数据插入// 初始化组件 DbManager::instance()-loadConfig(config/db.ini); DbManager::instance()-initAllConnections(); // 执行建表语句 QString createSql CREATE TABLE IF NOT EXISTS cache_data (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, created_at TEXT); DbManager::instance()-execSql(db_sqlite, createSql); // 插入数据 QString insertSql INSERT INTO cache_data(name, created_at) VALUES(?, ?); QVariantList params; params 测试记录 QDateTime::currentDateTime().toString(yyyy-MM-dd HH:mm:ss); bool ok DbManager::instance()-execSql(db_sqlite, insertSql, params);参数绑定这块我强烈建议统一用?占位符不要用直接拼接字符串。虽然SQLite对拼接的容忍度比MySQL高但组件面向odbc、sqlite、mysql多套后端时统一占位符能避免SQL注入和中文转义问题。4.4 MySQL接入的详细操作MySQL接入在组件里几乎没有额外代码只要配置文件正确连接管理模块走同样的流程。真正的坑经常出现在发布环境插件目录里没有qsqlmysql.dll或qsqlmysql.so。应用程序目录下没有libmysql.dll。MySQL服务端不允许当前IP访问。数据库默认字符集用的是latin1业务写入中文变乱码。MySQL连接串里我一般显式加上字符集参数在配置databaseName时使用“dbnameapp_db;charsetutf8mb4”这种格式部分场景有效。更保险的做法是连接成功后执行“SET NAMES utf8mb4”组件初始化阶段可以配置一个初始化SQL列表自动在open之后执行。4.5 ODBC接入的详细操作ODBC接入有两类典型场景。第一类是连接系统DSN配置文件里直接写DSN名称第二类是连接远程数据库驱动的DSN连接串比如SQL Server的ODBC驱动。第一类场景在Windows下比较顺畅第二类场景需要确认ODBC驱动版本和驱动位数。项目是32位编译的话ODBC管理器要打开32位版本64位驱动在64位管理器里才能看到。组件对ODBC的支持做了一个特殊处理连接失败时自动尝试读取ODBC管理器中注册的驱动列表并打印日志。这个日志在排查“找不到数据源”问题时非常有效。有一次模拟项目X在客户机器上报错日志显示系统里没有安装对应驱动安装驱动后一切正常。这个过程没有跳任何业务代码。5. 常见问题与排查技巧实录5.1 问题速查表我整理了一份排查表覆盖组件使用中最常见的五类问题问题现象可能原因排查与解决QSqlDatabase: QSQLITE driver not loadedQt安装时未启用SQLite插件检查sqldrivers目录是否存在qsqlite.dllMYSQL driver not loaded缺少qsqlmysql插件或libmysql.dll把插件和依赖库放入发布目录连接MySQL超时网络不通或防火墙拦截使用ODBC数据源测试连通性SQLite提示database is locked多个连接同时写同一个文件设置QSQLITE_BUSY_TIMEOUT改用单写连接中文写入后乱码字符集不一致统一使用utf8mb4连接后执行SET NAMES5.2 一个典型的MySQL驱动加载问题模拟项目X在客户Windows服务器上部署时程序启动日志显示QMYSQL驱动加载失败。我在本地测试环境一切正常最后对比发现客户机器上缺少libmysql.dll。Qt的MySQL插件是动态链接mysql客户端的插件本身加载成功后如果客户端库缺失调用时依然会失败。解决办法是把libmysql.dll和qsqlmysql.dll放在同一个目录下必要时改成对应名字。这类问题排查时重点不是看插件文件是否存在而是看插件的依赖库是否齐全。5.3 SQLite并发写的优化心得SQLite并发写问题在组件早期版本里出现得比较频繁。多个线程同时往本地缓存库写入时SQLite会返回database is locked。虽然设置QSQLITE_BUSY_TIMEOUT能缓解但根本方案是组件内增加一个写锁所有SQLite连接的写操作通过一个互斥锁串行执行读操作可以并行。这个方案牺牲了少量写入吞吐但换来了极大的稳定性。5.4 方言转换踩过的坑方言转换最让我头疼的是字符串拼接。MySQL里用CONCAT(a, b)SQLite里用a || b这两种写法在对方数据库里都可能报错。组件里我的策略是优先推广标准SQL的写法能用简单字符串拼接就尽量不用数据库方言。如果业务代码确实需要方言组件提供whiteList机制允许指定某条SQL不经过方言转换直接透传到数据库。这个白名单机制在应对复杂报表SQL时特别重要避免规则链误伤原本正确的语句。另外一个细节是表名和字段名的引号。MySQL默认反引号SQLite默认双引号也可以ODBC不同驱动手势不一。组件统一在方言规则里把反引号转为双引号因为标准SQL更认双引号。6. 一点实践经验收尾组件从最初的粗糙版本到现在的稳定状态我最大的体会是数据库封装类最怕的就是“贪多求全”真想做到同时覆盖odbc、sqlite、mysql多套后端一开始就不要把某个数据库的特殊函数写死在业务层也不要试图在组件里模拟所有数据库特性。让核心层做连接、查询、结果集统一让方言层做常见语法翻译让业务层尽量写标准SQL这才是一条可维护的路线。如果数据库量级不大SQLite作为本地缓存加MySQL作为服务端存储的组合用这套组件源码能减少大量重复代码。后续如果再接入PostgreSQL组件里只需要新增一个驱动枚举和对应的连接配置模板业务层完全不用动。这一点我在实际项目中验证过扩展成本比想象中的低得多。最后再提一句排数据库问题的时候一定要先看初始化日志里的驱动扫描结果这一步能省掉很多瞎猜的时间。

相关新闻

Wireshark深度解析DNS协议:从报文结构到故障根因定位

Wireshark深度解析DNS协议:从报文结构到故障根因定位

1. 为什么DNS协议分析是网络排查的“第一把钥匙”你有没有遇到过这样的情况:用户说“网站打不开”,你ping域名通,curl IP也通,但浏览器就是白屏;或者内部服务调用偶尔超时,监控显示HTTP状态码全绿&#xff…

2026/10/18 4:47:34 阅读更多 →
JsonHelper封装实战:统一JSON序列化与反序列化,解决后端开发核心痛点

JsonHelper封装实战:统一JSON序列化与反序列化,解决后端开发核心痛点

做后端开发这些年,JsonHelper几乎是我每次接手项目都要先翻一遍的类。不管项目用的是哪种JSON解析库,到了业务代码层,大家最终面对的都是那个封装好的工具类。有的项目里它叫JsonUtils,有的叫JsonUtil,有的干脆把工具类…

2026/10/18 4:47:34 阅读更多 →
crawl4ai Docker部署进阶:REST API配置与并发优化实战

crawl4ai Docker部署进阶:REST API配置与并发优化实战

自己折腾过一段时间容器化爬虫服务的人,应该都有这种体会:库本身再强大,只要涉及 Docker 部署和 REST 接口开放,复杂度立刻上了一个台阶。crawl4ai 官方 Docker 镜像就是典型——它解决了“怎么把这个爬虫框架跑起来”的第一层问题…

2026/10/18 4:47:34 阅读更多 →

最新新闻

律所案件管理系统设计与落地:角色权限、流程引擎与文书协同实践

律所案件管理系统设计与落地:角色权限、流程引擎与文书协同实践

1. 这个系统到底在解决什么问题做律师的朋友应该都有过这种体验:一个案子从收到材料到最终归档,中间要经过立案、排期、取证、文书起草、审核、盖章、送达、开庭、归档这么一串流程。传统做法是什么?微信群接龙、Excel表格、共享文件夹、Outl…

2026/10/18 5:36:42 阅读更多 →
YOLO固定翼无人机检测数据集实战:从目录结构到训练避坑指南

YOLO固定翼无人机检测数据集实战:从目录结构到训练避坑指南

简介:面向固定翼无人机视觉检测任务,这份YOLO格式数据集包含数百张真实场景图片,已按mq-20-drone单类别完成标注,适用于yolov5、yolov7、yolov8等主流目标检测算法。数据集目录已预先划分好train、val、test集合,并附带…

2026/10/18 5:36:42 阅读更多 →
基于UDP的自定义Socket封装:设计思路与踩坑实录

基于UDP的自定义Socket封装:设计思路与踩坑实录

做网络传输相关项目的时候,UDP往往是绕不开的一个选项。它比TCP轻量、不维持连接状态、没有握手过程,天然适合实时音视频、游戏同步、设备状态上报这类场景。可UDP真正用起来之后你就会发现,原生Socket虽然几行代码就能把数据发出去&#xff…

2026/10/18 5:36:42 阅读更多 →
无人机频射信号检测:YOLOv5目标检测实战与数据集解析

无人机频射信号检测:YOLOv5目标检测实战与数据集解析

简介:面向无人机频射信号检测与识别任务,这份数据集包含364张原始图像及配套的yolov5格式标注txt文件,平均正确识别率达到94.3%,适用于基于目标检测框架的模型训练与算法验证。资源包共729个文件,以jpg图像和txt标签为…

2026/10/18 5:36:42 阅读更多 →
2026年AI模型API聚合服务实力横评:词元之河与硅基流动的稳定性与落地适配对比

2026年AI模型API聚合服务实力横评:词元之河与硅基流动的稳定性与落地适配对比

中小技术团队部署大模型应用,普遍要过三道坎:其一,境外模型直连受网络策略、IP 信誉与额度审批限制,平均开通要等好几个工作日;其二,国产模型协议碎片化,Qwen、GLM、DeepSeek、Claude 各家的字段…

2026/10/18 5:36:42 阅读更多 →
GPS+INS组合导航:惯导漂移、误差状态卡尔曼滤波与工程避坑指南

GPS+INS组合导航:惯导漂移、误差状态卡尔曼滤波与工程避坑指南

简介:面向组合导航学习与研究者的Matlab实现资源,重点解决GPS信号在高楼密集区、树林或电磁干扰下易丢失,而惯导系统误差随运行时间不断累积的问题,通过两者优势互补实现连续稳定的高精度导航。资源适合导航专业学生、算法工程师及…

2026/10/18 5:35:42 阅读更多 →

日新闻

基于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/16 14:23:36 阅读更多 →