I'm running some tests in order to undestand what is the best way to update the GUI of a QDialog in a separate thread. I did the following:
main.cpp (untouched)
#include "dialog.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); Dialog w; w.show(); return a.exec(); }
dialog.h: the SLOT
draw_point(QPointF)
is connected to the signal emitted by theworker
class, in order to update the scene; int he UI there are only two buttons, a Start button that obviously starts the computation, and a Stop button that interrupts the computation.#ifndef DIALOG_H #define DIALOG_H #include <QDialog> #include <QElapsedTimer> #include <QGraphicsScene> #include <QThread> #include "worker.h" namespace Ui { class Dialog; } class Dialog : public QDialog { Q_OBJECT public: explicit Dialog(QWidget *parent = 0); ~Dialog(); private: Ui::Dialog *ui; QGraphicsScene scene; QThread t; QElapsedTimer chronometer; worker work; public slots: void draw_point(QPointF p); private slots: // for Start button void on_pushButton_clicked(); // for Stop button void on_pushButton_2_clicked(); void print_finished(); }; #endif // DIALOG_H
dialog.cpp
#include "dialog.h" #include "ui_dialog.h" #include <QDebug> #include <QElapsedTimer> #include <QTimer> Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog) { qDebug() << "Starting dialog thread" << thread(); ui->setupUi(this); scene.setSceneRect(0, 0, 400, 400); ui->graphicsView->setScene(&scene); ui->graphicsView->setFixedSize(400, 400); work.moveToThread(&t); connect(&work, SIGNAL(new_point(QPointF)), this, SLOT(draw_point(QPointF))); connect(&t, SIGNAL(started()), &work, SLOT(doWork())); connect(&work, SIGNAL(finished()), &t, SLOT(quit())); connect(&work, SIGNAL(finished()), this, SLOT(print_finished())); } Dialog::~Dialog() { delete ui; } void Dialog::draw_point(QPointF p) { scene.addEllipse(p.x(), p.y(), 1.0, 1.0); } // Start button void Dialog::on_pushButton_clicked() { t.start(); chronometer.start(); } // Stop button void Dialog::on_pushButton_2_clicked() { work.running = false; } void Dialog::print_finished() { qDebug() << "Finished dialog thread" << thread(); qDebug() << "after" << chronometer.elapsed(); }
worker.h
#ifndef WORKER_H #define WORKER_H #include <QObject> #include <QPointF> #include <QVector> class worker : public QObject { Q_OBJECT public: explicit worker(); bool running; signals: void new_point(QPointF); void finished(); public slots: void doWork(); }; #endif // WORKER_H
worker.cpp
#include "worker.h" #include <QDebug> #include <QElapsedTimer> #include <QPoint> #include <QTimer> #include <unistd.h> worker::worker() { qDebug() << "Worker thread" << thread(); running = true; } void worker::doWork() { qDebug() << "starting doWork thread" << thread(); int i = 0; QVector<QPoint> v; QElapsedTimer t; t.start(); while ((i < 100000) && running) { int x = qrand() % 400; int y = qrand() % 400; QPoint p(x, y); bool f = false; for (int j = 0; j < v.size() && !f; j++) if (v[i].x() == p.x() && v[i].y() == p.y()) f = true; if (!f) { emit new_point(p); i++; } } qDebug() << "elapsed time:" << t.elapsed(); qDebug() << "closing doWork thread" << thread(); emit finished(); }
PROBLEMS:
The signal
new_point
is emitted too fast, hence the scene is not able to keep up updating it, hence the scene is updated in blocks. the only way to make update it smoothly seems to be by adding ausleep(100000)
in thefor
loop, but I don't want to do this, since I really think it is a bad practice.Checking values in the console as regards the elapsed time in the
doWork()
method and in the Qdialog thread, it seems that thefor
loop executes very fast, often in less than 100 milliseconds. TheQdialog
thread takes instead much more time to process all the updates, that is, to draw all the points to the scene. Is there a better way to update the scene? I read on some forums to create a QImage and then pass it to the scene, could you provide a simple example for my case?I can also use
QCoreApplication::processEvents()
and do all computations in the GUI thread, and in fact the GUI is responsive, the scene updates smoothly. But the time required to draw all the points is much much more than the time required to draw them with a separate thread. So, what should I do? Thank you in advance.