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


77

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

我基本上只是在制作一个控制台应用程序来测试一些类代码,这些类代码随后将包含在适当的Qt GUI中,因此,到目前为止,我无需担心破坏整个事件驱动的模型。


您到底在测试什么?添加一个wait()sleep()完成什么?
Kaleb Pederson 2010年

我正在创建一个使用RS232串行命令来驱动蠕动泵的类。我已经在QT中创建了一个GUI,但与此同时,我只是在测试从控制台中的main()函数创建的函数。因此,我希望该类由QT编译,但同时我想mypump.startpump();。睡眠(1000); mypump.stoppump(); 例如。只是为了测试它的工作。
Zac

尽管使用QT进行编译,但我仍使用CONFIG + = console来运行调试字符串并将其输出到控制台。
Zac

3
QObject().thread()->usleep(1000*1000*seconds);会睡seconds几秒钟:)
mlvljr 2014年

我希望你能投票赞成。mlvijr的答案非常完美。你应该回答。
bandito40

Answers:


36

先前的问题提到使用qSleep()这是在QtTest模块。为了避免QtTest模块中的开销链接,请查看该函数的源代码,然后可以制作自己的副本并进行调用。它使用定义来调用WindowsSleep()或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(); 据我所知,这将立即执行,因为我知道我不会在最终程序中使用此代码,因为它破坏了事件驱动模型,但是现在这使我能够按原样测试程序。
Zac

您在哪里找到来源?
托马什Zato -恢复莫妮卡

现在已经六年了,几经易手,但您可以在qt.io/download-open-source中找到Qt的开源版本。那里有一些链接可以链接到源代码。我不知道上面的代码是否仍然存在。
阿诺德·斯彭斯

137

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

我建议您使用此代码,而不要使用sleep函数,因为它不会使GUI冻结。

这是代码:

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

若要将事件延迟n秒,请使用addSecs(n)


2
非常感谢您提供的精美功能!正是我在寻找什么,因为它没有暂停主事件循环。
2014年

7
while在Qt 5.7中将其放入循环中时,我是唯一经历100%CPU负载的人吗?
Neurotransmitter

4
这种使用时钟时间的延迟功能是为什么许多应用程序有时无法按预期运行并且没人能轻易重现该问题的原因。超时和延迟不应使用时钟时间。如果由于第一次调用currentTime()和后续currentTime()调用之间的日期/时间更改而修改了使用此代码运行应用程序的机器的日期/时间,则延迟是错误的。取决于日期/时间更改,延迟要么过期得太早,要么过期太晚(太晚了几秒钟,几分钟,几小时,几天,几个月甚至几年)。延迟/超时使用时钟时间是不行的。
Mofi's

6
processEvents()如果没有挂起的事件,该函数将立即返回,这将导致繁忙的循环(占用100%的CPU)。因此,您应该在循环中添加逻辑以等待剩余的100ms循环时间。
DavidJ

2
这是非常危险的,因为“ processEvents”基本上意味着“任何事情都可能发生”。您可能最终处理套接字通信,这可能导致Qt对象被删除-或应用程序终止。隐藏在delay();程序中无害外观的语句之后的所有内容。
Frerich Raabe

72

从Qt5开始,我们还可以使用

QThread的静态公共成员

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

4
这应该是公认的答案。它简单,易用,并且不会耗尽您的所有CPU周期。
Franz D.

但它仍然会使GUI没有响应
user889030

35

小+1到kshark27的答案使其动态:

#include <QTime>

void delay( int millisecondsToWait )
{
    QTime dieTime = QTime::currentTime().addMSecs( millisecondsToWait );
    while( QTime::currentTime() < dieTime )
    {
        QCoreApplication::processEvents( QEventLoop::AllEvents, 100 );
    }
}

加一,小记:我想你#include <QCoreApplication>也'#包括<QTIME>`
杰克

3
请参阅上面的评论。在正常情况下(空事件队列),这可能会导致繁忙等待100%的CPU使用率,并且在循环运行时会调整较大的系统时钟问题。
DavidJ

#include <QtCore>
nvd

11

多年来,在尝试使QApplication :: processEvents正常工作时遇到了很多麻烦,就像在一些最佳答案中使用的那样。IIRC,如果多个位置最终调用它,则可能导致某些信号无法处理(https://doc.qt.io/archives/qq/qq27-sensitive-guis.html)。我通常的首选方法是利用QEventLoop(https://doc.qt.io/archives/qq/qq27-sensitive-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即可运行,也不会出现骇人的sleeps()或processEvents()。谢谢!
杰克

9

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

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旨在协调不同线程之间的互斥体等待。但是使这项工作起作用的原因是wait方法具有超时。以这种方式调用时,它的功能与休眠功能完全相同,但是它使用Qt的事件循环进行计时。因此,没有其他事件或UI被阻止,就像正常的Windows Sleep功能一样。

另外,我们添加了CancelSleep函数,以允许程序的另一部分取消“睡眠”功能。

我们对此感兴趣的是它轻巧,可重用并且完全独立。

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

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


至少在qt5中,当前的实现似乎根本不与事件循环通信,因此从“处理事件”的角度来看,这是“可中断的睡眠”。
joonas

6

由于您正在尝试“测试某些类代码”,因此我真的建议您学习使用QTestLib。它提供了一个QTest命名空间和一个QtTest模块,其中包含许多有用的函数和对象,包括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 <QTest>
  • 对于QtTest模块中的所有项目:#include <QtTest>。这在功能上等同于为命名空间中存在的每个项目添加一个包含。

如何包含QTestlib?#include <QTestlib.h>似乎无法找到它。
Zac

6

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

#include <unistd.h>

从Qt版本4.8开始,以下睡眠功能可用:

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);

这些功能受到保护!
SAAD

3

我们可以使用以下方法QT_Delay

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

这将等待2秒钟,然后再致电print_lcd()


该线程上的所有其他答案都将导致事件不被调用。这应该是接受的答案。
mammadalius

2

类似于此处的一些答案,但可能更轻巧

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

2

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

while (someCondition) {

   // do something

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

};

如果在GUI线程中完成此操作,则显然会在响应用户事件之前引入1秒的GUI延迟。但是,如果可以接受,则该解决方案可能是最容易实现的,甚至Qt也在其“线程基础”文章中对此予以认可(请参阅何时使用线程替代品一节)。


1

如果您想使用跨平台的方法,通常的模式是从QThread派生并在派生类中创建一个函数(如果需要,可以创建静态函数),该函数将调用QThread中的sleep函数之一。


甚至更好-看到问题后的最新评论(截止到今天),谢谢您的启发:)
mlvljr 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.