在 Qt 中使用像素图或位图快速绘图

Tat*_*nho 2 plot qt bitmap repaint pixmap

我需要在 Qt 中快速绘制小部件,但我不想使用任何准备好的东西。我有一些浮动数据,例如QVector< float >数据,我需要在小部件上绘制它。但我不希望使用paintEventQPainter直接就可以了。有没有机会将此数据转换为位图(像素图?)并直接加载它以显示在小部件上?如何从浮点数创建位图?

dte*_*ech 6

如果绘制是开销,您可以将其移动到另一个线程,这样它就不会锁定您的应用程序的事件循环,并且完成后结果将被更新。

由于您想要任意绘图,因此实际上没有“将数据转换为位图”的神奇方法,您必须使用QPainter来绘制您的东西。

这是一个如何使用异步绘图仪对象的快速示例:

class Plotter : public QObject {
    Q_OBJECT
public:
    Plotter(QSize size, QRectF range, QVector<double> data)
        : _size(size), _range(range), _data(data) { }

signals:
    void done(QImage);
    void cleanup();

public slots:
    void plot() {
        QElapsedTimer t;
        t.start();
        QImage img(_size, QImage::Format_ARGB32_Premultiplied);
        img.fill(Qt::white);
        QPainter p(&img);
        QPen pen(Qt::darkBlue);
        pen.setWidth(1);
        p.setPen(pen);
        for (int i = 0; i < _data.size(); i += 2) {
            p.drawPoint(map(_data[i], _data[i + 1]));
        }
        qDebug() << "plotted in" << t.elapsed() << "msec";
        emit done(img);
        emit cleanup();
    }

private:
    inline QPointF map(double x, double y) {
        return QPointF(_size.width() * (x / (_range.width() - _range.x())),
                       _size.height() * (y / (_range.height() - _range.y())));
    }

    QSize _size;
    QRectF _range;
    QVector<double> _data;
};
Run Code Online (Sandbox Code Playgroud)

绘图仪是用它的大小、范围和数据参数创建的,我使用 aQRectF作为范围,基本上使用 x/width 和 y/height 作为水平和垂直范围。就示例而言,绘图仪是一个非常简单的实现,它使用该map()方法将数据点“标准化”到以线性方式绘制的图像区域。我还添加了一个计时器来查看绘制所有点需要多长时间。

这是用于创建绘图、填充数据并显示结果的示例小部件:

class Widget : public QWidget {
    Q_OBJECT
public:
    Widget(QWidget * parent = 0) : QWidget(parent) {
        // prepping data
        QVector<double> data;
        data.reserve(200000);
        for (int i = 0; i < 200000; ++i) data.append((double)qrand() / RAND_MAX);

        // create plotter and thread
        Plotter * p = new Plotter(size(), QRectF(0, 0, 1, 1), data);
        QThread * thread = new QThread;
        p->moveToThread(thread);

        // do connections
        connect(thread, SIGNAL(started()), p, SLOT(plot()));
        connect(p, SIGNAL(done(QImage)), this, SLOT(updatePlot(QImage)));
        connect(p, SIGNAL(cleanup()), thread, SLOT(quit()));
        connect(thread, SIGNAL(finished()), p, SLOT(deleteLater()));
        connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

        thread->start();
    }

protected:
    void paintEvent(QPaintEvent *) {
        QPainter p(this);
        p.drawImage(QPoint(0, 0), plot);
    }

public slots:
    void updatePlot(QImage p) {
        plot = p;
        repaint();
    }

private:
    QImage plot;
};
Run Code Online (Sandbox Code Playgroud)

对于我的示例,我使用 0-1 范围内的 200 000 个值填充数据。然后创建具有小部件大小的绘图仪,X 和 Y 的范围为 0-1,创建线程并将绘图仪移动到它,进行必要的连接并启动线程。启动时,线程将调用plot()插槽,它将绘图结果发送到小部件的updatePlot()插槽。发送结果后,绘图仪将退出线程事件循环,这将删除绘图仪对象和线程,以免它们泄漏。

至于这有多快,我的i7桌面在8毫秒内绘制了200 000个点,所以无论如何它都不慢。