Qt嵌入式调试利器:qttelnet-2.1_1实战指南与避坑
简介qttelnet-2.1_1 是一份基于 Qt 框架实现 Telnet 协议的开源项目源码包面向希望深入理解 Qt 网络模块、学习远程登录通信机制的 C 开发者与 Qt 初学者。项目以 LGPL/GPL3 双许可发布可用于研究 Telnet 客户端/服务端的连接建立、输入输出处理及事件驱动模型也适合作为参与开源社区的入门实践。压缩包共 27 个文件约 78KB以 pro、pri 工程配置与 cpp、h 源码为核心辅以 html、qdoc、qch 等文档资源及 png 图标、css 样式另含 README、INSTALL 说明与许可证文件结构紧凑、便于快速编译与阅读。目前已有 701 人学习下载。通过研读源码与配套文档读者可掌握 Qt 网络编程的典型写法理解 Telnet 协议在实际应用中的落地方式并借助示例工程与构建脚本完成本地调试为后续开发网络工具或贡献开源项目积累经验。1. qttelnet-2.1_1 到底解决了什么一个被低估的嵌入式调试刚需如果你在嵌入式 Linux 或工控设备上做过现场调试大概率遇到过这种场景设备跑在远端机柜里只有一个串口或网口可达你想看它的实时日志、想敲几条命令确认状态但手头只有一台 Windows 笔记本装个串口工具还得配驱动、配波特率、配流控折腾半天。更麻烦的是很多设备只暴露了一个 TCP 端口你需要的其实是一个能连上去、能交互、还能把会话记录下来的 Telnet 客户端而不是一个只会发数据的 socket 脚本。qttelnet-2.1_1 就是冲着这个需求来的。它是一个基于 Qt 的 Telnet 客户端实现版本号 2.1_1 通常对应一次小版本修订。核心价值在于把 Telnet 协议栈封装成 Qt 风格的类让你在 Qt 工程里几行代码就能建立一个可交互的远程终端会话同时天然继承 Qt 的信号槽机制方便做异步日志采集、命令自动下发和界面集成。它适合三类人一是做设备现场调试的嵌入式工程师二是需要把 Telnet 能力嵌进自己上位机工具的 Qt 开发者三是想找一个轻量级 Telnet 协议参考实现来改造成私有协议客户端的人。我最早接触它是因为一个模拟项目某实验室的一台图像处理 Demo 设备只开放了一个 Telnet 端口用于查看运行状态。当时用 Python 的 telnetlib 写了个脚本但发现它对中文回显和粘包处理很粗糙后来换成 Qt 方案才把交互稳定下来。qttelnet-2.1_1 不是那种功能大而全的库它更像一把趁手的小刀用对了场景很省事用错了场景也会让你踩坑。2. 把 qttelnet-2.1_1 跑起来从源码结构到第一个可交互会话2.1 源码目录里真正要关心的三个文件拿到 qttelnet-2.1_1 的源码包后不要急着全量编译。先看目录结构通常核心文件就三个qttelnet.h、qttelnet.cpp以及一个可选的qttelnetprivate.h有些版本会把私有实现拆出去。头文件里定义了对外的QTelnet类继承自QObject这意味着它可以被放进任何 Qt 线程的事件循环里。cpp 文件里是协议解析和 socket 读写的具体实现。我一般会先做一件事把qttelnet.h里的信号列表扫一遍。常见的信号包括connected()、disconnected()、readyRead(const QByteArray data)、error(QAbstractSocket::SocketError)。这几个信号决定了你后面怎么写业务逻辑。如果你发现某个版本里信号名不一样比如叫newData而不是readyRead那说明这个修订版改过接口后面的代码要跟着调。注意不同修订版的 qttelnet 在信号命名和连接方式上可能有差异拿到源码后先编译一个最小示例确认信号能正常触发再往下做业务集成。2.2 用 qmake 建一个最小可运行工程下面是一个能直接跑起来的最小工程配置。假设你已经把 qttelnet 的源码文件放到了工程目录下的qttelnet/子目录里。# minitelnet.pro QT core network QT - gui CONFIG console c11 CONFIG - app_bundle TARGET minitelnet TEMPLATE app INCLUDEPATH $$PWD/qttelnet SOURCES \ main.cpp \ qttelnet/qttelnet.cpp HEADERS \ qttelnet/qttelnet.h这段 qmake 配置的逻辑很直接只依赖core和network两个 Qt 模块去掉 gui 是为了让它在无显示环境的设备上也能编译。INCLUDEPATH指向 qttelnet 头文件所在目录SOURCES里把 cpp 文件加进去。如果你的 qttelnet 版本还有qttelnetprivate.cpp也要一并加到SOURCES里。参数说明CONFIG console让程序以控制台模式运行方便看输出c11是 qttelnet 常见的最低标准要求如果你的 Qt 版本较新可以改成c14或c17但不要低于 c11。2.3 第一个可交互会话的完整代码// main.cpp #include QCoreApplication #include QTimer #include QDebug #include qttelnet.h int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); QTelnet client; // 连接成功后自动发送一条查询命令 QObject::connect(client, QTelnet::connected, [client]() { qDebug() 已连接到远端设备; client.sendData(status\r\n); // 注意行尾用 \r\n多数 Telnet 服务端要求 }); // 收到数据后直接打印到控制台 QObject::connect(client, QTelnet::readyRead, [](const QByteArray data) { qDebug() 收到回显: data; }); QObject::connect(client, QTelnet::error, [](QAbstractSocket::SocketError err) { qDebug() 连接出错错误码: err; }); // 5 秒后自动退出避免程序一直挂着 QTimer::singleShot(5000, app, QCoreApplication::quit); client.connectToHost(192.168.1.100, 23); // 替换成你的设备地址和端口 return app.exec(); }这段代码的逻辑说明先实例化QTelnet对象然后通过信号槽把「连接成功」「收到数据」「出错」三个关键事件接出来。连接成功后立刻发一条status命令这是调试时最常用的手法——先确认链路通不通再谈业务。sendData的参数里带了\r\n因为很多 Telnet 服务端按行解析命令只发\n可能不触发执行。参数说明connectToHost的第一个参数是目标 IP第二个是端口Telnet 默认 23但很多设备会改成 2323 或 8023 之类的非标端口以实际为准。QTimer::singleShot里的 5000 毫秒是保险丝防止在自动化脚本里程序卡死。2.4 编译与运行时的两个关键检查点编译命令用 qmake 的标准流程qmake minitelnet.pro make -j4 ./minitelnet如果编译报错说找不到QTelnet类先检查INCLUDEPATH是否真的指向了头文件所在目录再检查头文件里的类名是否被命名空间包了一层。有些修订版会把类放进QTELNET_NAMESPACE之类的宏里这时候你要么在 pro 文件里加DEFINES QTELNET_NAMESPACE要么在代码里用完整的命名空间前缀。运行时的检查点如果程序启动后没有任何输出先确认目标设备的 Telnet 服务是否真的在监听。可以在另一台机器上用nc -vz 192.168.1.100 23测一下端口通不通。如果端口通但connected信号不触发可能是 qttelnet 内部在等 Telnet 协议协商完成有些服务端会先发一堆 IAC 选项qttelnet 需要处理完这些才认为连接就绪。这时候可以抓包看一下协商过程或者临时把 qttelnet 里的协商超时调短。3. 参数怎么调超时、缓冲与 Telnet 协商的实战配置3.1 连接超时和读超时不是一回事qttelnet 内部通常有两个超时相关的东西一个是 socket 连接超时一个是读数据时的等待超时。连接超时决定你调用connectToHost后多久算失败读超时决定你等一个完整响应包等多久。我见过有人把这两个混为一谈结果设备明明在线却因为读超时设得太短把分片到达的数据当成超时丢掉了。常见做法是连接超时设 3 到 5 秒读超时设 200 到 500 毫秒。读超时不能设太长否则界面会卡也不能设太短否则一个稍大的回显包会被截断。如果你发现回显总是缺尾巴先把读超时翻倍试试。// 假设 qttelnet 提供了 setConnectTimeout 和 setReadTimeout client.setConnectTimeout(5000); // 单位毫秒 client.setReadTimeout(300); // 单位毫秒参数说明这两个 setter 不是所有修订版都有如果你的版本里没有就需要在 socket 层面自己控制或者改 qttelnet 源码里的默认值。改源码时注意超时逻辑通常写在readyRead的槽函数或者一个内部的QTimer里。3.2 缓冲区大小对粘包和拆包的影响Telnet 是流式协议没有消息边界。qttelnet 收到数据后可能一次给你一个完整回显也可能分两次给。如果你的业务逻辑假设「一次 readyRead 就是一个完整响应」那在回显较长或网络抖动时一定会翻车。我一般会在业务层加一个简单的缓冲拼接逻辑用一个QByteArray累积数据然后按结束符比如\r\n或#提示符切分。下面是一个可抄的缓冲处理片段QByteArray buffer; QObject::connect(client, QTelnet::readyRead, [buffer](const QByteArray data) { buffer.append(data); // 按行切分处理粘包 int idx; while ((idx buffer.indexOf(\r\n)) ! -1) { QByteArray line buffer.left(idx); buffer.remove(0, idx 2); qDebug() 完整行: line; // 在这里做命令响应匹配 } });逻辑说明每次收到数据先追加到buffer然后用indexOf找行结束符。找到就切出一行剩下的留在 buffer 里等下次数据。这样即使服务端把一行拆成三个 TCP 包发过来也能正确拼回一行。参数说明结束符不一定是\r\n有些设备用\n有些用\0。最稳妥的办法是先抓一次真实回显看它的行尾到底是什么。如果回显里包含二进制数据就不能按文本行切得按长度字段或固定分隔符来。3.3 Telnet 协商选项要不要管标准 Telnet 协议在正式数据之前会有一轮 IAC 协商比如IAC WILL ECHO、IAC DO SUPPRESS_GO_AHEAD之类。qttelnet 一般会自动回应这些协商但不同设备的协商行为差异很大。有的设备发完协商就安静了有的设备会一直发IAC DO TIMING_MARK等你回应。如果你发现连接建立后收到一堆乱码先确认这些乱码是不是 IAC 序列。IAC 的字节值是 255后面跟命令码。qttelnet 通常会在内部过滤掉这些但如果你的版本过滤不干净就会在readyRead里看到它们。解决办法是在业务层加一个简单的过滤遇到 255 开头的三字节序列就跳过。注意不要轻易禁用 qttelnet 的协商逻辑除非你确定目标设备完全不发 IAC。禁用后如果设备发了协商请求而你没回应有些设备会直接断开连接。4. 避坑记录qttelnet-2.1_1 集成时最容易翻车的五个地方4.1 现象连接成功信号不触发但端口明明是通的原因qttelnet 把「TCP 连接建立」和「Telnet 会话就绪」分成了两个阶段。有些实现里connected信号是在 Telnet 协商完成后才发的而不是 TCP 三次握手完成就发。如果服务端在协商阶段卡住信号就永远不来。解决先用nc或抓包确认 TCP 层是否已建立。如果 TCP 通了但信号不来检查 qttelnet 源码里connected的发射位置必要时改成在 socket 的connected信号里直接发射跳过协商等待。4.2 现象中文回显乱码英文正常原因Telnet 本身不定义字符编码qttelnet 默认按 Latin-1 或本地编码处理字节流。如果设备发的是 UTF-8 中文而 qttelnet 按单字节编码转成 QString就会乱码。解决不要在 qttelnet 内部做编码转换让它原样返回QByteArray然后在业务层用QString::fromUtf8(data)显式解码。如果设备用的是 GBK就换成QTextCodec::codecForName(GBK)。关键是保持字节流的原始性编码转换放在最后一步。4.3 现象程序运行一段时间后内存持续上涨原因qttelnet 内部可能有一个接收缓冲区如果readyRead的槽函数处理太慢或者你在槽里做了阻塞操作缓冲区就会堆积。另一个常见原因是信号槽连接没有断开导致对象被反复创建却无法释放。解决检查readyRead槽里有没有QEventLoop或sleep之类的阻塞调用。如果有改成异步处理。另外在对象销毁前显式调用disconnect或者用QObject::connect的Qt::UniqueConnection标志防止重复连接。4.4 现象发送命令后设备没反应但手动用 Telnet 工具是好的原因行尾符不对。Windows 下的 Telnet 工具默认发\r\n而 qttelnet 的sendData如果你只传了命令字符串没带行尾设备就一直在等行结束符。解决在sendData的参数里显式加上\r\n。如果设备要求\n或\r就按设备手册改。不确定的时候先发一个\r\n看设备有没有回显提示符。4.5 现象多线程环境下程序随机崩溃原因qttelnet 对象是在主线程创建的但你在工作线程里调用了它的sendData或connectToHost。Qt 的 socket 对象不是线程安全的跨线程调用会出问题。解决把 qttelnet 对象移到工作线程用moveToThread然后通过信号槽跨线程通信。或者干脆在主线程做所有网络操作工作线程只处理数据。不要图省事直接跨线程调方法。5. 进阶用法把 qttelnet-2.1_1 改造成可复用的调试组件5.1 封装一个带自动重连和日志落盘的 Telnet 客户端类直接裸用 qttelnet 的QTelnet类在简单场景够用但一旦要做长期运行的调试工具就需要加自动重连、心跳保活和日志落盘。我一般会包一层DebugTelnetClient内部持有QTelnet对象对外暴露start()、sendCommand()和onResponse()三个接口。class DebugTelnetClient : public QObject { Q_OBJECT public: explicit DebugTelnetClient(QObject *parent nullptr) : QObject(parent), m_retryTimer(new QTimer(this)) { m_retryTimer-setInterval(3000); connect(m_retryTimer, QTimer::timeout, this, DebugTelnetClient::tryConnect); connect(m_telnet, QTelnet::readyRead, this, DebugTelnetClient::onData); connect(m_telnet, QTelnet::disconnected, this, [this]() { m_retryTimer-start(); // 断开后自动重连 }); } void start(const QString host, quint16 port) { m_host host; m_port port; tryConnect(); } void sendCommand(const QString cmd) { m_telnet.sendData(cmd.toUtf8() \r\n); } signals: void responseLine(const QString line); private slots: void tryConnect() { m_telnet.connectToHost(m_host, m_port); } void onData(const QByteArray data) { m_buffer.append(data); int idx; while ((idx m_buffer.indexOf(\r\n)) ! -1) { QByteArray line m_buffer.left(idx); m_buffer.remove(0, idx 2); emit responseLine(QString::fromUtf8(line)); } } private: QTelnet m_telnet; QTimer *m_retryTimer; QByteArray m_buffer; QString m_host; quint16 m_port 23; };逻辑说明构造函数里把重连定时器和 qttelnet 的信号接好。start记录目标地址并立即尝试连接。onData做粘包处理按行发射responseLine信号。断开时启动重连定时器3 秒后重试。参数说明m_retryTimer的间隔 3000 毫秒可以根据网络质量调整局域网可以设 1000跨公网建议 5000 以上。m_buffer没有设上限如果回显量极大需要加一个最大长度保护超过就丢弃最旧的数据。5.2 用日志落盘做现场问题回溯调试工具最值钱的功能不是实时看而是事后查。我会在responseLine信号上再接一个槽把每行带时间戳写到文件里。QObject::connect(client, DebugTelnetClient::responseLine, [](const QString line) { QFile log(debug_session.log); if (log.open(QIODevice::Append | QIODevice::Text)) { QTextStream out(log); out QDateTime::currentDateTime().toString(yyyy-MM-dd hh:mm:ss.zzz) | line \n; } });这段代码每次收到一行就追加写文件。参数说明用Append模式避免覆盖历史日志时间戳精确到毫秒方便和抓包数据对齐。如果日志量很大不要每行都开一次文件改成在DebugTelnetClient里持有一个QFile对象在析构时统一关闭。5.3 验证改造是否成功的三个检查点改造完成后不要只看「能连上」就完事。我一般会做三个验证第一手动拔掉网线再插回看自动重连是否在 3 秒内恢复第二连续发送 100 条命令看日志文件里是否每条都有对应回显没有丢行第三用top或任务管理器看内存占用跑 10 分钟不持续上涨才算过关。这三个检查点里第二个最容易暴露粘包处理的问题。如果发现丢行先检查m_buffer的切分逻辑再检查readyRead信号有没有被阻塞。第三个检查点则能发现信号槽泄漏或缓冲区无限增长的问题。我自己在这上面踩过最狠的一次坑是忘了在disconnected信号里停掉重连定时器结果设备断电后程序每 3 秒重连一次日志文件被重连记录撑到了几个 G。后来养成一个习惯任何自动重连逻辑必须配一个最大重试次数或指数退避不能无限重试。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Flutter鸿蒙适配实战:高性能微服务路由与客户端网关设计

Flutter鸿蒙适配实战:高性能微服务路由与客户端网关设计

我在这套 Flutter 三端(Android/iOS/鸿蒙)业务组件里负责把路由层抽出来的时候,第一个现实问题并不是"怎么适配鸿蒙",而是"鸿蒙到底能不能用 Community 版本的 Flutter 桥接层顺畅跑完我们的微服务网关流量"。…

2026/10/15 19:00:56 阅读更多 →
模拟CMOS集成电路设计:课后答案的正确用法与SPICE仿真验证

模拟CMOS集成电路设计:课后答案的正确用法与SPICE仿真验证

简介:模拟集成电路设计是工程师进阶的常见难点,拉扎维所著《模拟CMOS集成电路设计》更是公认经典。这份课后答案覆盖第二版各章习题,尤其针对第二章中N沟道与P沟道场效应管漏电流随栅源电压变化、跨导与输出阻抗等基础计算,给出逐…

2026/10/15 19:00:56 阅读更多 →
PTC自限温效应的物理机制与工程应用解析

PTC自限温效应的物理机制与工程应用解析

技术解析 | 材料物理本文从材料物理学角度深入解析PTC加热器的自限温特性,涵盖PTC效应起源、居里温度机制、电阻-温度特性、自调节过程及工程应用要点。一、PTC效应概述PTC(Positive Temperature Coefficient,正温度系数)效应是指…

2026/10/15 19:00:56 阅读更多 →

最新新闻

Claude Code 使用指南:从 Node.js 环境到终端与 IDE 的完整配置

Claude Code 使用指南:从 Node.js 环境到终端与 IDE 的完整配置

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

2026/10/15 19:46:41 阅读更多 →
Redis 技术分享主线:数据结构、缓存三大问题与高可用落地

Redis 技术分享主线:数据结构、缓存三大问题与高可用落地

简介:围绕 Redis 核心技术点的分享课件,适合后端开发、运维人员及对内存数据库感兴趣的学习者快速建立知识框架。PPT 从简介、配置文件、持久化机制到常用命令逐步展开,涵盖每秒 11 万次读、8.1 万次写的高性能表现,string、hash、…

2026/10/15 19:46:41 阅读更多 →
go-gitignore:基于简单树索引的高性能 Go 免提交匹配库

go-gitignore:基于简单树索引的高性能 Go 免提交匹配库

云原生开发工具DevOps容器编排 【免费下载链接】devspace DevSpace - The Fastest Developer Tool for Kubernetes ⚡ Automate your deployment workflow with DevSpace and develop software directly inside Kubernetes. 项目地址: https://gitcode.com/gh_mirro…

2026/10/15 19:46:41 阅读更多 →
常微分方程数值解法详解:从欧拉法到有限差分法的MATLAB实战

常微分方程数值解法详解:从欧拉法到有限差分法的MATLAB实战

常微分方程的数值解法,在工程计算里几乎是绕不开的。不管是电路瞬态分析、化学反应速率模拟,还是机械系统动力学建模,最后都会落到一个“给定导数关系、给初始条件、积分出整条曲线”的问题上。MATLAB里虽然直接有 ode45、ode23 这些现成求解…

2026/10/15 19:46:41 阅读更多 →
基于 Next.js + Notion 搭建个人网站:nextjs-notion-starter-kit 配置、部署与源码原理全解析

基于 Next.js + Notion 搭建个人网站:nextjs-notion-starter-kit 配置、部署与源码原理全解析

前端 【免费下载链接】nextjs-notion-starter-kit Deploy your own Notion-powered website in minutes with Next.js and Vercel. 项目地址: https://gitcode.com/gh_mirrors/ne/nextjs-notion-starter-kit 点击查看 免费下载 本文以 readme.md 为骨架&#xff0c…

2026/10/15 19:46:41 阅读更多 →
多个传感器如何协同作战?World2Agent多传感器组合与fanout传输完整指南

多个传感器如何协同作战?World2Agent多传感器组合与fanout传输完整指南

【免费下载链接】world2agent World2Agent(W2A) is an open protocol that standardizes how Al agents perceive the real world. 项目地址: https://gitcode.com/gh_mirrors/wo/world2agent 点击查看 免费下载 World2Agent(W2A)是一个开放…

2026/10/15 19:45:41 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 0:14:36 阅读更多 →
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/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练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/15 9:49:14 阅读更多 →