如何在添加项目后顺畅更新QGraphicsView内的QGraphicsScene?

时间:2015-11-07 13:15:09

标签: c++ multithreading qt qgraphicsscene

我正在运行一些测试,以便找出在单独的线程中更新QDialog的GUI的最佳方法。 我做了以下事情:

  • main.cpp (未触及)

    #include "dialog.h"
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        Dialog w;
        w.show();
    
        return a.exec();
    }
    
  • dialog.h :SLOT draw_point(QPointF)连接到worker类发出的信号,以便更新场景;在UI中,只有两个按钮,一个显然开始计算的“开始”按钮,以及一个中断计算的“停止”按钮。

    #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();
    }
    
  • 问题:

  • 信号new_point发射得太快,因此场景无法跟上更新,因此场景会以块的形式更新。顺利更新它的唯一方法似乎是在usleep(100000)循环中添加for,但我不想这样做,因为我认为这是一种不好的做法。

  • doWork()方法和Qdialog线程中检查控制台中关于已用时间的值,似乎for循环执行速度非常快,通常不到100毫秒。 Qdialog线程需要更多时间来处理所有更新,即将所有点绘制到场景中。有更好的方法来更新场景吗?我在一些论坛上阅读创建一个QImage,然后将其传递到场景中,你能为我的案例提供一个简单的例子吗?

  • 我也可以使用QCoreApplication::processEvents()并在GUI线程中进行所有计算,实际上GUI是响应式的,场景平滑更新。但绘制所有点所需的时间远远超过用单独的线程绘制它们所需的时间。 所以我该怎么做?提前谢谢你。

0 个答案:

没有答案