文章

QTimer

QTimer 可实现周期性或延时任务,常用于界面刷新、按钮冷却、定时轮询等场景,通过 timeout 信号驱动逻辑执行,依赖事件循环。

QTimer

QTimer

QTimer 是 Qt 框架中提供的一个用于事件驱动定时操作的类,属于 QObject 派生类。它常用于定时更新 UI、实现延时处理、周期性任务执行等功能。

基本用法

周期性定时器(循环触发)

1
2
3
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &MyClass::onTimeout);
timer->start(1000); // 每隔 1000 毫秒触发一次

单次定时器(只触发一次)

1
2
3
4
5
QTimer::singleShot(3000, this, SLOT(onSingleShotTimeout()));
// 或者使用 lambda(推荐)
QTimer::singleShot(3000, this, []() {
    qDebug() << "3 秒后只执行一次";
});

常用成员函数

方法说明
start(int msec)启动定时器,单位毫秒
stop()停止定时器
setInterval(int msec)设置时间间隔
setSingleShot(bool)设置是否只触发一次
isActive()判断是否正在运行
interval()返回当前设置的间隔
isSingleShot()是否是单次定时器

工作机制(基于事件循环)

  • QTimer 是基于 事件循环 (QEventLoop) 的,定时器超时时会生成一个 QTimerEvent 放入事件队列。
  • Qt 会通过主线程的事件循环检测这些事件,并在时间到了后发送 timeout() 信号。
  • 所以 必须在有事件循环的线程中使用,通常是主线程或 QThreadrun() 中手动启动事件循环。

示例

每秒更新时间到 QLabel

1
2
3
4
5
6
auto *label = new QLabel(this);
auto *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, [=]() {
    label->setText(QTime::currentTime().toString());
});
timer->start(1000);

使用 QTimer 控制动画帧刷新

1
2
3
auto *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &MyWidget::update); // 调用重绘
timer->start(16); // 约等于 60 FPS

与多线程配合使用

  • 如果在子线程中使用 QTimer,需要保证该线程有运行事件循环。
  • 不能将一个 QTimer 用在它不属于的线程。

正确方式(在线程内创建 QTimer):

1
2
3
4
5
6
7
8
9
10
class WorkerThread : public QThread {
    void run() override {
        QTimer timer;
        connect(&timer, &QTimer::timeout, []() {
            qDebug() << "子线程定时任务";
        });
        timer.start(1000);
        exec(); // 启动事件循环
    }
};

QTimer 与 QObject 生命周期绑定

  • QTimer 会跟随 QObject 的生命周期自动清理,无需手动 delete。
  • QTimer::singleShot 不需要手动管理 timer 对象。

实战建议

  • UI 控制更新建议在主线程中使用 QTimer
  • 耗时任务不要放在 timeout() 槽中执行,容易阻塞事件循环。
  • 多个任务可用多个 QTimer 管理,或用一个定时器统一调度。

替代方案

方案说明
QThread::msleep()阻塞式等待,适用于后台线程
QElapsedTimer精确计时,测量耗时
QTimer::singleShot()延时执行一次任务,代替 sleep
QMetaObject::invokeMethod() + Qt::QueuedConnection实现延时调用

动画刷新示例

创建一个小球在窗口中左右移动的动画。

MovingBallWidget.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#pragma once

#include <QWidget>
#include <QTimer>

class MovingBallWidget : public QWidget {
    Q_OBJECT

public:
    explicit MovingBallWidget(QWidget *parent = nullptr);

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    QTimer timer_;     // 用于刷新动画的定时器
    int ballX_ = 0;    // 小球的横坐标
    int direction_ = 1; // 移动方向(1:右,-1:左)

private slots:
    void onTimeout(); // 每帧更新
};

MovingBallWidget.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include "MovingBallWidget.h"
#include <QPainter>

MovingBallWidget::MovingBallWidget(QWidget *parent)
    : QWidget(parent) {
    setFixedSize(400, 200);       // 设置固定窗口大小
    timer_.setInterval(16);       // 约 60FPS
    connect(&timer_, &QTimer::timeout, this, &MovingBallWidget::onTimeout);
    timer_.start();               // 启动定时器
}

void MovingBallWidget::paintEvent(QPaintEvent *) {
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setBrush(Qt::blue);
    painter.drawEllipse(ballX_, height() / 2 - 10, 20, 20); // 画小球
}

void MovingBallWidget::onTimeout() {
    // 更新小球位置
    ballX_ += direction_ * 2;
    if (ballX_ < 0 || ballX_ > width() - 20) {
        direction_ *= -1; // 碰到边界换方向
    }
    update(); // 请求重绘
}

main.cpp

1
2
3
4
5
6
7
8
9
#include <QApplication>
#include "MovingBallWidget.h"

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    MovingBallWidget w;
    w.show();
    return app.exec();
}

按钮冷却示例

发送验证码按钮冷却 60 秒。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <QPushButton>
#include <QTimer>

class CooldownButton : public QPushButton {
    Q_OBJECT

public:
    explicit CooldownButton(QWidget *parent = nullptr)
        : QPushButton("发送验证码", parent) {
        connect(this, &QPushButton::clicked, this, &CooldownButton::startCooldown);

        timer_ = new QTimer(this);
        timer_->setInterval(1000); // 每秒更新一次
        connect(timer_, &QTimer::timeout, this, &CooldownButton::updateCooldown);
    }

private slots:
    void startCooldown() {
        setEnabled(false); // 禁用按钮
        cooldown_ = 60;
        setText(QString("请等待 %1 秒").arg(cooldown_));
        timer_->start();
    }

    void updateCooldown() {
        cooldown_--;
        if (cooldown_ > 0) {
            setText(QString("请等待 %1 秒").arg(cooldown_));
        } else {
            timer_->stop();
            setText("发送验证码");
            setEnabled(true); // 恢复可点击
        }
    }

private:
    QTimer *timer_;
    int cooldown_ = 0;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <QApplication>
#include <QWidget>
#include "CooldownButton.h"

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    QWidget window;

    auto *btn = new CooldownButton(&window);
    btn->move(50, 50);
    window.resize(200, 150);
    window.show();

    return app.exec();
}
本文由作者按照 CC BY 4.0 进行授权