I have around 1000 graphics item in my QGraphicsScene
. I want to move all of these 1000 items to new position. New positions don't relate to each other and all of them should be done at the same time.
One way is to iterate through these 1000 items and call setPos
for each one ! I think this will block user interface. Another way is to draw an image in another thread and to set this image as a result in QGraphicsScene!
May you have another idea.I'm looking forward to hearing that !
Qt drawing can be very quick if you understand how it works, even if you want to draw, for example, 1000 fish all moving independently.
In the case of a large number of items, the worst way to handle this is to create a separate QGraphicsItem / QGraphicsObject for each item and try to move and draw them all independently. One major issue people don't realise here is that when the paint(QPainter * painter...) function is called, they set the pen and brush on the painter. Normally, that's ok, but there is an overhead doing this as internally, the graphics pipeline will be stalled. For 1000 items, that's really going to slow things down.
Instead, if we design the fish as a school of fish and create just one QGraphicsItem, we can keep track of their positions internally and have the paint function called just once.
class SchoolOfFish : QGraphicsObject // QGraphicsObject for signals / slots
{
Q_OBJECT
public:
void UpdateFish();
protected:
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0); // overloaded paint function
private:
QList<QPoint> m_fishPositionList;
};
Note that all the positions of the fish are kept in a QList of QPoint objects. There are several optimisations that can be done here. Firstly, I often see people updating item positions in the paint function, which causes poor performance; Only drawing functionality should be done in paint.
Updating the fish positions can initially be done on a timer, perhaps aiming for 30 frames per second. If this is too slow, then we could create a separate thread that updates all the fish positions and emits the list back to the SchoolOfFish object; all graphics rendering must be done on the main thread.
This method is actually just treating the school of fish as a particle system. After designing the system this way, if required, the last optimisation I'd look to make would be moving to OpenGl. However, note that you can actually get the standard Qt paint calls to use OpenGl as the docs of QWidget state
To render using OpenGL, simply call setViewport(new QGLWidget). QGraphicsView takes ownership of the viewport widget.
For drawing large numbers of items in Qt you might be better served by moving to a lower level drawing interface. OpenGL, for example, can be implemented onto a QWindow (which can be anchored into your main window), see http://qt-project.org/doc/qt-5.0/qtgui/openglwindow.html. OpenGL can be a bit of a pain to get into but you won't have to worry about blocking, my current project does exactly this to render out 300,000 coloured squares in about 100 msec.
A substantial speed up of setPos()
in dynamic scenes can be achieved with:
QGraphicsScene scene;
scene.setItemIndexMethod(QGraphicsScene.NoIndex);
This is much faster than the default QGraphicsScene.BspTreeIndex
for dynamic scenes, but comes at a cost, as events that need to query the QGraphicsScene
will now be substantially slower (e.g. hover events).
Another way to speed things up is to reduce the total number of objects. If the scene uses a lot of QGraphicsItemGroup
, one can try to replace them with a plain QGraphicsItem
and override it's paint()
method instead of using child objects.
That said, even with this optimizations in place I find the performance of QGraphicsScene still pretty miserable once you go beyond a few hundred objects. QtQuick seems to handle large object collections much better.
来源:https://stackoverflow.com/questions/18397603/how-to-move-around-1000-items-in-a-qgraphicsscene-without-blocking-the-ui