необъяснимая задержка после завершения загрузки QProgressBar

У меня есть сигнал из цикла (который выполняет некоторые вычисления), который запускает обновление индикатора выполнения, расположенного в основном графическом интерфейсе, после завершения цикла индикатор выполнения обновляется до 100% (индикатор выполнения становится скрытым, когда процесс заканчивается), но чем есть задержка, индикатор выполнения остается на 100%, а иногда мышь меняется на занято, и только через несколько секунд индикатор выполнения становится скрытым (указывает мне, что задержка заканчивается), после этого цикла ничего нет, поэтому я ничего не могу думает о том, может сделать эту задержку.

  • Я должен отметить, что если вычисления цикла легкие (то есть не нужно выполнять много вычислений), такой задержки нет.

Сигнал излучения находится внутри класса на логическом уровне, я попробовал кое-что, включив <QtGui/QApplication> в этот класс (что мне кажется неправильным, так как это логический уровень, поэтому зачем ему нужны библиотеки QtGui, но я я только что-то тестирую), я поместил следующий код qApp->processEvents(); внутри цикла, и теперь все кажется более плавным, мышь не занята, но все же есть задержка (единственное, что я могу реагировать с помощью графического интерфейса, когда эта задержка происходит, но нет обновленных результатов, пока эта задержка не закончится).

Из-за теста с processEvents() я подумал, что это что-то связанное с потоками, но если да, то как я могу исправить поведение задержки, конечно, если кто-то думает, что это может быть что-то еще, скажите, пожалуйста.

Некоторый пример кода:

Класс логического слоя:

#include <QtGui/QApplication>
...

processMethod(...)
{
    Loop(...)
    {
        qApp->processEvents();
        emit processBarSignle(value);
        ...some calculations...
    }
    emit processBarSignle(100);
}

Слой просмотра (MainWindow):

on_btn_nextProcess_clicked()
{
    m_ui->pBar_process->setVisible(true);
    LogicClass->processMethod(...);
    m_ui->pBar_process->setVisible(false);
}

Спасибо


person GoldenAxe    schedule 18.01.2013    source источник


Ответы (2)


Попробуйте следующее:

#include <QtCore/QCoreApplication>
...

processMethod(...)
{
    Loop(...)
    {
        emit processBarSignle(value);
        QCoreApplication::processEvents();
        ...some calculations...
    }
    emit processBarSignle(100);
    QCoreApplication::processEvents();
}

processEvents() — это статический метод QCoreApplication, например, достаточно включить только QCoreApplication, который является частью библиотеки QtCore.

Кроме того, вы должны добавить processEvents() после обновления индикатора выполнения, а не до этого.

Обратите внимание, что processEvents() не возвращается до тех пор, пока не будет обработано каждое событие в очереди событий Qt. Если есть, например. кнопку Cancel, вам придется проверять, действительно ли пользователь отменил операцию каждый раз, когда вы вызывали processEvents().
Вы можете исключить пользовательские события, такие как щелчки мышью / нажатия клавиш, с помощью

QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents )

но это не позволит щелкнуть что-либо (например, кнопку «Отмена»), пока цикл активен.

В качестве дальнейшего примечания: он должен называться "processBarSi ng le" ;-)


Уточнение по темам и т. д.:

Весь ваш цикл и любые щелчки мыши и т. д. выполняются только в одном потоке. Если вы вызываете emit(), slot, связанный с этим signal, выполняется мгновенно (если слот фактически не находился в другом потоке). При этом цикл не продолжается!

Когда слот закончится, ваш цикл продолжится. В моем примере это означает, что будет вызван processEvents(). Теперь, если ваш слот обновил индикатор выполнения или сделал что-то еще, что вызвало перерисовку, в очереди событий будет событие перерисовки, и теперь произойдет эта перерисовка.
Если вы выполните processEvents() перед вызовом своего слота, будет нет события перерисовки, которое могло бы быть обработано в этот момент.

И снова цикл не продолжается до тех пор, пока processEvents() не будет полностью выполнено. Когда все ожидающие события будут обработаны, цикл продолжит ваши вычисления.

person Tim Meyer    schedule 18.01.2013
comment
С вашим методом все еще есть задержка (я думаю, задержка меньше, но трудно сказать). - person GoldenAxe; 18.01.2013

В вашем примере кода на самом деле есть только один поток. Когда вызывается on_btn_nextProcess_clicked(), он показывает индикатор выполнения, а затем запускает processMethod() в том же потоке. В идеале вы хотите разделить свой пользовательский интерфейс и логику обработки данных.

При инициализации создайте отдельный QThread, запустите его и переместите свой объект LogicClass в этот поток, вызвав logicClassObject->moveToThread([ваш новый поток]). Затем превратите processMethod() в слот, создайте сигнал startProcessing() в MainWindow и соедините их. Наконец, создайте слот processingDone() в MainWindow и слот finishedProcessing() в LogicClass и соедините их. Когда все это настроено, вы можете изменить свой код на следующее:

void LogicClass::processMethod(...)
{
    Loop(...)
    {
        emit processBarSignal(value);
        ...some calculations...
    }
    emit processingDone();
}

void MainWindow::on_btn_nextProcess_clicked()
{
    m_ui->pBar_process->setVisible(true);
    emit startProcessing(...);
}

void MainWindow::finishedProcessing()
{
    m_ui->pBar_process->setVisible(false);
}

Всякий раз, когда вы соединяете сигналы и слоты из двух отдельных потоков, многопоточность обеспечивается автоматически. Выдача сигнала в одном потоке приводит к тому, что событие помещается в очередь в другом, который вызывает слот только после того, как второй поток восстанавливает управление.

В этом случае поток пользовательского интерфейса запланирует событие в потоке обработки, чтобы начать обработку. Затем поток обработки будет постоянно планировать обновления значения progressBar и, наконец, запланировать событие, чтобы отключить индикатор выполнения, когда оно будет выполнено. Два потока будут выполняться в соответствии с планировщиком потоков ОС, и обработка не будет блокировать пользовательский интерфейс.

person Joey    schedule 18.01.2013
comment
Должен ли я использовать один и тот же метод для разделения пользовательского интерфейса и QGraphicsView? - person GoldenAxe; 18.01.2013
comment
QGraphicsView является частью пользовательского интерфейса и должен находиться в потоке пользовательского интерфейса. Вы получите ошибки, если переместите его. То, что вы захотите убрать из потока пользовательского интерфейса, — это любая тяжелая обработка, которую необходимо выполнить в качестве предварительного условия для настройки QGraphicsScene. - person Joey; 18.01.2013
comment
Кстати, я рекомендую вам ознакомиться с прекрасным обсуждением Майи Пош Как правильно использовать QThreads - person Joey; 18.01.2013