Зависание GUI при изменении QProgressBar из QThread с помощью сигналов

323
07 октября 2017, 21:11

Зависает GUI при изменении QProgressBar из QThread с помощью сигналов.

Код:

#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QFileDialog>
#include <QThread>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
public slots:
private slots:
void on_pushButton_1_clicked();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H

#include "widget.h"
#include "worker.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_1_clicked()
{
QString filename = QFileDialog::getOpenFileName(
                this,
                "Открыть файл",
                "",
                "Все файлы (*.*)",
                nullptr,
                QFileDialog::DontUseNativeDialog
                );
if (filename != QString("")) {
    ui->label_2->setText(filename);
    QThread *thread = new QThread;
    Worker *worker = new Worker(filename);
    worker->moveToThread(thread);
    connect(thread, &QThread::started, worker, &Worker::process);
    connect(worker, &Worker::finished, thread, &QThread::quit);
    connect(worker, &Worker::finished, worker, &QObject::deleteLater);
    connect(thread, &QThread::finished, thread, &QObject::deleteLater);
    connect(worker, &Worker::progressRangeChanged, ui->progressBar, &QProgressBar::setRange);
    connect(worker, &Worker::progressValueChanged, ui->progressBar, &QProgressBar::setValue);
    thread->start();
}
}
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
#include <QDebug>
#include <QFile>
#include <QThread>
class Worker : public QObject
{
Q_OBJECT
QString workerFilename;
public:
explicit Worker(QString filename, QObject *parent = nullptr);
virtual ~Worker();
signals:
void finished();
void error(QString err);
void progressRangeChanged(int min,int max);
void progressValueChanged(int progress);
public slots:
void process();
};
#endif // WORKER_H
#include "worker.h"
Worker::Worker(QString filename, QObject *parent) : QObject(parent)
{
workerFilename = filename;
}
Worker::~Worker()
{
}
void Worker::process()
{
//    qDebug()<<workerFilename;
QFile file(workerFilename);
int fileSize = file.size();
emit progressRangeChanged(0, fileSize);
for (int i = 0; i <= fileSize; i++) {
    emit progressValueChanged(i);
}
emit finished();
}

QProgressBar обновляется. GUI виджета зависают, окно нельзя переместить.

Подскажите, пожалуйста, в чем ошибка.

Answer 1

Эта задача достаточно типичная. Давайте представим, что в файле миллион строк (а иначе зачем вообще нужно было бы отображать прогресс?) И на каждую строку будет делать обновление прогресса (что бы пользователь его видел). Самые ходовые мониторы сейчас - это fullhd и им подобные, которые имеют 1920 точек по ширине. Так как полоса прогресса вряд ли будет занимать весь экран, я думаю, 1000 пикселей удобное число и вполне реальное для ширины самого прогресса.

А теперь вопрос. Увидит ли пользователь разницу между 9000 и 9500 итеррацией цикла? Нет, не увидит. Потому что обе будут занимать 9 (или 10) пикселей ширины полосы. Если посидеть помедитировать, то станет ясно, что обновлять чаще, чем раз в 1000 итерраций нет смысла (1000 это 1000000 строк файле на 1000 пикселей ширины экрана).

По собственному опыту скажу, что на самом деле можно обновлять раз в 5-10 пикселей и пользователь будет счастлив.

Как это исправить самым простым способом?

Вместо

emit progressValueChanged(i);

написать строку вот так

if (i % 1000 == 0) {
  emit progressValueChanged(i);
}

вместо 1000 лучше подставить свою константу, которая должна быть не меньше, чем fileSize деленный на ширину прогресса (а оптимально - ещё и умножить на 5-10).

И самое главное, не забыть в самом конце добавить строку, которая подвинет прогресс в самый конец (на 100%). А то тут как повезет (если кол-во записей будет кратно 1000, то добежит, а иначе нет).

READ ALSO
Ошибка в cstdio (нет fgetpos и fsetpos)

Ошибка в cstdio (нет fgetpos и fsetpos)

Здравствуйте! Пытаюсь установить YouCompleteMe для подсказок в Vim

403
Ошибка линковки Qt+VS

Ошибка линковки Qt+VS

Использую библиотеки Qt56 под VS2013

361
Скролинг jQueryUI Sortable списка на сенсорном экране

Скролинг jQueryUI Sortable списка на сенсорном экране

На веб странице имеется JQuery sortable списокТакже добавил на страницу jQuery UI Touch Punch для поддержки сенсорных экранов

445
Как преобразовать число в элементе

Как преобразовать число в элементе

Как посредством JS/jQuery преобразовать число в элементе? Например, 350 умножить на 057 и заменить $ на руб

289