如果绘制是开销,您可以将其移动到另一个线程,这样它就不会锁定您的应用程序的事件循环,并且完成后结果将被更新。
由于您想要任意绘图,因此实际上没有“将数据转换为位图”的神奇方法,您必须使用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个点,所以无论如何它都不慢。
| 归档时间: |
|
| 查看次数: |
2541 次 |
| 最近记录: |