Qt Quick 定时技巧全攻略:从底层原理到高级应用(一)

简介: Qt Quick 定时技巧全攻略:从底层原理到高级应用

一、Qt Quick 定时基础知识(Basic Knowledge of Qt Quick Timers)

1.1 Qt Quick 定时器概览(Overview of Qt Quick Timers)

在 Qt Quick 应用程序开发中,定时器(Timers)是一种常见且实用的功能,用于在指定的时间间隔内执行或重复执行某个任务。通常,定时器都是在编程界面、动画、游戏以及物联网设备等场景中使用。

Qt Quick 提供了多种定时器类型,常见的有:

  • QTimer: 属于 Qt 的 QObject 类,并位于 QtCore 模块中。这是一个 Feature-rich 的定时器对象,用于在指定时间周期内诱发信号或启动槽函数;
  • QQuickTimer: 属于 Qt Quick 的 QML 类,位于 QtQuick 模块中。适用于 QML 场景的定时器,可以直接与 QML 中的其他对象进行交互;
  • QElapsedTimer: 属于 Qt 的 QObject 类,并位于 QtCore 模块中。该定时器适用于在 C++ 代码中精确测量经过的时间;

在本章,我们将探讨以上几种类型的定时器的原理、应用场景、以及如何用 Qt Quick 实现定时功能的详细介绍。

关于定时器的操作和特点,这里列出了一些基本概念:

  1. 启动定时器: 启动一个定时器后,这个定时器会在设定的时间周期内诱发一个信号或事件。如 QTimer 可以通过 start() 方法进行启动;
  2. 停止定时器: 停止一个定时器后,它将停止诱发信号或事件。如 QTimer 可以通过 stop() 方法进行停止;
  3. 单次触发与重复触发: 定时器可以设置为仅触发一次,或者在每个时间周期内重复触发。例如,在 QTimer 中可以通过设置 singleShot 属性来确定触发类型;
  4. 定时器精度: 不同定时器提供了不同的精度。例如,QTimer 默认以毫秒级精度进行计时,而 QElapsedTimer 可以提供更高的精度;
  5. 定时器与事件循环: 定时器通常依赖于事件循环机制执行。当一个定时器事件由事件循环捕获时,会发出相应的信号或执行相应的处理方法。因此,了解这一机制有助于更好地掌握定时器的运行过程。

继续阅读本章,您将深入了解定时器的类型、原理与比较,以及如何运用心理学让这些知识更通俗易懂。

1.2 定时器类型与比较(Timers Types and Comparison)

在 Qt Quick 中,我们主要讨论以下三种定时器类型:

  • QTimer:
  • QQuickTimer:
  • QElapsedTimer:

我们将通过对比这三种定时器的特点、用途和不同场景的适用性来加深对它们的理解。

QTimer

QTimer 是 Qt 中使用较为广泛的定时器类型。其主要特点是:

  1. 属于 QObject 类,天然支持信号和槽的机制;
  2. 可以设置定时器为单次触发或重复触发;
  3. 支持毫秒级的精度;
  4. 与事件循环密切相关,依赖于事件循环捕获并处理定时器事件;
  5. 是一个 QObject,适用于 C++ 代码中的定时任务。

QQuickTimer

QQuickTimer 是 Qt Quick QML 中的一种定时器类型。主要特点为:

  1. 属于 QML 类,与 QML 内其他对象无缝交互;
  2. 支持信号和槽的机制;
  3. 可以设置定时器为单次触发或重复触发;
  4. 支持毫秒级的精度;
  5. 同样与事件循环密切相关,依赖于事件循环捕获并处理定时器事件;
  6. 专为 QML 场景设计的定时器。

QElapsedTimer

QElapsedTimer 主要适用于精确测量经过的时间,而非事件触发器。其主要特点是:

  1. 属于 QObject 类;
  2. 提供高精度的时间测量;
  3. 主要用于 C++ 代码中的耗时计算和性能分析;
  4. 不具有信号和槽的机制,无法作为事件触发器;
  5. 与事件循环无关。

通过以上对比,我们可以得出如下结论:

  • 当我们在 C++ 代码中需要定时触发事件并处理时,首选 QTimer
  • 若要在 QML 场景中实现定时任务,QQuickTimer 是更优的选择;
  • 对于精确计时和性能分析等场景,使用 QElapsedTimer 较为合适;

下一节中,我们将深入探讨定时器的底层原理,以及如何利用心理学的方法让这些知识更加通俗易懂。

1.3 深入理解定时器原理(Understanding Timer Principles)

在本节中,我们将更深入地探讨定时器的底层原理,并尝试用通俗易懂的方式阐述这些知识点。

定时器与事件循环

定时器的工作原理与事件循环机制密切相关。简单来说,事件循环是一个不断监测和处理事件的循环结构。定时器事件作为事件循环中的一种事件,当达到指定的时间节点时,会被事件循环捕获并处理。

以一个简单的钟表为例,我们可以将事件循环比喻成一个钟摆,每次摆动都检查是否到达了设定的时间节点。当到达预定时间后,事件循环执行相应的处理操作。

QTimer 与 QQuickTimer 的底层原理

QTimerQQuickTimer 的底层原理相似,都依赖于事件循环。当定时器启动时,它加入到了事件循环的任务队列中。当时间达到指定的时间节点,事件循环捕获这个定时器事件并发出信号或执行槽函数。

这里,我们可以用人类的心跳来进行类比。设想有一个任务需要每隔一定的心跳执行一次,那么我们可以把定时器看作是在心跳中执行这个任务的机制。当到达指定的时间节点,任务就会执行,然后等待下一个周期的心跳再次执行。

QElapsedTimer 的底层原理

与前述类型的定时器不同,QElapsedTimer 的主要任务是测量经过的时间,而不是触发事件。因此,它的原理类似于一个精确的 Stopwatch。当我们启动 QElapsedTimer 时,它开始记录当前的时间点,然后在需要获取经过时间时,通过计算当前时间点与记录的时间点之差来获取。

这里的类比可以是一个计步器。假设一个人行走的速度为每秒走一步,那么计步器可以记录每一个时间节点的步数,我们可以通过计步器获取这段时间内走了多少步。

用通俗易懂的方式讲解定时器原理

通过以上类比,我们可以更加生动地理解定时器原理。

  1. 事件循环: 就像一个更大的监控系统,不断检查是否有任务需要处理,定时器是其中的一个组成部分;
  2. QTimer/QQuickTimer: 可以看作是心跳,每隔一定时间节点触发事件;
  3. QElapsedTimer: 类似于计步器,在不断记录经过的时间,帮助我们准确测量时间间隔。

通过这种讲解方式,即使给非计算机专业的人讲解定时器知识也可以显得简单易懂。在接下来的章节中,我们将利用这些原理来实际编写 Qt Quick 定时器的代码示例。

2.1 使用 QTimer 实现定时功能(Using QTimer for Timing)

在本节中,我们将学习如何使用 QTimer 创建一个定时器,并设置其时间间隔、触发类型等属性。然后,我们会展示如何与 Qt 信号和槽机制相结合,实现一个简单的定时任务。

创建 QTimer 实例

首先,我们需要在 C++ 代码中创建一个 QTimer 实例。为此,我们通常需要包含以下头文件:

#include <QTimer>

然后,我们实例化 QTimer

QTimer *timer = new QTimer(this);

这里,我们将 QTimer 实例分配给当前类的对象(this)。QTimer 作为子对象,会随父对象一起被销毁。

设置时间间隔和触发类型

在创建了 QTimer 实例之后,我们可以设置定时器的时间间隔和触发类型。例如,我们可以设置一个周期为 1000 毫秒(1 秒)的定时器,每秒触发一次:

timer->setInterval(1000); // 设置时间间隔为 1000 毫秒

接下来,我们确定触发类型。若要设置定时器为单次触发:

timer->setSingleShot(true); // 设置为单次触发

如果想要设置为重复触发,只需将 true 改为 false 即可。或者,不设置 setSingleShot,因为其默认值为 false

编写槽函数

接下来,我们需要为定时器创建一个槽函数,用于处理定时器触发时的操作。在定义槽函数之前,先在类定义中声明其类型为槽:

public slots:
    void onTimerTimeout();

然后,在类实现文件中定义槽函数的具体操作:

void YourClass::onTimerTimeout() {
    // 这里编写定时器触发时需要执行的操作,例如输出当前时间:
    qDebug() << "Timer triggered at: " << QDateTime::currentDateTime().toString();
}

关联 QTimer 信号和槽函数

创建了 QTimer 实例并设置其属性后,我们需要将定时器的 timeout() 信号关联到目标槽函数。这可以通过 connect() 函数实现:

connect(timer, &QTimer::timeout, this, &YourClass::onTimerTimeout);

启动 QTimer

最后,我们通过调用 start() 函数启动定时器:

timer->start();

现在,我们已经创建了一个完整的 QTimer 示例,每隔 1 秒会触发一次,直到程序结束或我们调用 stop() 函数停止定时器。在接下来的章节中,我们将学习使用 QML 中的 QQuickTimer 实现定时功能。

2.2 在 QML 中使用 QQuickTimer 实现定时功能(Using QQuickTimer in QML for Timing)

在本节中,我们将介绍如何在 Qt Quick QML 中使用 QQuickTimer 实现定时功能,并与其他 QML 对象进行交互。

创建 QQuickTimer 实例

首先,在 QML 文件中创建一个 Timer 对象:

import QtQuick 2.12
Rectangle {
    Timer {
        id: timer
    }
}

设置时间间隔和触发类型

接下来,我们可以设置意表的周期和触发类型。例如,设置一个每秒触发一次的定时器:

Timer {
    id: timer
    interval: 1000 // 设置时间间隔为 1000 毫秒
}

若要设置定时器为单次触发,可以使用 repeat 属性:

Timer {
    id: timer
    interval: 1000
    repeat: false // 设置为单次触发
}

若要设置为重复触发,将 false 改为 true 即可。其默认值为 true ,因此你也可以不用设置 repeat

设定定时器触发时的操作

要执行定时器触发时的操作,我们使用 onTriggered 信号处理器。例如,每隔1秒更新一个文本框的内容:

import QtQuick 2.12
import QtQuick.Window 2.12
Window {
    id: root
    width: 400
    height: 300
    visible: true
    Text {
        id: textDisplay
        text: "Hello, world!"
        anchors.centerIn: parent
    }
    Timer {
        id: timer
        interval: 1000
        running: true // 让定时器运行
        repeat: true // 设置为重复触发
        onTriggered: {
            // 更新文本框的内容为当前时间
            textDisplay.text = "Current time: " + new Date().toLocaleTimeString();
        }
    }
}

在这个例子中,当 QQuickTimer 触发时,onTriggered 会将文本框的内容更新为当前的时间。

启动和停止 QQuickTimer

要启动和停止定时器,我们可以设置 running 属性。如上面的例子,定时器默认处于运行状态。若要手动启动或停止定时器,可以设置 running 属性为 true(启动)或 false(停止)。例如,可以使用按钮来控制定时器的启动和停止:

Button {
    text: timer.running ? "Stop Timer" : "Start Timer"
    anchors.bottom: parent.bottom
    anchors.horizontalCenter: parent.horizontalCenter
    onClicked: {
        // 切换定时器运行状态
        timer.running = !timer.running;
    }
}

在本节中,我们探讨了如何在 QML 中使用 QQuickTimer 实现定时功能,并与其他 QML 对象进行交互。这为创建具有定时操作的 Qt Quick 应用程序提供了基础。

2.3 将 QTimer 和 QQuickTimer 结合使用(Combining QTimer and QQuickTimer)

在实际应用中,我们可能需要在 QML 和 C++ 之间共享和同步定时任务。在这种情况下,我们可以结合使用 QTimerQQuickTimer。以下示例演示了如何在 C++ 中使用 QTimer 更新 QML 中的文本显示:

1. 在 C++ 中设置 QTimer

首先,在 C++ 类定义中创建定时器和槽函数。为此,我们需要包括以下头文件:

#include <QTimer>
#include <QQmlApplicationEngine>
#include <QDate>

然后,在类定义中创建一个用于保存 QQmlApplicationEngine 引用的对象,这样我们可以在槽函数中访问 QML 的对象:

private:
    QQmlApplicationEngine *m_engine;

接下来,在构造函数中设置和启动 QTimer

YourClass::YourClass(QQmlApplicationEngine *engine, QObject *parent)
    : QObject(parent), m_engine(engine)
{
    QTimer *timer = new QTimer(this);
    timer->setInterval(1000); // 设置间隔为 1000 毫秒
    connect(timer, &QTimer::timeout, this, &YourClass::onTimerTimeout);
    timer->start();
}

2. 在槽函数中修改 QML 对象的属性

在定时器触发时的槽函数中,我们需要访问 QML 对象并修改其属性。首先,通过对象名称查找 QML 对象:

void YourClass::onTimerTimeout() {
    QObject *qmlText = m_engine->rootObjects().first()->findChild<QObject*>("exampleText");
    if (qmlText) {
        QDateTime currentDateTime = QDateTime::currentDateTime();
        qmlText->setProperty("text", "Current time: " + currentDateTime.toString());
    } else {
        qDebug() << "QML text object not found.";
    }
}

3. 在 QML 中设置对象

在 QML 文件中创建一个文本对象,并为其设置 objectName 属性。我们可以根据此名称在 C++ 代码中查找 QML 对象:

import QtQuick 2.12
import QtQuick.Window 2.12
Window {
    id: root
    width: 400
    height: 300
    visible: true
    Text {
        objectName: "exampleText"
        text: "Hello, world!"
        anchors.centerIn: parent
    }
}

现在,程序每隔 1 秒钟将通过C++中的 QTimer 更新 QML 中的文本控件。这个例子展示了如何通过C++代码与 QML 对象进行交互,实现定时任务的同步。这种方法为更复杂的应用场景提供了更大的灵活性和控制。

三、定时器编程的调试方法 (Debugging method of timer programming)

3.1 如何测试定时器(How to Test Timers)

定时器在许多应用程序中起着关键作用,因此测试定时器的功能和稳定性非常重要。在本节中,我们将介绍如何测试包含定时器的 Qt 和 Qt Quick 应用程序。

方法1:使用 QSignalSpy 测试信号

QSignalSpy 可以用于监视 Qt 对象发出的信号。它的一个常见用途是在单元测试中检查是否触发了定时器信号。例如,以下测试示例检查 QTimer 的超时信号是否已触发:

#include <QTest>
#include <QTimer>
#include <QSignalSpy>
void TimerTest::testTimerTimeoutSignal()
{
    QTimer timer;
    QSignalSpy spy(&timer, SIGNAL(timeout()));
    timer.setInterval(1000); // 设置间隔为 1000 毫秒
    timer.start();
    // 等待 1.2 秒并确保信号已触发
    QVERIFY(spy.wait(1200));
    QCOMPARE(spy.count(), 1); // 检查信号发出次数
}

方法2:使用模拟时钟和 QTest::qWait 函数

在某些情况下,使用模拟时钟更适合测试定时器。QTest::qWait 函数允许你模拟指定时间的流逝,以触发定时器事件。这对于测试期望在特定时间点触发的功能特别有用。

#include <QTest>
#include <QTimer>
void TimerTest::testTimerWithQTestQWait()
{
    QTimer timer;
    bool timerTriggered = false;
    // 当定时器触发时,设置 timerTriggered 为 true
    QObject::connect(&timer, &QTimer::timeout, [&]() {
        timerTriggered = true;
    });
    timer.setInterval(1000); // 设置间隔为 1000 毫秒
    timer.start();
    // 使用 QTest::qWait 模拟 1.2 秒时间流逝
    QTest::qWait(1200);
    QVERIFY(timerTriggered); // 检查定时器是否触发
}

在上面的示例中,我们通过使用 QTest::qWait 函数模拟 1.2 秒的时间流逝,以检查 QTimer 是否已触发。timerTriggered 变量在超时事件发生时设置为 true,用于验证定时器功能。

这两种方法都可以有效测试包含定时器的 Qt 和 Qt Quick 应用程序。你可以根据应用程序的需求和具体情况选择最适合的方法。测试定时器功能有助于确保应用程序的实时性和准确性。

3.2 使用 Qt Quick Test 测试 QQuickTimer

Qt Quick Test 提供了一种简便的方式来测试 Qt Quick(QML)应用程序。在本节中,我们将讨论如何使用 Qt Quick Test 来测试应用程序中的 QQuickTimer

1. 创建 QML 测试文件

在与你的 Qt Quick(QML)应用程序相同的目录下,创建一个新的 QML 测试文件(例如 TestQQuickTimer.qml)。该文件应从 Qt.test.qtestroot 继承并具有你的应用程序的 import 语句:

import QtQuick 2.12
import Qt.test 1.0
import QtQuick.Window 2.12
TestCase {
    name: "TestQQuickTimer"
    id: testCase
    Window {
        id: root
        visible: false
    }
    // 将其他需要的 Qt Quick 元素放在此处
}

2. 编写测试用例

TestCase 对象中,添加用于测试你的 QQuickTimerfunction 对象:

function test_timer() {
    var timer = Qt.createQmlObject(
        'import QtQuick 2.12; Timer {interval: 500; running: true}',
        root,
        'TestTimer'
    );
    var triggered = false;
    timer.triggered.connect(function() {
        triggered = true;
    });
    compare(timer.interval, 500, "Timer interval should be 500 ms.");
    compare(timer.repeat, true, "Timer should have default repeat value.");
    tryCompare(triggered, true, 600); // 关键一步:等待超时并检查定时器是否触发
}

这个测试用例定义了一个测试用的 QQuickTimer,将其超时 interval 设置为 500 毫秒。然后,我们连接 triggered 信号处理器,以在触发定时器时设置 triggered 变量为 true。我们检查定时器的各项属性,并使用 tryCompare 确保在超时后triggered 变量会更新。

3. 运行测试

要运行您的测试,确保您的Qt环境正确设置,然后在命令行中运行以下命令:

qmltestrunner -input TestQQuickTimer.qml

如果测试成功,你应该会看到如下输出:

********* Start testing of TestQQuickTimer *********
Config: Using QtTest library 5.x.y, Qt 5.x.y (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC x.y.z)
PASS   : TestQQuickTimer::initTestCase()
PASS   : TestQQuickTimer::test_timer()
PASS   : TestQQuickTimer::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted, 602ms
********* Finished testing of TestQQuickTimer *********

使用 Qt Quick Test 进行 QQuickTimer 测试,可以确保定时器在 Qt Quick(QML)应用程序中正常工作。这有助于保证应用程序在实时性和准确性方面可以可靠地运行。

3.3 使用 QTimer 和 QQuickTimer 追踪历史记录(Tracking History with QTimer and QQuickTimer)

在许多应用程序中,我们需要追踪不同事件的历史记录或监控应用程序的状态。在本节中,我们将讨论如何使用 QTimerQQuickTimer 追踪历史记录。

使用 QTimer 追踪历史记录

为了实现这个功能,我们可以在每个定时器触发时将信息添加到一个容器中,例如一个 QListQVector

  1. 定义一个容器,例如 QListQVector,用于保存历史记录。
  2. 在定时器的槽函数中,每次触发时将信息添加到容器中。
  3. 提供一个访问历史记录的方法,以供其他代码使用。
class HistoryTracker : public QObject {
    Q_OBJECT
public:
    HistoryTracker(QObject *parent = nullptr) : QObject(parent) {
        QTimer *timer = new QTimer(this);
        timer->setInterval(1000);
        connect(timer, &QTimer::timeout, this, &HistoryTracker::trackHistory);
        timer->start();
    }
    QList<QString> getHistory() const {
        return m_history;
    }
private slots:
    void trackHistory() {
        QDateTime currentDateTime = QDateTime::currentDateTime();
        m_history.append(currentDateTime.toString());
    }
private:
    QList<QString> m_history;
};

使用 QQuickTimer 追踪历史记录

在 QML 中使用 QQuickTimer 时,我们可以利用 JavaScript 数组来实现类似的功能:

  1. 定义一个 JavaScript 数组,例如 var history = []
  2. 在定时器的 onTriggered 事件处理器中,每次触发时将信息添加到数组中。
  3. 如果需要,将数组公开到其他 QML 元素或 C++ 代码以供访问。
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
    id: root
    width: 400
    height: 300
    visible: true
    property var history: []
  controller: {
    onHistoryChanged: {
      // 当历史记录发生改变时执行的操作,例如刷新视图或执行其他任务。
    }
  }
    Timer {
        id: timer
        interval: 1000
        running: true
        repeat: true
        onTriggered: {
            var currentTimestamp = new Date();
            root.history.push(currentTimestamp);
            controller.historyChanged();
        }
    }
  // 控制器,用于在历史记录发生更改时触发外部处理。
    Item {
        id: controller
    signal historyChanged()
    }
  // Button,用于显示历史记录。
    Button {
        id: displayButton
        text: "Display History"
        anchors.centerIn: parent
        onClicked: {
            for (var i = 0; i < root.history.length; i++) {
                console.log(root.history[i]);
            }
        }
    }
}

通过结合使用 QTimerQQuickTimer 和相应的容器,我们可以实现简单但有效的历史记录追踪功能,以在 Qt 或 Qt Quick 应用程序中满足各种需求。


Qt Quick 定时技巧全攻略:从底层原理到高级应用(二)https://developer.aliyun.com/article/1464355

目录
相关文章
|
6月前
|
设计模式 缓存 编译器
【C++ 元对象系统03】深入探索Qt反射:从原理到实践
【C++ 元对象系统03】深入探索Qt反射:从原理到实践
284 5
|
6月前
|
存储 安全 编译器
【Qt 底层机制之信号和槽 】深入探究Qt信号和槽背后的原理
【Qt 底层机制之信号和槽 】深入探究Qt信号和槽背后的原理
2016 4
|
6月前
|
算法 Unix 调度
【Qt 线程】深入探究QThread线程优先级:原理、应用与最佳实践
【Qt 线程】深入探究QThread线程优先级:原理、应用与最佳实践
530 0
Qt 窗口常用位置API函数 & 绘图原理 & 双缓冲机制 总结
Qt 窗口常用位置API函数 & 绘图原理 & 双缓冲机制 总结
|
6月前
|
存储 编译器 C++
【Qt 元对象系统 02】深入探索Qt的元对象编译器:从原理到实践
【Qt 元对象系统 02】深入探索Qt的元对象编译器:从原理到实践
416 0
|
6月前
|
Java 程序员 API
【深入探究 Qt 线程】一文详细解析Qt线程的内部原理与实现策略
【深入探究 Qt 线程】一文详细解析Qt线程的内部原理与实现策略
579 0
|
6月前
|
存储 算法 数据可视化
Qt与Excel:从底层原理到上层应用的全面探索
Qt与Excel:从底层原理到上层应用的全面探索
349 0
|
4月前
|
数据安全/隐私保护 C++ 计算机视觉
Qt(C++)开发一款图片防盗用水印制作小工具
文本水印是一种常用的防盗用手段,可以将文本信息嵌入到图片、视频等文件中,用于识别和证明文件的版权归属。在数字化和网络化的时代,大量的原创作品容易被不法分子盗用或侵犯版权,因此加入文本水印成为了保护原创作品和维护知识产权的必要手段。 通常情况下,文本水印可以包含版权声明、制作者姓名、日期、网址等信息,以帮助识别文件的来源和版权归属。同时,为了增强防盗用效果,文本水印通常会采用字体、颜色、角度等多种组合方式,使得水印难以被删除或篡改,有效地降低了盗用意愿和风险。 开发人员可以使用图像处理技术和编程语言实现文本水印的功能,例如使用Qt的QPainter类进行文本绘制操作,将文本信息嵌入到图片中,
183 1
Qt(C++)开发一款图片防盗用水印制作小工具
|
3月前
|
监控 C++ 容器
【qt】MDI多文档界面开发
【qt】MDI多文档界面开发
90 0
|
2月前
|
开发工具 C++
qt开发技巧与三个问题点
本文介绍了三个Qt开发中的常见问题及其解决方法,并提供了一些实用的开发技巧。

推荐镜像

更多
下一篇
无影云桌面