深入理解 Qt 中的定时器(QTimer):从入门到实践

        在 GUI 开发、串口通信、动画刷新、任务调度等场景中,定时器是不可或缺的工具。Qt 提供了强大而易用的定时器机制,主要通过 QTimer 实现高精度的定时任务控制。本文将全面介绍 Qt 定时器的用法、分类、常见模式和注意事项。     

1. 什么是 QTimer?

           QTimer 是 Qt 提供的定时器类,可用于在一段时间之后或以固定间隔触发一个槽函数(slot)。它基于 Qt 事件循环机制,具有跨平台性强、资源开销小的优点。

2. 使用方式概览

 单次触发

QTimer::singleShot(1000, this, SLOT(doSomething()));

  • 延迟 1000 毫秒(1 秒)后触发一次 doSomething() 槽函数。
  • 可用于倒计时、动画延迟等场景。

周期性触发

QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &MyClass::updateUI);
timer->start(500);  // 每500ms触发一次
  • 循环触发,常用于定时更新界面、定时通信等。

 3. QTimer 常用 API 详解

函数

说明

start(int msec)

启动定时器,触发间隔为 msec 毫秒

stop()

停止定时器

isActive()

判断定时器是否正在运行

setInterval(int msec)

设置触发间隔(不立即生效)

setSingleShot(bool)

设置为只触发一次

remainingTime()

获取当前定时器剩余触发时间(毫秒)

timeout()

定时器到期时发出的信号

4. 常见使用模式

模式一:周期刷新界面

// 用于图像显示/温度更新/状态轮询等

m_timer = new QTimer(this);
connect(m_timer, &QTimer::timeout, this, &MyWindow::refreshDisplay);
m_timer->start(100);

模式二:串口轮询指令

connect(&m_serialTimer, &QTimer::timeout, this, &MyDevice::sendPollingCmd);
m_serialTimer.setInterval(500);
m_serialTimer.start();

模式三:防抖动(如按键)

// 启动单次定时器,仅触发一次防止频繁响应
QTimer::singleShot(300, this, SLOT(handleClick()));

模式四:定时退出或操作超时处理

// 如果 5 秒未收到响应,则视为超时
connect(&timeoutTimer, &QTimer::timeout, this, &MyApp::handleTimeout);
timeoutTimer.setSingleShot(true);
timeoutTimer.start(5000);

模式五:多个定时器同时运行

       每个QTimer实例都是独立运行的。可以在同一个类中同时启用多个定时器,分别绑定不同的槽函数。

#include "mainwindow.h"
#include 

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    // 创建第一个定时器,每秒触发
    timer1 = new QTimer(this);
    connect(timer1, &QTimer::timeout, this, &MainWindow::onTimer1Timeout);
    timer1->start(1000);  // 1秒

    // 创建第二个定时器,每两秒触发
    timer2 = new QTimer(this);
    connect(timer2, &QTimer::timeout, this, &MainWindow::onTimer2Timeout);
    timer2->start(2000);  // 2秒

    // 创建第三个定时器,每500ms触发
    timer3 = new QTimer(this);
    connect(timer3, &QTimer::timeout, this, &MainWindow::onTimer3Timeout);
    timer3->start(500);  // 0.5秒
}

MainWindow::~MainWindow() {}

void MainWindow::onTimer1Timeout() {
    qDebug() << "[Timer1] 每1秒触发";
}

void MainWindow::onTimer2Timeout() {
    qDebug() << "[Timer2] 每2秒触发";
}

void MainWindow::onTimer3Timeout() {
    qDebug() << "[Timer3] 每0.5秒触发";
}

 预期输出:

[Timer3] 每0.5秒触发
[Timer1] 每1秒触发
[Timer3] 每0.5秒触发
[Timer2] 每2秒触发
[Timer3] 每0.5秒触发
[Timer1] 每1秒触发
...

模式六:用lambda表达式简化代码

#include 
#include 
#include 

class MainWindow : public QMainWindow {
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
        // timer1 - 每1秒打印一次
        QTimer *timer1 = new QTimer(this);
        connect(timer1, &QTimer::timeout, this, [=]() {
            qDebug() << "[Lambda Timer1] 每1秒触发";
        });
        timer1->start(1000);

        // timer2 - 每2秒打印一次
        QTimer *timer2 = new QTimer(this);
        connect(timer2, &QTimer::timeout, this, [=]() {
            qDebug() << "[Lambda Timer2] 每2秒触发";
        });
        timer2->start(2000);

        // timer3 - 每3秒打印一次
        QTimer *timer3 = new QTimer(this);
        connect(timer3, &QTimer::timeout, this, [=]() {
            static int count = 0;
            qDebug() << "[Lambda Timer3] 第" << ++count << "次触发";
        });
        timer3->start(3000);
    }
};

输出:

[Lambda Timer1] 每1秒触发
[Lambda Timer2] 每2秒触发
[Lambda Timer3] 第 1 次触发
[Lambda Timer1] 每1秒触发
[Lambda Timer1] 每1秒触发
[Lambda Timer2] 每2秒触发
[Lambda Timer3] 第 2 次触发
...

模式七:多线程环境使用定时器

#include "worker.h"
#include 
//worker.cpp
Worker::Worker(QObject *parent) : QObject(parent) {
    timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, [=]() {
        emit sendLog("[Worker] 子线程定时器触发:" + QTime::currentTime().toString());
    });
}

Worker::~Worker() {}

void Worker::startWork() {
    timer->start(1000);  // 每秒触发
}

//mainwindow.cpp
#include "mainwindow.h"
#include "worker.h"
#include 
#include 

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    QThread *thread = new QThread(this);
    Worker *worker = new Worker();

    // 将 worker 移动到线程
    worker->moveToThread(thread);

    // 启动线程后,再调用 worker 的启动函数
    connect(thread, &QThread::started, worker, &Worker::startWork);

    // 从子线程发回消息
    connect(worker, &Worker::sendLog, this, [](const QString &msg) {
        qDebug() << msg;
    });

    // 程序退出时清理资源
    connect(this, &MainWindow::destroyed, thread, &QThread::quit);
    connect(thread, &QThread::finished, worker, &QObject::deleteLater);
    connect(thread, &QThread::finished, thread, &QObject::deleteLater);

    thread->start();
}

预计输出:

[Worker] 子线程定时器触发:12:34:56
[Worker] 子线程定时器触发:12:34:57
[Worker] 子线程定时器触发:12:34:58
...

5. 结合 UI 控件的定时操作示例

示例:按钮点击后倒计时 5 秒禁用

connect(ui->pushButton, &QPushButton::clicked, this, [this]() {
    ui->pushButton->setEnabled(false);
    QTimer::singleShot(5000, this, [this]() {
        ui->pushButton->setEnabled(true);
    });
});

示例:每秒更新当前时间

QTimer *clock = new QTimer(this);
connect(clock, &QTimer::timeout, this, [this]() {
    ui->label->setText(QTime::currentTime().toString());
});
clock->start(1000);

6. 按次数发送指令

设计目标:

按指定次数循环发送指令,通常适用于固定次数的任务(如重试机制、逐步操作等)。

解决方案:使用 QTimer + 计数器

       可以通过 QTimer 设置定时器并结合计数器进行次数控制。当计数器达到指定次数时停止发送指令。

设计步骤:

  1. 初始化计数器:设定发送次数。
  2. 使用 QTimer 设置发送频率:设定每次发送之间的时间间隔。
  3. 定时器回调函数:每次触发时发送指令并更新计数器。
  4. 停止条件:当计数器达到预定次数时,停止定时器。

示例代码:

#include 
#include 

class CommandSender : public QObject {
    Q_OBJECT

public:
    CommandSender(QObject* parent = nullptr) : QObject(parent), m_count(0), m_maxCount(5) {
        // 设置定时器,每500ms触发一次
        m_timer.setInterval(500);
        connect(&m_timer, &QTimer::timeout, this, &CommandSender::sendCommand);
    }

    void start() {
        // 开始发送命令
        m_count = 0;
        m_timer.start();
    }

private slots:
    void sendCommand() {
        if (m_count < m_maxCount) {
            qDebug() << "发送命令:" << m_count + 1;
            m_count++;
        } else {
            // 发送完毕,停止定时器
            m_timer.stop();
            qDebug() << "所有命令已发送完毕。";
        }
    }

private:
    int m_count;  // 当前发送次数
    const int m_maxCount;  // 最大发送次数
    QTimer m_timer;  // 定时器
};

// 使用示例
CommandSender sender;
sender.start();

设计思路:

  • QTimer:用来控制命令发送的频率(定时触发)。
  • 计数器:控制发送次数,达到最大次数时停止发送。
  • 高效性:无需手动管理线程,Qt 的事件循环会确保在合适的时间触发。

 7. 定时发送指令

设计目标:

       按照固定的时间间隔重复发送指令,适用于周期性任务(如心跳包、定时状态报告、定期同步数据等)。

解决方案:使用 QTimer + 固定间隔

       与按次数发送指令类似,定时发送指令可以使用 QTimer 来实现。不同的是,定时发送需要持续不断地发送直到用户停止或达到某个条件。

设计步骤:

  1. 使用 QTimer 设置固定间隔:指定发送的时间间隔。
  2. 定时器回调函数:每次触发时发送指令。
  3. 停止条件:可以通过外部信号或条件停止定时器(例如:用户手动停止或达到某种逻辑条件)。

示例代码:

#include 
#include 

class PeriodicCommandSender : public QObject {
    Q_OBJECT

public:
    PeriodicCommandSender(QObject* parent = nullptr) : QObject(parent) {
        // 设置定时器,每2秒触发一次
        m_timer.setInterval(2000);
        connect(&m_timer, &QTimer::timeout, this, &PeriodicCommandSender::sendPeriodicCommand);
    }

    void start() {
        m_timer.start();
    }

    void stop() {
        m_timer.stop();
    }

private slots:
    void sendPeriodicCommand() {
        qDebug() << "定时发送命令";
    }

private:
    QTimer m_timer;  // 定时器
};

// 使用示例
PeriodicCommandSender sender;
sender.start();

设计思路:

  • QTimer:用来控制周期性任务的执行时间间隔。
  • QTimer::timeout 信号:每到指定时间间隔触发,持续发送指令。
  • 灵活停止:可以通过 stop() 方法手动停止定时器,也可以根据外部条件或任务完成情况进行停止。

扩展功能:

  • 动态调整间隔:可以动态修改 setInterval() 设置的时间间隔。
  • 发送次数限制:如果需要限制发送的次数,可以结合计数器来实现。

8. 按次数与定时发送结合的方案

        有时可能会遇到按次数和定时发送指令的结合需求,例如每隔一段时间发送指定次数的指令。此时,我们可以使用 嵌套定时器递归定时器

设计步骤:

  1. 外层定时器:控制发送间隔。
  2. 内层计数器:控制每个周期内发送的次数。
  3. 控制条件:当计数达到最大次数时,停止定时器。

示例代码:

#include 
#include 

class TimedRepeatingSender : public QObject {
    Q_OBJECT

public:
    TimedRepeatingSender(QObject* parent = nullptr) : QObject(parent), m_count(0), m_maxCount(5), m_repeatInterval(1000) {
        // 外层定时器,用于每1秒重复触发一次
        m_timer.setInterval(m_repeatInterval);
        connect(&m_timer, &QTimer::timeout, this, &TimedRepeatingSender::sendCommandPeriodically);
    }

    void start() {
        m_count = 0;
        m_timer.start();
    }

private slots:
    void sendCommandPeriodically() {
        if (m_count < m_maxCount) {
            qDebug() << "发送命令,次数:" << m_count + 1;
            m_count++;
        } else {
            // 达到最大次数后停止定时器
            m_timer.stop();
            qDebug() << "所有命令已发送完毕。";
        }
    }

private:
    int m_count;
    const int m_maxCount;
    const int m_repeatInterval; // 每次发送之间的间隔时间
    QTimer m_timer;
};

// 使用示例
TimedRepeatingSender sender;
sender.start();

设计思路:

  • 外层定时器:控制定时触发,周期性地执行发送任务。
  • 内层计数器:在每个周期内控制发送的次数。
  • 灵活控制:通过 m_maxCount 控制最大发送次数,可以动态调整。

9.注意事项与常见问题

 定时器无效的常见原因:
•    没有进入 Qt 的 事件循环;
•    定时器对象被提前析构;
•    使用 QTimer::singleShot() 时 lambda 没绑定有效 this;
•    在子线程中未启用事件循环。
精度问题:
•    QTimer 基于系统计时器,有一定抖动;
•    对于高精度实时任务,不推荐使用 QTimer,可使用多线程 + QElapsedTimer 代替。

总结

场景

推荐做法

单次延迟任务

QTimer::singleShot()

周期执行/轮询

QTimer 实例 + start()

多线程中使用

moveToThread() + 事件循环

精准测量执行耗时

使用 QElapsedTimer

复杂控制需求(组合)

多个 QTimer 实例 + 状态机控制

1. 按次数发送指令

  • 使用 QTimer 控制时间间隔,配合计数器控制次数。
  • 在次数达到时,停止定时器。

2. 定时发送指令

  • 使用 QTimer 持续触发指令,控制发送的时间间隔。
  • 可通过外部条件停止。

3. 按次数与定时结合

  • 使用嵌套定时器,外层定时器控制间隔,内层计数器控制次数。

        这两种方案都可以通过 QTimer 来实现,灵活控制定时任务的执行时间和频率。根据具体的应用场景,选择合适的方式进行设计,确保代码简洁、可维护且高效。如果有更具体的需求或场景,可以进一步细化和优化设计方案。

你可能感兴趣的:(单片机,qt,运维,stm32,定时器,QTimer,嵌入式软件)