问题
What is the proper way to draw thousands of rects
in QT (around 100,000 or more)?
I tried:
- Simple with
paintEvent()
ofQWidget
. - Drawing objects to
QImage
then this image toQWidget
. - Using
QGraphicsScene
(maybe I didn't use it properly, I just addedrects
to scene)
Every time drawing was really slow and I don't have more ideas on how to do this (maybe with opengl/directx but this doesn't sound like a good idea). I know that there exist applications that do that so there should be some way.
EDIT:
I wonder how drawRects()
work? Is there a chance that filling some uchar*
array and passing it to QImage
will be better?
回答1:
The first trick is to do the drawing in a separate thread onto a QImage
, then pass that into the main thread. This won't make it quicker, but it'll make it not block the GUI thread.
// https://github.com/KubaO/stackoverflown/tree/master/questions/threaded-paint-36748972
#include <QtWidgets>
#include <QtConcurrent>
class Widget : public QWidget {
Q_OBJECT
QImage m_image;
bool m_pendingRender { false };
Q_SIGNAL void hasNewRender(const QImage &);
// Must be thread-safe, we can't access the widget directly!
void paint() {
QImage image{2048, 2048, QImage::Format_ARGB32_Premultiplied};
image.fill(Qt::white);
QPainter p(&image);
for (int i = 0; i < 100000; ++i) {
QColor c{rand() % 256, rand() % 256, rand() % 256};
p.setBrush(c);
p.setPen(c);
p.drawRect(rand() % 2048, rand() % 2048, rand() % 100, rand() % 100);
}
emit hasNewRender(image);
}
void paintEvent(QPaintEvent *) {
QPainter p(this);
p.drawImage(0, 0, m_image);
}
public:
Widget(QWidget * parent = 0) : QWidget(parent) {
this->setAttribute(Qt::WA_OpaquePaintEvent);
setMinimumSize(200, 200);
connect(this, &Widget::hasNewRender, this, [this](const QImage & img) {
m_image = img;
m_pendingRender = false;
update();
});
refresh();
}
Q_SLOT void refresh() {
if (!m_pendingRender) {
m_pendingRender = true;
QtConcurrent::run([this] { paint(); });
}
}
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
Widget w;
QPushButton button{"Refresh", &w};
button.connect(&button, &QPushButton::clicked, &w, [&w]{ w.refresh(); });
w.show();
return app.exec();
}
#include "main.moc"
As a separate concern, you can then split the drawing across multiple parallel jobs, by clipping each job's painter to a sub-area of the shared image, and noting that fully clipped rectangle draws are no-ops, and partially clipped ones will only fill the pixels they affect.
回答2:
Solution which I found:
Create array of uint32_t
which can contain all pixels of widget
, fill it using memcpy()
. Create QImage
with this array and use drawImage()
to show it.
It can have some optimization like (for profiler) merging rects
that are continues ( start time second is equal to end of first ). Don't draw rects
that are out of time bounds. Maybe skip too small rects
.
For drawing things like text, tool tips you can still use Qt
functions.
For alpha blending in simplest case you can just take existing values, blend them in loop and write blended values or maybe use SIMD
for this.
Of course for more complex shapes it will get harder to draw but still, I think, it will be faster than using Qt
functions.
来源:https://stackoverflow.com/questions/36748972/drawing-thousands-of-rects-quickly