Wie erstelle ich mit Qt eine Pause / Warte-Funktion?

77

Ich spiele mit Qt herum und möchte eine einfache Pause zwischen zwei Befehlen erstellen. Es scheint mir jedoch nicht zu erlauben Sleep(int mili);, und ich kann keine offensichtlichen Wartefunktionen finden.

Ich mache im Grunde nur eine Konsolenanwendung, um einen Klassencode zu testen, der später in einer richtigen Qt-GUI enthalten sein wird. Daher mache ich mir vorerst nicht die Mühe, das gesamte ereignisgesteuerte Modell zu beschädigen.

Zac
quelle
Was genau testest du? Was bedeutet das Hinzufügen eines wait()oder sleep()erreichen?
Kaleb Pederson
Ich erstelle eine Klasse zum Antreiben einer peristatischen Pumpe mit seriellen RS232-Befehlen. Ich habe eine GUI in QT erstellt, aber in der Zwischenzeit teste ich nur die Funktionen, die ich aus einer main () - Funktion in der Konsole erstellt habe. Daher möchte ich, dass die Klasse von QT kompiliert wird, aber gleichzeitig möchte ich mypump.startpump (); Schlaf (1000); mypump.stoppump (); zum Beispiel. Nur um es zu testen, funktioniert es.
Zac
Trotz Kompilieren mit QT verwende ich CONFIG + = Konsole, um Debugging-Strings auszuführen und an die Konsole auszugeben.
Zac
3
QObject().thread()->usleep(1000*1000*seconds);wird für secondsSekunden schlafen :)
mlvljr
Ich wünschte, Sie könnten Kommentare abgeben. mlvijrs Antwort funktionierte perfekt. Sie sollten eine Antwort geben.
Bandito40

Antworten:

36

In dieser vorherigen Frage wird erwähnt, qSleep()welche im QtTestModul enthalten ist. Um die Overhead-Verknüpfung im QtTestModul zu vermeiden, können Sie bei der Suche nach der Quelle für diese Funktion einfach eine eigene Kopie erstellen und diese aufrufen. Es verwendet Definitionen, um entweder Windows Sleep()oder Linux aufzurufen 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
}
Arnold Spence
quelle
Dank der Einbeziehung von windows.h können Sie Sleep () verwenden. Dies reicht vorerst aus, da ich verstehe, dass ich diesen Code im endgültigen Programm nicht verwenden werde, da er das ereignisgesteuerte Modell beschädigt. Im Moment kann ich das Programm jedoch so testen, wie es ist.
Zac
Wo hast du die Quelle gefunden?
Tomáš Zato - Wiedereinsetzung Monica
Es ist jetzt sechs Jahre später und ein paar haben den Besitzer gewechselt, aber Sie können die Open-Source-Version von Qt unter qt.io/download-open-source finden . Dort gibt es Links, die zum Quellcode führen. Ich habe keine Ahnung, ob der obige Code noch existiert.
Arnold Spence
137

Ich habe eine supereinfache Verzögerungsfunktion für eine Anwendung geschrieben, die ich in Qt entwickelt habe.

Ich würde Ihnen raten, diesen Code anstelle der Sleep-Funktion zu verwenden, da dadurch Ihre GUI nicht einfriert.

Hier ist der Code:

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

Um ein Ereignis um n Sekunden zu verzögern, verwenden Sie addSecs(n).

Kartik Sharma
quelle
2
Vielen Dank für diese schöne Funktion! Genau das, wonach ich gesucht habe, da es die Hauptereignisschleife nicht unterbricht.
Marc
7
Bin ich nur derjenige, der 100% CPU-Last erfährt, wenn er dies whilein Qt 5.7 in eine Schleife setzt ?
Neurotransmitter
4
Solche Verzögerungsfunktionen, die die Uhrzeit verwenden, sind die Ursache dafür, dass viele Anwendungen manchmal nicht wie erwartet funktionieren und niemand das Problem leicht reproduzieren kann. Zeitüberschreitungen und Verzögerungen sollten niemals die Uhrzeit verwenden. Die Verzögerung ist falsch, wenn Datum und Uhrzeit des Computers, auf dem die Anwendung mit diesem Code ausgeführt wird, aufgrund einer Änderung von Datum und Uhrzeit zwischen dem ersten currentTime()und weiteren currentTime()Aufrufen geändert werden . Je nach Änderung von Datum und Uhrzeit läuft die Verzögerung entweder zu früh oder viel zu spät ab (Sekunden, Minuten, Stunden, Tage, Monate oder sogar Jahre zu spät). Die Verwendung der Uhrzeit für Verzögerungen / Timeouts ist kein Problem.
Mofi
6
Die processEvents()Funktion wird sofort zurückgegeben, wenn keine ausstehenden Ereignisse vorliegen, was zu einer Besetztschleife führt (die 100% der CPU beansprucht). Daher sollten Sie der Schleife Logik hinzufügen, um auf den Rest der verbleibenden 100-ms-Schleifenzeit zu warten.
DavidJ
2
Dies ist äußerst gefährlich, da "processEvents" im Grunde bedeutet "alles kann passieren". Möglicherweise wird die Socket-Kommunikation verarbeitet, Qt-Objekte werden möglicherweise gelöscht - oder die Anwendung wird beendet. All das verbirgt sich hinter einer harmlos aussehenden delay();Aussage in Ihrem Programm.
Frerich Raabe
72

Ab Qt5 können wir auch verwenden

Statische öffentliche Mitglieder von QThread

void    msleep(unsigned long msecs)
void    sleep(unsigned long secs)
void    usleep(unsigned long usecs)
Yash
quelle
4
Dies sollte die akzeptierte Antwort sein. Es ist einfach, benutzerfreundlich und verbraucht nicht alle CPU-Zyklen.
Franz D.
Trotzdem
35

Kleine +1 auf die Antwort von kshark27 , um es dynamisch zu machen:

#include <QTime>

void delay( int millisecondsToWait )
{
    QTime dieTime = QTime::currentTime().addMSecs( millisecondsToWait );
    while( QTime::currentTime() < dieTime )
    {
        QCoreApplication::processEvents( QEventLoop::AllEvents, 100 );
    }
}
GraehamF
quelle
plus eins, kleine Anmerkung: Ich denke, Sie enthalten #include <QCoreApplication>auch <QTime> `
Jack
3
Siehe Kommentare oben. Dies kann unter normalen Umständen zu einer 100% igen CPU-Auslastung führen (leere Ereigniswarteschlange), und größere Probleme der Systemuhr werden angepasst, wenn die Schleife ausgeführt wird.
DavidJ
#include <QtCore>
nvd
11

Ich hatte im Laufe der Jahre große Probleme damit, QApplication :: processEvents zum Laufen zu bringen, wie es in einigen der wichtigsten Antworten verwendet wird. IIRC: Wenn mehrere Standorte es aufrufen, kann dies dazu führen, dass einige Signale nicht verarbeitet werden ( https://doc.qt.io/archives/qq/qq27-responsive-guis.html ). Meine übliche bevorzugte Option ist die Verwendung eines 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();
}
dvntehn00bz
quelle
Tolle Lösung, auch heute noch. Dies sollte die akzeptierte Antwort sein. Funktioniert ohne Auflegen der GUI und ohne hacky sleeps () oder processEvents (). Vielen Dank!
Jack
9

Wir haben die folgende Klasse verwendet -

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 wurde entwickelt, um das Warten von Mutex zwischen verschiedenen Threads zu koordinieren. Aber was diese Arbeit macht, ist, dass die Wartemethode eine Zeitüberschreitung aufweist. Wenn es auf diese Weise aufgerufen wird, funktioniert es genau wie eine Schlaffunktion, verwendet jedoch die Ereignisschleife von Qt für das Timing. Daher werden keine anderen Ereignisse oder die Benutzeroberfläche blockiert, wie dies bei der normalen Windows-Schlaffunktion der Fall ist.

Als Bonus haben wir die CancelSleep-Funktion hinzugefügt, damit ein anderer Teil des Programms die "Sleep" -Funktion abbrechen kann.

Was uns daran gefallen hat, ist, dass es leicht, wiederverwendbar und völlig eigenständig ist.

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

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

photo_tom
quelle
Zumindest scheint die aktuelle Implementierung in qt5 überhaupt nicht mit der Ereignisschleife zu kommunizieren. Aus der Sicht von "Verarbeiten von Ereignissen" ist dies also "unterbrechbarer Schlaf".
Joonas
6

Da Sie versuchen, "Klassencode zu testen", würde ich wirklich empfehlen, den Umgang mit QTestLib zu lernen . Es bietet einen QTest-Namespace und ein QtTest-Modul , die eine Reihe nützlicher Funktionen und Objekte enthalten, einschließlich QSignalSpy , mit dem Sie überprüfen können, ob bestimmte Signale ausgegeben werden.

Da Sie sich irgendwann in eine vollständige Benutzeroberfläche integrieren werden, erhalten Sie durch die Verwendung von QTestLib und das Testen ohne Schlafen oder Warten einen genaueren Test, der die tatsächlichen Verwendungsmuster besser darstellt. Sollten Sie sich jedoch dafür entscheiden, diesen Weg nicht zu gehen, können Sie QTestLib :: qSleep verwenden , um das zu tun, was Sie angefordert haben.

Da Sie zwischen dem Starten und Abschalten der Pumpe nur eine Pause benötigen, können Sie problemlos einen Timer mit einem Schuss verwenden:

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

Aber es qSleep()wäre definitiv einfacher, wenn Sie nur ein paar Dinge in der Befehlszeile überprüfen würden.

BEARBEITEN : Basierend auf dem Kommentar sind hier die erforderlichen Verwendungsmuster.

Zuerst müssen Sie Ihre .pro-Datei bearbeiten, um qtestlib einzuschließen:

CONFIG += qtestlib

Zweitens müssen Sie die erforderlichen Dateien einschließen:

  • Für den QTest-Namespace (einschließlich qSleep):#include <QTest>
  • Für alle Elemente im QtTestModul : #include <QtTest>. Dies entspricht funktional dem Hinzufügen eines Includes für jedes Element, das im Namespace vorhanden ist.
Kaleb Pederson
quelle
Wie füge ich QTestlib hinzu? Das #include <QTestlib.h> scheint es nicht finden zu können.
Zac
6

Um die Standard- Schlaffunktion zu verwenden, fügen Sie Folgendes in Ihre CPP-Datei ein:

#include <unistd.h>

Ab Qt Version 4.8 stehen folgende Schlaffunktionen zur Verfügung:

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

Um sie zu verwenden, fügen Sie einfach Folgendes in Ihre CPP-Datei ein:

#include <QThread>

Referenz: QThread (über die Qt-Dokumentation): http://doc.qt.io/qt-4.8/qthread.html

Andernfalls führen Sie diese Schritte aus ...

Ändern Sie die Projektdatei wie folgt:

CONFIG += qtestlib

Beachten Sie, dass in neueren Versionen von Qt der folgende Fehler angezeigt wird:

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

... also ändern Sie stattdessen die Projektdatei wie folgt:

QT += testlib

Fügen Sie dann in Ihrer CPP-Datei Folgendes hinzu:

#include <QtTest>

Und dann verwenden Sie eine der Schlaffunktionen wie folgt:

usleep(100);
Richard Nalezynski
quelle
Diese Funktionen sind geschützt!
SAAD
3

Wir können folgende Methode verwenden QT_Delay:

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

Dies wird 2 Sekunden warten, bevor Sie anrufen print_lcd().

user11514364
quelle
Jede andere Antwort in diesem Thread führt dazu, dass die Ereignisse nicht aufgerufen werden. Dies sollte die akzeptierte Antwort sein.
Mammadalius
2

Ähnlich wie einige Antworten hier, aber vielleicht etwas leichter

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

Die Antwort von @ kshark27 hat aus irgendeinem Grund bei mir nicht funktioniert (weil ich Qt 5.7 verwende?), also habe ich Folgendes getan:

while (someCondition) {

   // do something

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

};

Wenn dies im GUI-Thread erfolgt, führt dies offensichtlich zu einer Verzögerung von 1 Sekunde, bevor auf Benutzerereignisse reagiert wird. Aber wenn Sie damit leben können, ist diese Lösung wahrscheinlich am einfachsten zu implementieren, und sogar Qt unterstützt sie in ihrem Artikel über Threading-Grundlagen (siehe Abschnitt Wann werden Alternativen zu Threads verwendet ).

Neurotransmitter
quelle
1

Wenn Sie dazu eine plattformübergreifende Methode wünschen, besteht das allgemeine Muster darin, von QThread abzuleiten und in Ihrer abgeleiteten Klasse eine Funktion (statisch, wenn Sie möchten) zu erstellen, die eine der Sleep-Funktionen in QThread aufruft.

San Jacinto
quelle
oder noch besser - siehe den letzten Kommentar unter der Frage (Stand heute), danke, dass
du das