Як створити функцію паузи / очікування за допомогою Qt?


78

Я граюся з Qt , і хочу створити просту паузу між двома командами. Однак це, здається, не дозволяє мені використовувати Sleep(int mili);, і я не можу знайти жодної очевидної функції очікування.

В основному я просто роблю консольний додаток для тестування коду класу, який згодом буде включений до належного графічного інтерфейсу Qt, тому наразі мене не турбує порушення всієї моделі, керованої подіями.


Що саме ви тестуєте? Що означає додавання wait()або sleep()досягнення?
Калеб Педерсон,

Я створюю клас для керування перистатичним насосом за допомогою послідовних команд RS232. Я створив графічний інтерфейс у QT, але тим часом я просто тестую функції, які я створив із функції main () у консолі. Таким чином, я хочу, щоб клас компілювався QT, але в той же час я хочу mypump.startpump (); сон (1000); mypump.stoppump (); наприклад. просто перевірити це працює.
Zac,

Незважаючи на компіляцію з QT, я використовую CONFIG + = console для запуску та виведення рядків налагодження на консоль.
Zac,

3
QObject().thread()->usleep(1000*1000*seconds);спатиму secondsсекунди :)
mlvljr

Я хотів би, щоб ви могли проголосувати коментарі. Відповідь mlvijr спрацювала бездоганно. Ви повинні дати відповідь.
bandito40

Відповіді:


36

У цьому попередньому питанні згадується використання того, 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 (); Наразі це буде зроблено, оскільки я розумію, що не буду використовувати цей код у остаточній програмі, оскільки він порушує модель, керовану подіями, але наразі це дозволяє мені протестувати програму як є.
Zac

Де ви знайшли джерело?
Томаш Зато - відновити Моніку

Зараз шість років потому, і кілька людей змінили власників, але ви можете знайти відкриту версію Qt за адресою qt.io/download-open-source . Там є посилання, які ведуть до вихідного коду. Я не маю уявлення, чи код все ще існує.
Арнольд Спенс

138

Я написав надзвичайно просту функцію затримки для програми, яку розробив у Qt.

Я б порадив вам використовувати цей код, а не функцію сну, оскільки це не дасть вашому графічному інтерфейсу замерзнути.

Ось код:

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

Щоб затримати подію на n секунд - використовуйте addSecs(n).


2
Щиро дякую за цю прекрасну функцію! Саме те, що я шукав, оскільки воно не призупиняє основний цикл подій.
Марк

7
Чи я лише той, хто відчуває 100% завантаження процесора, коли вводить це в whileцикл в Qt 5.7?
Нейромедіатор

4
Такі функції затримки, що використовують годинник, є причиною того, що багато програм іноді не працюють належним чином, і ніхто не може легко відтворити проблему. Час очікування та затримки ніколи не повинні використовувати годинник. Затримка неправильна, якщо дату / час машини, на якій запущено програму з цим кодом, змінено через зміну дати / часу між першим currentTime()та будь-якими подальшими currentTime()дзвінками. Залежно від зміни дати / часу затримка закінчується занадто рано або набагато пізно (секунди, хвилини, години, дні, місяці або навіть роки занадто пізно). Використання годинникового часу для затримок / тайм-аутів не можна використовувати.
Mofi

6
processEvents()Функція повертає негайно , якщо немає відкладених подій, що призводить до зайнятої петлі (приймаючи 100% від ЦП). Отже, слід додати логіку в цикл, щоб дочекатися залишку часу циклу 100 мс.
DavidJ

2
Це надзвичайно небезпечно, оскільки "processEvents" в основному означає "все може статися". Ви можете закінчити обробку сокетного зв’язку, це може призвести до видалення об’єктів Qt - або припинення роботи програми. Все, що ховається за нешкідливим delay();висловом у вашій програмі.
Фріріх Раабе,

74

Починаючи з Qt5, ми також можемо використовувати

Статичні публічні члени QThread

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

4
Це має бути прийнятою відповіддю. Він простий, простий у використанні і не з’їдає всіх ваших процесорних циклів.
Франц Д.

але все одно це призведе до того, що графічний інтерфейс не буде реагувати
user889030 02

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% використання процесора зайнятим очікуванням за звичайних обставин (порожня черга подій) і більші проблеми системного годинника коригуються під час запуску циклу.
DavidJ

#include <QtCore>
nvd

11

У мене було багато проблем протягом багатьох років, намагаючись змусити 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();
}

Чудове рішення навіть сьогодні. Це має бути прийнятою відповіддю. Працює без відключення графічного інтерфейсу та без випадкового сну () або 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 призначений для координації мотексного очікування між різними потоками. Але що робить цю роботу те, що метод очікування має час очікування. Якщо його викликати таким чином, він працює точно так само, як функція сну, але для синхронізації використовує цикл подій Qt. Отже, жодні інші події або користувальницький інтерфейс не блокуються, як це робить звичайна функція сну Windows.

Як бонус ми додали функцію CancelSleep, щоб дозволити іншій частині програми скасувати функцію "сплячого режиму".

Що нам сподобалось у цьому, так це те, що він легкий, багаторазовий та повністю автономний.

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

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


Принаймні, поточна реалізація в qt5 , схоже, взагалі не взаємодіє з циклом подій, тому з pov "обробки подій" це "переривний сон".
joonas

6

Оскільки ви намагаєтесь "протестувати якийсь код класу", я справді рекомендую навчитися користуватися QTestLib . Він надає простір імен QTest та модуль QtTest, які містять ряд корисних функцій та об'єктів, включаючи QSignalSpy , за допомогою яких можна перевірити, чи видаються певні сигнали.

Оскільки ви врешті-решт інтегруєтесь із повним графічним інтерфейсом, використання 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(), безумовно, було б простіше, якщо все, що вас цікавить, - перевірити кілька речей у командному рядку.

EDIT : На основі коментаря, ось необхідні схеми використання.

По-перше, вам потрібно відредагувати файл .pro, щоб включити qtestlib:

CONFIG += qtestlib

По-друге, вам потрібно включити необхідні файли:

  • Для простору імен QTest (що включає qSleep):#include <QTest>
  • Для всіх елементів в QtTestмодулі: #include <QtTest>. Це функціонально еквівалентно додаванню включення для кожного елемента, що існує в просторі імен.

Як включити QTestlib? Схоже, #include <QTestlib.h> не може його знайти.
Zac,

6

Щоб використовувати стандартну функцію сну, додайте у свій файл .cpp наступне:

#include <unistd.h>

Починаючи з версії 4.8 Qt, доступні такі функції сну :

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

1
ці функції захищені!
СААД

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

};

Якщо це робиться в потоці графічного інтерфейсу, це, очевидно, вводить 1 секундне відставання графічного інтерфейсу перед тим, як відповідати на події користувача. Але якщо ви можете жити з ним, це рішення, мабуть, найпростіше у впровадженні, і навіть Qt схвалює його у своїй статті " Основи потокової роботи" (див. Розділ " Коли використовувати альтернативи потокам ").


1

Якщо ви хочете зробити це міжплатформеним методом, загальним шаблоном є виведення з QThread і створення функції (статичної, якщо хочете) у своєму похідному класі, яка буде викликати одну з функцій сну в QThread.


або ще краще - дивіться останній коментар під питанням (станом на сьогодні), дякую за натхнення :)
mlvljr
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.