MATLAB GUI定时器实战:用timer实现实时时钟与可重置倒计时

简介:这是一份面向MATLAB GUI初学者的定时任务功能案例资源,针对需要掌握timer函数与TimerFcn回调机制、实现动态界面更新的开发者。资源包含两个可直接运行的Demo:第一个Demo每间隔1秒获取系统时间并在编辑框实时刷新;第二个Demo演示从5开始每秒递减至0的倒计时效果,完整覆盖定时器创建、回调函数设置、启动与停止等关键操作。压缩包共4个文件,以2个.m函数源码和2个.fig界面文件组成,整体大小仅21KB,结构紧凑便于对照学习。目前已有2483人学习下载,适合通过实际代码理解MATLAB GUI中定时任务的实现思路。借助这两个示例,读者可快速掌握TimerFcn在界面动态显示中的典型用法,包括定时器的启动、停止与删除操作,并能迁移到数据采集、状态监控等需要周期性刷新的类似场景中。

1. 一分钟看懂这个项目:为什么选 timer 而不是 while 循环

在 MATLAB 里做 GUI 实时刷新,大部分人第一反应是while true ... pause(0.1)死循环,结果界面转圈、按钮点不动、任务管理器里内存疯涨。这个项目反直觉的地方在于:它用timer函数挂一个TimerFcn回调,把周期性任务从主线程里剥出去,系统时间和倒计时各自占一个定时器,界面流畅度完全不受刷新频率影响。项目本身是个典型的 MATLAB GUI + 定时任务 Demo,核心就两件事:实时呈现系统时间、实现一个可重置的倒计时器。适合两类人——刚接触 MATLAB 开发语言、想找一个能把控件布局和回调机制一次性讲透的入门样板;以及做数据采集、监控类小工具,需要周期性刷新 UI 文本的开发者。工程不大,但 timer 的创建、启停、回调传参、跨线程访问控件这几个关键点全带上了,属于拆开就能当模板用的那种代码。

2. GUI 与定时机制:界面结构、timer 参数和回调的数据通路

2.1 界面布局:两个显示区加三个控制按钮

先说界面。这个 Demo 用的是指南针式的布局:顶部一个显示系统时间的文本框,中间一个显示倒计时的文本框,底部一排按钮控制倒计时的启动、暂停和重置。在 App Designer 里对应一个GridLayout套两个Label、一个ButtonGroup,在 GUIDE 里就是两个静态文本框加三个按钮,本质没有区别。

我习惯用 App Designer 版本讲,因为它的回调语法更接近现代 MATLAB。每个组件都有自己的 Tag,比如LampTime、LblCountdown、BtnStart、BtnPause、BtnReset。界面是死的,活的是背后的定时器。

项目内目录(以 App Designer 为例) ClockApp.mlapp % 主程序,包含所有方法和回调 ClockApp.m % 导出后的纯代码版本,逻辑相同 README.txt % 运行说明 docs/screenshot.png % 运行效果图

运行入口就一行:ClockApp;或者直接双击.mlapp文件。注意.mlapp是压缩包格式,没有装 App Designer 的老版本 MATLAB 打不开,但ClockApp.m这种纯 m 文件是全版本通用的。如果拿到的是.m版本,直接run('ClockApp.m')就行。

2.2 timer 创建与参数选型:fixedRate 与 fixedSpacing 的区别

timer 对象是 MATLAB 自带的一个定时任务类,它不是靠线程轮询实现,而是底层注册了回调队列,到点由 MATLAB 调度到TimerFcn。创建语法很简洁:

t = timer('Name', 'SystemClock', ... 'ExecutionMode', 'fixedRate', ... 'Period', 1.0, ... 'TimerFcn', @(src, evt) updateClock(app));

关键参数就三个:ExecutionMode、Period、TimerFcn。ExecutionMode选fixedRate表示每个Period秒执行一次回调,只要回调本身执行时间小于周期,两次触发之间的间隔就是恒定的;如果回调体很耗时,选fixedRate可能导致回调排队。选fixedSpacing则是回调结束之后再隔Period秒执行下一次,适合倒计时这种对“回调运行时间本身不可控”的场景。我一般系统时间刷新用fixedRate,倒计时用fixedSpacing,这样即便回调里有些复杂运算,也不会出现时间跳变。

TimerFcn的回调签名固定是(src, evt),src是 timer 对象本身,evt是事件数据结构。里面要用到 GUI 的控件,直接靠闭包捕获app对象——这是 App Designer 里最常见的写法,简单直接,但要考虑窗口关闭后回调仍被触发的情况,第 4 章专门讲这个问题。

2.3 回调的数据通路:从 timer 到 UI 控件的三种姿势

TimerFcn 里更新控件文本,常见有三种做法。第一种是上面说的闭包捕获整个 app 对象,代码如下:

tmr = timer('TimerFcn', @(src, evt) updateDisplay(app));

第二种是把组件句柄作为参数传进回调:

tmr = timer('TimerFcn', @(src, evt) updateLabel(app.LampTime));

第三种是保存 timer 句柄到 app 的属性里,回调里通过app反向查找组件。三种写法对最终效果没区别,但后两种更稳——它们不会因为闭包意外捕获了一整个 app 导致内存长期被占用。我推荐第二种,修改哪个组件一目了然,排查问题的时候不用去翻整个回调上下文。

3. 核心模块拆解:系统时间刷新与倒计时两套逻辑的实现

3.1 系统时间实时呈现:datetime 加 fixedRate 定时器

先看系统时间模块。整个模块就两步:启动时创建定时器,回调里格式化时间字符串写到标签文本。这里贴的是最接近源码的完整逻辑:

properties (Access = private) ClockTimer % 系统时间定时器 CountdownTimer % 倒计时定时器 CountdownTarget % 倒计时目标时刻 end methods (Access = private) function createClockTimer(app) % fixedRate 保证每秒走一次,回调体很轻,不会拥塞 app.ClockTimer = timer( ... 'Name', 'ClockTimer', ... 'ExecutionMode', 'fixedRate', ... 'Period', 1.0, ... 'TimerFcn', @(src, evt) app.updateClock()); start(app.ClockTimer); end function updateClock(app) % datetime('now') 比 now 精度高,且支持直接格式化 t = datetime('now'); str = datestr(t, 'yyyy-mm-dd HH:MM:SS'); app.LampTime.Text = str; end end

datetime('now')返回的是一个 datetime 对象,底层是双精度时间戳,精度到微秒级,显示的时候通过datestr指定格式。注意格式串里HH是 24 小时制,hh是 12 小时制,一个小写字母区别很大。如果界面想显示毫秒,把格式串改成yyyy-mm-dd HH:MM:SS.FFF即可,但Period要缩到0.1,不然毫秒信息就浪费了。

启动流程是在startupFcn里调createClockTimer,窗口关闭时另做一个closeRequestFcn把 timer 删掉。这里有个小设计:Period设成1.0,但执行到datestr其实只有几毫秒,所以fixedRate完全够用。如果哪天你把系统时间刷新改成读网络校时接口,回调体可能要到几百毫秒,那就建议切换成fixedSpacing,否则会出现积压回调全部一起触发,界面跟抽风一样。

3.2 倒计时逻辑:目标时刻减当前时刻,每秒算一次差

倒计时模块是这个项目里算法最多的一处。这里用一个固定的目标时刻,实际项目里可以改成界面输入框让用户填。核心逻辑是先存一个CountdownTarget,每次回调把当前时间拉出来算差值:

function startCountdown(app, target) app.CountdownTarget = target; if ~isempty(app.CountdownTimer) && isvalid(app.CountdownTimer) stop(app.CountdownTimer); delete(app.CountdownTimer); end % 倒计时不需要高刷新率,0.5 秒一次足够,视觉在秒级是平滑的 app.CountdownTimer = timer( ... 'Name', 'CountdownTimer', ... 'ExecutionMode', 'fixedSpacing', ... 'Period', 0.5, ... 'TimerFcn', @(src, evt) app.refreshCountdown()); start(app.CountdownTimer); end function refreshCountdown(app) remain = app.CountdownTarget - datetime('now'); if remain <= seconds(0) app.LblCountdown.Text = '倒计时结束'; stop(app.CountdownTimer); beep; % 结束时提醒一声 return; end % 把 duration 对象转成易读的时:分:秒 文本 remainText = char(remain); app.LblCountdown.Text = remainText; end

CountdownTarget - datetime('now')的结果是duration类型,char()会把它格式化成HH:MM:SS这样的文本。这里要留神:remain是带小数秒的,比如01:23:45.678,界面会闪小数点后的数值。想只显示整秒,先做fix(remain)再转 char:

remain = fix(app.CountdownTarget - datetime('now')); app.LblCountdown.Text = char(remain);

上面代码里倒计时结束的判断用remain <= seconds(0),这里用seconds(0)构造一个零时长,比直接和0比较可读性好,也算是一个小习惯。beep在 MATLAB 里是内置提示音,GUI 场景下有声音反馈,用户能立刻感知到“到点了”,比单纯变文字强。

3.3 两个定时器的协同与互斥:启停与重置的状态机

这个项目里系统时钟定时器和倒计时定时器是互不干扰的两个对象,但它们的启停状态影响整个界面的交互逻辑。开发者对按钮回调做了三态控制:

methods (Access = public) function onStart(app) % 把目标时间设成当前时刻加 10 分钟,演示倒计时 target = datetime('now') + minutes(10); app.startCountdown(target); app.BtnStart.Enable = 'off'; app.BtnPause.Enable = 'on'; app.BtnReset.Enable = 'on'; end function onPause(app) % 暂停不是清空,是记录剩余时长,等再次点击继续时从剩余值开始 if strcmp(app.CountdownTimer.Running, 'on') stop(app.CountdownTimer); % 存当前剩余时长,resume 时用它重新设目标时刻 app.PausedRemain = app.CountdownTarget - datetime('now'); else app.CountdownTarget = datetime('now') + app.PausedRemain; start(app.CountdownTimer); end end function onReset(app) stop(app.CountdownTimer); app.LblCountdown.Text = '00:00:00'; app.BtnStart.Enable = 'on'; app.BtnPause.Enable = 'off'; app.BtnReset.Enable = 'off'; end end

暂停的细节值得参考:不是简单stop(timer)完事,而是先把剩余时长保存在一个属性里,再次启动时把CountdownTarget重算成“当前时刻 + 剩余时长”,这样用户点暂停再继续,时间不会因为没有减去暂停期间的空转时间而出现跳变。很多半成品 Demo 都挂在这一点上——暂停期间定时器虽然停了,但目标时刻还在往前走,恢复后发现倒计时少了一段,这就是典型的“暂停没存状态”的翻车。

系统时间定时器则始终保持运行,不参与状态切换。它俩唯一的交集在关窗:closeRequestFcn里要同时删除两把 timer,否则 MATLAB 的timer对象会残留在工作区里,反复开关窗口后内存越占越多。

4. 常见问题与避坑指南:TimerFcn 挂在 GUI 上最容易踩的五个坑

4.1 回调里访问控件报 “Invalid or deleted object”

  • 现象:程序跑起来后几秒,命令行报错,提示访问的控件对象无效或被删除,有时界面直接卡死。
  • 原因:TimerFcn 的回调是异步触发的,窗口关闭时控件被系统销毁,但定时器还没来得及停,回调在关窗间隙访问了已经销毁的句柄。这是 timer + GUI 最常见的死法。
  • 解决:在closeRequestFcn里先删 timer 再执行delete(app)。顺序必须是“先停定时器,再关窗口”,不能反过来。再用isvalid做一层保护,回调开头判断一下:
function updateClock(app) if ~isvalid(app.LampTime) return; end app.LampTime.Text = datestr(datetime('now'), 'yyyy-mm-dd HH:MM:SS'); end

4.2 时间显示跳秒 / 刷新不连续

  • 现象:系统时间刷新不是每秒平滑走动,有时候一秒跳两秒,有时候两秒走一次。
  • 原因:ExecutionMode选成fixedSpacing,而回调本身执行要花时间,导致“执行时长 + 间隔”累计变长。或者Period设成 1 但回调体里做重活,比如每次都读文件、查数据库。
  • 解决:把创建定时器的代码检查一遍,确定fixedRate后面Period是精确的 1.0。回调体里只做控件赋值和文本格式化,不要放任何 I/O 操作。若真要写日志,用一个累加器攒够 N 次再批量写。

4.3 倒计时结束后显示负数时间

  • 现象:倒计时到 0 之后继续跑,显示-00:00:23这种诡异文本。
  • 原因:判断终止的条件写成了remain == 0,而datetime('now')的精度在微秒级,永远不可能精确命中 0 秒,于是跨过零点继续执行。
  • 解决:用<= seconds(0)判断,并把结束后的操作放进同一个分支里。最优做法是结束分支里直接stop+ 改文本 +beep三连,别留到下一个回调再处理。

4.4 关闭窗口后 timer 还在运行,MATLAB 无法退出

  • 现象:窗口关了,但 MATLAB 命令行显示 timer 队列仍存在,clear all都清不干净,最后只能强退 MATLAB。
  • 原因:timer 对象创建后被存储在 app 的属性里,窗口删除时属性也被销毁,但 MATLAB 对 timer 的引用计数不一定立即归零。有时用户在工作区手动创建了同一个 timer,产生双重实例。
  • 解决:在closeRequestFcn里显式stop+delete所有定时器。推荐写在单独的cleanupTimers(app)方法里,防止忘删。经验是每次创建 timer 都用Name做唯一标识,排错时timerfindall一眼就能看出哪些是孤儿。

4.5 暂停恢复后倒计时凭空少了一段

  • 现象:倒计时 10 分钟,走 3 分钟后点暂停,喝了口水回来点继续,直接显示 4 分钟,中间空转的 20 秒被扣掉了。
  • 原因:暂停时只停了定时器,没有记录“剩余时长”,恢复时直接start(app.CountdownTimer),而目标时刻是创建时固定好的,永远在倒计时,暂停期间它也在“脑补”走字。
  • 解决:按 3.3 节的方案,暂停时把(目标时刻 - 当前时刻)存下来,继续时把目标时刻重算成当前时刻 + 剩余时长。这条坑在几乎所有 timer 类工具里都有,写的时候多留一个属性的事,忘了就是返工。

5. 二次开发改造:从显示 Demo 变成一个正经的桌面小工具

代码拿到手之后,有三条改造路径,从易到难。第一条是把倒计时目标时刻从写死改成界面输入。在界面上加一个EditField和一个DatePicker,用户自己填日期时间,然后调startCountdown。改动量很小:只改按钮回调里target那一行,从datetime('now') + minutes(10)换成:

target = datetime(app.DatePicker.Value, 'Format', 'yyyy-MM-dd HH:mm:ss');

第二条是增加一个时间记录面板。把每次倒计时结束的时刻和时间戳写到日志文件,做法是在refreshCountdown的结束分支里加三行:

fid = fopen('timer_log.txt', 'a'); fprintf(fid, '%s, 倒计时结束\n', datestr(datetime('now'), 'yyyy-mm-dd HH:MM:SS')); fclose(fid);

这套代码同样可以反向用在系统时间刷新里,做每个整点的记录上报,稍加扩展就是个小型的定时任务打卡工具。

第三条是把系统时钟从“单行文本”升级成模拟钟表。加一个axes控件,画表盘和指针,每帧回调里用clock取时分秒,按 30 度 / 6 度算指针角度。这个属于视觉改造,代码量要多一截,但核心定时机制不变,掌握了TimerFcn之后只是多画几根 line 的事。

我自己的习惯是把startupFcn里所有 timer 创建都收敛到initTimers(app)一个函数里,窗口 close 时统一走cleanupTimers(app)。改任何一个定时器不需要去翻别的回调,也不会出现定时器漏停的问题。从那以后我每次搭 MATLAB GUI,第一件事就是先把这两行框架写出来,再往里面填控件逻辑。这个项目最大的价值也在这——它是一个能直接运行、能看清 timer 全生命周期的完整闭环,比你从文档里零散读到的什么时候stop、什么时候delete要直观得多。希望帮到你。

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

相关新闻

MSA存储入门指南:从SAN协议到选型场景的全面解析

MSA存储入门指南:从SAN协议到选型场景的全面解析

简介&#xff1a;这份文档面向备考H3C认证工程师-MSA存储&#xff08;H3CNE-MSA&#xff09;的技术人员与H3C代理服务工程师&#xff0c;聚焦MSA存储设备基础配置与维护知识&#xff0c;帮助读者在有限时间内系统梳理考点、检验掌握程度。资源为1个docx文件&#xff0c;压缩包约…

2026/10/20 19:39:14 阅读更多 →
C语言大作业高分实践:工程结构、内存安全与评分对齐

C语言大作业高分实践:工程结构、内存安全与评分对齐

简介&#xff1a;这是一份面向高校C语言初学者与课程设计学生的高分期末大作业实战项目&#xff0c;完整实现了一个具备学生端与管理员双角色的学生成绩管理系统。资源包含可直接编译运行的C/Qt源码&#xff08;含18个.h头文件与18个.cpp实现文件&#xff09;、图形界面.ui文件…

2026/10/20 19:39:14 阅读更多 →
Redis Java客户端选型与实战:Jedis、Lettuce、Redisson对比

Redis Java客户端选型与实战:Jedis、Lettuce、Redisson对比

1. 先搞清楚需求&#xff0c;再选客户端写Java应用连Redis&#xff0c;第一步不是去翻依赖、复制代码&#xff0c;而是先想清楚&#xff1a;你的业务到底需要Redis做什么&#xff0c;以及你的应用是什么形态。这话可能听起来像废话&#xff0c;但我在实际项目里见过太多人一上来…

2026/10/20 19:39:14 阅读更多 →

最新新闻

模型量化如何影响推理性能?实战解析原理、选型与问题排查

模型量化如何影响推理性能?实战解析原理、选型与问题排查

在AI推理优化这条路上&#xff0c;量化是我见过投入产出比最高的手段。很多人以为量化就是把模型从FP32变成INT8&#xff0c;体积缩小、推理变快&#xff0c;但实操后才发现&#xff0c;AI模型量化对推理性能的影响并不只有正向&#xff1a;有时速度反而下降&#xff0c;精度却…

2026/10/20 20:20:51 阅读更多 →
基于Simulink的23自由度分布式电驱动整车动力学模型搭建与仿真

基于Simulink的23自由度分布式电驱动整车动力学模型搭建与仿真

做分布式电驱动车辆的控制&#xff0c;最头疼的问题就是你手上的整车模型到底该建多细。我这次在Matlab Simulink里完整搭了一套23自由度整车动力学模型&#xff0c;把车身六个方向的运动、四个车轮的垂向跳动、车轮旋转、转向系统、以及分布式驱动链条的扭转自由度全部拉进来了…

2026/10/20 20:20:51 阅读更多 →
用JSON和Python命令行构建个人技能管理工具:从数据建模到实践

用JSON和Python命令行构建个人技能管理工具:从数据建模到实践

1. 为什么需要技能管理工具&#xff1a;需求拆解与设计取舍上周末想更新简历&#xff0c;结果发现我根本说不清“现在到底会什么”。十几项技能分散在旧简历、脑图、收藏夹和一摞笔记本里&#xff0c;有的早已过期&#xff0c;有的只停留在“看过教程”的阶段&#xff0c;还有的…

2026/10/20 20:20:51 阅读更多 →
企业微信回调验证与AES加解密:.NET开发者接入指南

企业微信回调验证与AES加解密:.NET开发者接入指南

接手过几个企业微信自建应用的项目&#xff0c;每次新人对接时几乎都会卡在同一个地方&#xff1a;管理后台保存“接收消息服务器”配置时提示URL验证失败&#xff0c;或者明明验证过了&#xff0c;消息却一直推送不进来。这个环节看着只是填几个参数&#xff0c;背后实际上是一…

2026/10/20 20:20:51 阅读更多 →
vLLM Prefix Caching 原理:如何用 Paged KV 消除重复计算

vLLM Prefix Caching 原理:如何用 Paged KV 消除重复计算

先说一个我遇到过的情况。某个线上推理服务&#xff0c;所有请求都带着同一段两千多字的 system prompt&#xff0c;并发一上来&#xff0c;GPU 利用率看着很高&#xff0c;但真正对用户有用的计算没多少——大部分算力在一遍遍重复计算同一段内容的 K 和 V。很长一段时间里&am…

2026/10/20 20:20:51 阅读更多 →
用Design.md构建符合产品风格的原型设计:设计令牌实战指南

用Design.md构建符合产品风格的原型设计:设计令牌实战指南

上周跟某团队聊原型评审&#xff0c;对方把三个页面的设计稿拍在我面前&#xff0c;主色蓝就出现了五种&#xff0c;没有两个颜色值相同。评审会开了一个小时&#xff0c;快一半时间在争论“这个蓝到底是不是我们品牌那个蓝”。原型阶段最让人心累的从来不是画得快不快&#xf…

2026/10/20 20:19:47 阅读更多 →

日新闻

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/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

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

如果你的信息流最近也天天刷到“机器人大脑”这个词&#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 阅读更多 →