gpt4 book ai didi

c++ - 如果停止 QTimer,它会发出 timeout() 信号吗?

转载 作者:太空狗 更新时间:2023-10-29 23:30:19 27 4
gpt4 key购买 nike

我试图了解 QTimer 的操作。我有一些事情触发了 timeout() 信号,但我无法在文档中找到如果我提前停止计时器是否会发出 timeout() 信号。

基本上,如何在计时器完成计数之前强制超时()?只是通过以最小 ms 增量重新启动计时器来破解?

myTimer->start(1);

http://qt-project.org/doc/qt-5/qtimer.html#timeout

最佳答案

在 Qt 4 和 Qt 5 中都不能直接发出 QTimer::timeout从课外。这是一个私有(private)信号:在 Qt 4 中,它被声明为 private , 在 Qt 5 中,它是用私有(private)类型的参数声明的 QObjectPrivate .

不过,您可以调用它:

// fast, guaranteed to work in Qt 4 and 5
myTimer->QTimer::qt_metacall(QMetaObject::InvokeMetaMethod, 5, {});

// slower, has to look up the method by name
QMetaObject::invokeMethod(myTimer, "timeout");

在 Qt 5 中,moc 生成的 QTimer::qt_static_metacall为我们构造私有(private)参数:
//...
case 0: _t->timeout(QPrivateSignal()); break;

您还可以通过向计时器发送计时器事件来使计时器表现得好像它已超时:
void emitTimeout(QTimer * timer) {
Q_ASSERT(timer);
QTimerEvent event{timer->timerId()};
QCoreApplication::sendEvent(timer, &event);
}

这两种方法都适用于 Qt 4 和 Qt 5。

由于您希望在停止事件计时器时发出超时,因此解决方案分别是:
void emitTimeoutAndStop(QTimer * timer) {
Q_ASSERT(timer);
Q_ASSERT(QAbstractEventDispatcher::instance()); // event loop must be on the stack
if (!timer->isActive()) return;
timer->QTimer::qt_metacall(QMetaObject::InvokeMetaMethod, 5, {});
timer->stop();
}

或者
void emitTimeoutAndStop(QTimer * timer) {
Q_ASSERT(timer);
Q_ASSERT(QAbstractEventDispatcher::instance()); // event loop must be on the stack
if (!timer->isActive()) return;
QTimerEvent event{timer->timerId()};
QCoreApplication::sendEvent(timer, &event);
timer->stop();
}

信号将立即发出,而不是由事件循环中的 Qt 代码发出。这应该不是问题,因为 emitTimeoutAndStop将通过堆栈上的事件循环调用。我们断言这个事实。如果您希望支持调用 emitTimeoutAndStop来自绑定(bind)到同一计时器的代码 timeout无需重新输入所述代码即可发出信号,那么您必须使用 ChattyTimer下面,或 the solution from another answer .

如果您所需要的不是一个计时器,而只是一个即时的单次发射信号, QObject::destroyed用于此目的。它将在 block 的末尾发出,其中 source超出范围并被破坏。
{ QObject source;
connect(&source, &QObject::destroyed, ...); }

或者,你可以有一个小的帮助类:
// signalsource.h
#pragma once
#include <QObject>
class SignalSource : public QObject {
Q_OBJECT
public:
Q_SIGNAL void signal();
SignalSource(QObject * parent = {}) : QObject(parent) {}
};

由于您可以将多个信号连接到单个接收器,因此将计时器和此类信号源都连接到接收器可能会使事情更清晰,而不是试图绕过计时器的行为。

另一方面,如果这种“停止时发出信号”计时器在多个地方都非常有用,那么实际上将它实现为专用类会更好——这并不难。重用 QTimer类是不可能的,因为 stop()插槽不是虚拟的,因此 ChattyTimer不是 Liskov 可替代的 QTimer .这将是一个等待发生的错误 - 在很难找到的错误类别中。

有几个行为细节需要注意。这可能表明改变像计时器这样基本的东西的行为很棘手 - 你永远不知道哪些代码可能做出在 QTimer 中显然正确的假设,但当 stop() 时并非如此可能会发出超时。将所有这些放在一个非类中是个好主意 QTimer ——真的不是!
  • QTimer ,超时事件总是从事件循环中发出。让它立即从 stop() 发出, 设置 immediateStopTimeout .
  • isActive 有两种可能的概括停止时的行为(与 QTimer 的行为相比):
  • stop 之后立即变为 false返回,即使最后 timeout稍后会发出,或者
  • 指示事件循环是否可以发出超时事件,并将保持truestop() 之后如果最后timeout信号延迟。

  • 我选择第一个行为作为默认行为。套装 activeUntilLastTimeout选择第二个行为。
  • 每个 timerId 的行为都有可能的三种概括。和 remainingTime停止时(与 QTimer 相比):
  • 返回 -1isActive()为假,否则为有效的标识符/时间(即遵循选择的 isActive() 行为),
  • 变成 -1紧随其后 stop返回,即使最后 timeout稍后会发出,
  • 只要事件循环仍可能发出超时事件,就返回有效的 id/time。

  • 我为 timerId 选择了第一个行为和 remainingTime , 并且它不能以其他方式配置。
    // https://github.com/KubaO/stackoverflown/tree/master/questions/chattytimer-25695203
    // chattytimer.h
    #pragma once
    #include <QAbstractEventDispatcher>
    #include <QBasicTimer>
    #include <QTimerEvent>
    class ChattyTimer : public QObject {
    Q_OBJECT
    Q_PROPERTY(bool active READ isActive)
    Q_PROPERTY(int remainingTime READ remainingTime)
    Q_PROPERTY(int interval READ interval WRITE setInterval)
    Q_PROPERTY(bool singleShot READ singleShot WRITE setSingleShot)
    Q_PROPERTY(Qt::TimerType timerType READ timerType WRITE setTimerType)
    Q_PROPERTY(bool immediateStopTimeout READ immediateStopTimeout WRITE setImmediateStopTimeout)
    Q_PROPERTY(bool activeUntilLastTimeout READ activeUntilLastTimeout WRITE setActiveUntilLastTimeout)
    Qt::TimerType m_type = Qt::CoarseTimer;
    bool m_singleShot = false;
    bool m_stopTimeout = false;
    bool m_immediateStopTimeout = false;
    bool m_activeUntilLastTimeout = false;
    QBasicTimer m_timer;
    int m_interval = 0;
    void timerEvent(QTimerEvent * ev) override {
    if (ev->timerId() != m_timer.timerId()) return;
    if (m_singleShot || m_stopTimeout) m_timer.stop();
    m_stopTimeout = false;
    emit timeout({});
    }
    public:
    ChattyTimer(QObject * parent = {}) : QObject(parent) {}
    Q_SLOT void start(int msec) {
    m_interval = msec;
    start();
    }
    Q_SLOT void start() {
    m_stopTimeout = false;
    m_timer.stop(); // don't emit the signal here
    m_timer.start(m_interval, m_type, this);
    }
    Q_SLOT void stop() {
    if (!isActive()) return;
    m_timer.stop();
    m_stopTimeout = !m_immediateStopTimeout;
    if (m_immediateStopTimeout)
    emit timeout({});
    else // defer to the event loop
    m_timer.start(0, this);
    }
    Q_SIGNAL void timeout(QPrivateSignal);
    int timerId() const {
    return isActive() ? m_timer.timerId() : -1;
    }
    bool isActive() const {
    return m_timer.isActive() && (m_activeUntilLastTimeout || !m_stopTimeout);
    }
    int remainingTime() const {
    return
    isActive()
    ? QAbstractEventDispatcher::instance()->remainingTime(m_timer.timerId())
    : -1;
    }
    int interval() const { return m_interval; }
    void setInterval(int msec) {
    m_interval = msec;
    if (!isActive()) return;
    m_timer.stop(); // don't emit the signal here
    start();
    }
    bool singleShot() const { return m_singleShot; }
    void setSingleShot(bool s) { m_singleShot = s; }
    Qt::TimerType timerType() const { return m_type; }
    void setTimerType(Qt::TimerType t) { m_type = t; }
    bool immediateStopTimeout() const { return m_immediateStopTimeout; }
    void setImmediateStopTimeout(bool s) { m_immediateStopTimeout = s; }
    bool activeUntilLastTimeout() const { return m_activeUntilLastTimeout; }
    void setActiveUntilLastTimeout(bool s) { m_activeUntilLastTimeout = s; }
    };

    关于c++ - 如果停止 QTimer,它会发出 timeout() 信号吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25695203/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com