如何使用 Qt 创建暂停/等待功能?

Posted

技术标签:

【中文标题】如何使用 Qt 创建暂停/等待功能?【英文标题】:How do I create a pause/wait function using Qt? 【发布时间】:2011-04-14 17:49:52 【问题描述】:

我正在玩Qt,我想在两个命令之间创建一个简单的暂停。但是它似乎不允许我使用Sleep(int mili);,而且我找不到任何明显的等待功能。

我基本上只是制作一个控制台应用程序来测试一些类代码,这些代码稍后将包含在适当的 Qt GUI 中,所以现在我不担心破坏整个事件驱动模型。

【问题讨论】:

你到底在测试什么?添加wait()sleep() 有什么作用? 我正在创建一个类来使用 RS232 串行命令驱动蠕动泵。我在 QT 中创建了一个 GUI,但同时我只是在测试我从控制台中的 main() 函数创建的函数。因此,我希望该类由 QT 编译,但同时我想 mypump.startpump();睡眠(1000); mypump.stoppump();例如。只是为了测试它是否有效。 尽管使用 QT 进行编译,但我使用 CONFIG += 控制台运行并将调试字符串输出到控制台。 QObject().thread()->usleep(1000*1000*seconds); 将休眠 seconds 秒 :) 我希望你能投票给 cmets。 mlvijr 的回答非常完美。你应该回答。 【参考方案1】:

我为我在 Qt 中开发的一个应用程序编写了一个超级简单的延迟函数。

我建议您使用此代码而不是 sleep 功能,因为它不会让您的 GUI 冻结。

代码如下:

void delay()

    QTime dieTime= QTime::currentTime().addSecs(1);
    while (QTime::currentTime() < dieTime)
        QCoreApplication::processEvents(QEventLoop::AllEvents, 100);

要将事件延迟 n 秒 - 使用 addSecs(n)

【讨论】:

非常感谢这个漂亮的功能!正是我想要的,因为它不会暂停主事件循环。 只有我在 Qt 5.7 的 while 循环中遇到 100% CPU 负载吗? 这种使用时钟时间的延迟功能是许多应用程序有时无法按预期工作并且没有人可以轻松重现该问题的原因。超时和延迟不应该使用时钟时间。如果由于第一次 currentTime() 和任何进一步的 currentTime() 调用之间的日期/时间更改而修改了使用此代码运行应用程序的机器的日期/时间,则延迟是错误的。根据日期/时间的变化,延迟到期太早或太晚(几秒、几分钟、几小时、几天、几个月甚至几年太晚)。使用时钟时间进行延迟/超时是不行的。 如果没有挂起的事件,processEvents() 函数将立即返回,这会导致繁忙的循环(占用 100% 的 CPU)。因此,您应该在循环中添加逻辑以等待剩余的 100 毫秒循环时间。 这是非常危险的,因为“processEvents”基本上意味着“任何事情都可能发生”。您最终可能会处理套接字通信,这可能会导致 Qt 对象被删除 - 或应用程序终止。所有这些都隐藏在程序中看似无害的 delay(); 语句后面。【参考方案2】:

从 Qt5 开始我们也可以使用

Static Public Members of QThread

void    msleep(unsigned long msecs)
void    sleep(unsigned long secs)
void    usleep(unsigned long usecs)

【讨论】:

这应该是公认的答案。它简单易用,不会占用您所有的 CPU 周期。 但它仍然会使 GUI 没有响应 @user889030 当然会。如果您只想暂停自己的代码,则必须自己排空/处理 ui 事件。见***.com/a/11487434/10126273【参考方案3】:

这个previous question 提到使用qSleep(),它在QtTest 模块中。为了避免QtTest 模块中的开销链接,查看该函数的源代码,您可以制作自己的副本并调用它。它使用定义来调用 Windows Sleep() 或 Linux nanosleep()

#ifdef Q_OS_WIN
#include <windows.h> // for Sleep
#endif
void QTest::qSleep(int ms)

    QTEST_ASSERT(ms > 0);

#ifdef Q_OS_WIN
    Sleep(uint(ms));
#else
    struct timespec ts =  ms / 1000, (ms % 1000) * 1000 * 1000 ;
    nanosleep(&ts, NULL);
#endif

【讨论】:

谢谢,包括 windows.h 允许使用 Sleep();就目前而言,我知道我不会在最终程序中使用此代码,因为它破坏了事件驱动模型,但现在这使我能够按原样测试程序。 你在哪里找到源代码的? 现在已经六年过去了,有些易手,但您可以在qt.io/download-open-source 找到 Qt 的开源版本。那里有指向源代码的链接。我不知道上面的代码是否仍然存在。【参考方案4】:

给kshark27 的答案+1 使其动态化:

#include <QTime>

void delay( int millisecondsToWait )

    QTime dieTime = QTime::currentTime().addMSecs( millisecondsToWait );
    while( QTime::currentTime() < dieTime )
    
        QCoreApplication::processEvents( QEventLoop::AllEvents, 100 );
    

【讨论】:

加一,小记:我猜你#include &lt;QCoreApplication&gt;还有´#include ` 见上面的 cmets。这可能会导致在正常情况下(空事件队列)忙等待 100% cpu 使用率,并且在循环运行时调整系统时钟的更大问题。 #include 【参考方案5】:

多年来,我在尝试让 QApplication::processEvents 工作时遇到了很多麻烦,正如一些热门答案中所使用的那样。 IIRC,如果多个位置最终调用它,最终可能导致某些信号无法处理(https://doc.qt.io/archives/qq/qq27-responsive-guis.html)。我通常首选的选项是使用 QEventLoop (https://doc.qt.io/archives/qq/qq27-responsive-guis.html#waitinginalocaleventloop)。

inline void delay(int millisecondsWait)

    QEventLoop loop;
    QTimer t;
    t.connect(&t, &QTimer::timeout, &loop, &QEventLoop::quit);
    t.start(millisecondsWait);
    loop.exec();

【讨论】:

很好的解决方案,即使在今天也是如此。这应该是公认的答案。无需挂断 GUI 也无需 hacky sleeps() 或 processEvents() 即可工作。谢谢! 这应该是公认的答案。它不会占用 CPU,使用事件系统使计时器到期,并且没有循环。【参考方案6】:

我们一直在使用下面的类 -

class SleepSimulator
     QMutex localMutex;
     QWaitCondition sleepSimulator;
public:
    SleepSimulator::SleepSimulator()
    
        localMutex.lock();
    
    void sleep(unsigned long sleepMS)
    
        sleepSimulator.wait(&localMutex, sleepMS);
    
    void CancelSleep()
    
        sleepSimulator.wakeAll();
    
;

QWaitCondition 旨在协调不同线程之间的互斥等待。但是使这项工作有效的是等待方法有一个超时。当以这种方式调用时,它的功能与睡眠函数完全一样,但它使用 Qt 的事件循环进行计时。因此,不会像正常的 Windows 睡眠功能那样阻止其他事件或 UI。

作为奖励,我们添加了 CancelSleep 函数以允许程序的另一部分取消“睡眠”函数。

我们喜欢它的地方在于它轻巧、可重复使用且完全独立。

QMutex:http://doc.qt.io/archives/4.6/qmutex.html

QWaitCondition:http://doc.qt.io/archives/4.6/qwaitcondition.html

【讨论】:

至少current implementation in qt5 似乎根本不与事件循环通信,所以从“处理事件”的观点来看,这是“可中断的睡眠”。【参考方案7】:

要使用标准 sleep 功能,请在您的 .cpp 文件中添加以下内容:

#include <unistd.h>

从 Qt 4.8 版开始,以下 sleep 函数可用:

void QThread::msleep(unsigned long msecs)
void QThread::sleep(unsigned long secs)
void QThread::usleep(unsigned long usecs)

要使用它们,只需在 .cpp 文件中添加以下内容:

#include <QThread>

参考:QThread(通过 Qt 文档): http://doc.qt.io/qt-4.8/qthread.html

否则,请执行这些步骤...

修改工程文件如下:

CONFIG += qtestlib

请注意,在较新版本的 Qt 中,您将收到以下错误:

Project WARNING: CONFIG+=qtestlib is deprecated. Use QT+=testlib instead.

...所以,改为如下修改项目文件:

QT += testlib

然后,在您的 .cpp 文件中,请务必添加以下内容:

#include <QtTest>

然后像这样使用睡眠功能之一:

usleep(100);

【讨论】:

这些功能受到保护!【参考方案8】:

由于您正在尝试“测试一些类代码”,我真的建议您学习使用QTestLib。它提供了一个QTest namespace 和一个QtTest module,其中包含许多有用的函数和对象,包括可用于验证某些信号是否已发出的QSignalSpy。

由于您最终将与完整的 GUI 集成,因此使用 QTestLib 并在不休眠或等待的情况下进行测试将为您提供更准确的测试——一种更能代表真实使用模式的测试。但是,如果您选择不走那条路,您可以使用QTestLib::qSleep 来完成您的请求。

由于您只需要在启动和关闭泵之间暂停一下,因此您可以轻松地使用单次计时器:

class PumpTest: public QObject 
    Q_OBJECT
    Pump &pump;
public:
    PumpTest(Pump &pump):pump(pump) ;
public slots:
    void start()  pump.startpump(); 
    void stop()  pump.stoppump(); 
    void stopAndShutdown() 
        stop();
        QCoreApplication::exit(0);
    
    void test() 
        start();
        QTimer::singleShot(1000, this, SLOT(stopAndShutdown));
    
;

int main(int argc, char* argv[]) 
    QCoreApplication app(argc, argv);
    Pump p;
    PumpTest t(p);
    t.test();
    return app.exec();

但如果您感兴趣的只是在命令行上验证几件事,qSleep() 肯定会更容易。

编辑:根据评论,这是所需的使用模式。

首先,您需要编辑 .pro 文件以包含 qtestlib:

CONFIG += qtestlib

其次,你需要包含必要的文件:

对于 QTest 命名空间(包括 qSleep):#include &lt;QTest&gt; 对于QtTest 模块中的所有项目:#include &lt;QtTest&gt;。这在功能上等同于为命名空间中存在的每个项目添加一个包含。

【讨论】:

如何包含 QTestlib? #include 似乎找不到它。【参考方案9】:

@kshark27 的回答由于某种原因对我不起作用(因为我使用 Qt 5.7?)所以我最终这样做了:

while (someCondition) 

   // do something

   QApplication::processEvents();
   QThread::sleep(1); // 1 second

;

如果这是在 GUI 线程中完成的,它显然会在响应用户事件之前引入 1 秒的 GUI 延迟。但如果你能接受它,这个解决方案可能是最容易实现的,甚至 Qt 在他们的Threading Basics 文章中也支持它(参见何时使用替代线程部分)。

【讨论】:

【参考方案10】:

我们可以使用下面的方法QT_Delay:

QTimer::singleShot(2000,this,SLOT(print_lcd()));

这将在调用print_lcd() 之前等待 2 秒。

【讨论】:

此线程上的所有其他答案都将导致事件不被调用。这应该是已接受的答案。【参考方案11】:

与此处的一些答案类似,但可能更轻量级

void MyClass::sleepFor(qint64 milliseconds)
    qint64 timeToExitFunction = QDateTime::currentMSecsSinceEpoch()+milliseconds;
    while(timeToExitFunction>QDateTime::currentMSecsSinceEpoch())
        QApplication::processEvents(QEventLoop::AllEvents, 100);
    

【讨论】:

【参考方案12】:

如果你想要一个跨平台的方法来做这件事,一般的模式是从 QThread 派生并在你的派生类中创建一个函数(静态的,如果你愿意的话),它将调用 QThread 中的睡眠函数之一.

【讨论】:

甚至更好 - 请参阅问题下的最后一条评论(截至今天),感谢您的启发:)【参考方案13】:

这适用于 Qt5:

void wait( int ms ) 
   QElapsedTimer timer;
   timer.start();

   while ( timer.elapsed() < ms )
       QCoreApplication::processEvents();

【讨论】:

以上是关于如何使用 Qt 创建暂停/等待功能?的主要内容,如果未能解决你的问题,请参考以下文章

Gtkmm:如何暂停应用程序的执行并等待用户输入?

如何让Qt 的程序等待一段时间

如何在 discord.py 机器人加入语音通道之间添加暂停?

Java如何实现线程的暂停和重新启用?求大神

Qt5:如何在线程中等待信号?

如何在 Retrofit 中创建用于暂停功能的调用适配器?