How to improve QGraphicsView performance in a 2D static scene with many items? (no way to solve it?)



  • If understood correctly, QGraphicsView is supposed to handle million items efficiently.

    In my application, I only have some few thousand and the performance is already very bad. When the View is showing the whole scene, zooming, hoverEnvents and any other stuff becomes impossible.

    I have tried to create a parent-child relationship among items, and try different optimization flags, but still the same result. I really hope that I'm making some stupid mistake, but after several days looking for a way to solve the problem, I did not find any solution.

    I would really appreciate any help!

    This reproduces my problem:
    @
    import sys
    import random
    from PyQt4.QtGui import *

    NO_INDEX = False
    OPTIMAZE = False
    ITEM_COORD_CACHE = False
    ITEM_DEVICE_CACHE = False
    NESTED_ITEMS = False
    
    
    class TestItem(QGraphicsEllipseItem):
        def paint(self, painter, option, index):
            return QGraphicsEllipseItem.paint(self, painter, option, index)
    
        def hoverEnterEvent (self, e):
            self.setBrush(QBrush(QColor("orange")))
    
        def hoverLeaveEvent(self,e):
            self.setBrush(QBrush(None))
    
    if __name__ == '__main__':
        n = int(sys.argv[1]) # Number of items. With 5000 I already
                               # have performance problems
        app = QApplication(sys.argv)
        scene = QGraphicsScene()
    
        # Populates scene
        prev = None
        for i in xrange(n):
            # Random geometry and position
            r1 = random.randint(10, 100)
            r2 = random.randint(10, 100)
            x = random.randint(0, 500)
            y = random.randint(0, 500)
    
            item = TestItem(x, y, r1*2, r2*2)
            item.setAcceptsHoverEvents(True)
    
            if NESTED_ITEMS: 
                # Creates a parent child structure among items
                if not prev:
                    scene.addItem(item)
                else:
                    item.setParentItem(prev)
                prev = item
            else:
                scene.addItem(item)
    
            if ITEM_COORD_CACHE:
                item.setCacheMode(QGraphicsItem.ItemCoordinateCache)
            elif ITEM_DEVICE_CACHE:
                item.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
    
        # Creates View
        view = QGraphicsView(scene)
        # Sets basic Flags for nice rendering 
        view.setRenderHints(QPainter.Antialiasing or QPainter.SmoothPixmapTransform)
    
        if NO_INDEX:
            view.setItemIndexMethod(QGraphicsScene.NoIndex);
            
        if OPTIMAZE:
            view.setOptimizationFlags(QGraphicsView.DontAdjustForAntialiasing
                                      or QGraphicsView.DontClipPainter
                                      or QGraphicsView.DontSavePainterState)
    
        view.show()
        sys.exit(app.exec_())@
    
    • Intel(R) Xeon(R) CPU E5410 @ 2.33GHz
    • nVidia Corporation G84 [Quadro FX 1700]
    • Ubuntu 9.04 64 bits
    • qt4 4.5.3
    • python-qt4 4.6


  • I converted your sample to C++ (see below) and ran it through a profiler. Your main bottleneck is QPainter::drawEllipse().

    The expensive parts, especially, seem to be QPainterPath::toFillPolygons() and QTesselator::tesselate().

    This isn't especially surprising, as with thousands of items, they all overlap, meaning hover and unhover triggers for all of them, which means a full redraw of that item each time.

    @#include <QApplication>
    #include <QGraphicsScene>
    #include <QGraphicsView>
    #include <QGraphicsEllipseItem>
    #include <QDebug>

    bool NO_INDEX = false;
    bool OPTIMAZE = false;
    bool ITEM_COORD_CACHE = false;
    bool ITEM_DEVICE_CACHE = false;
    bool NESTED_ITEMS = false;

    class TestItem : public QGraphicsEllipseItem
    {
    public:
    TestItem(qreal x, qreal y, qreal width, qreal height, QGraphicsItem *parent = 0) : QGraphicsEllipseItem(x, y, width, height, parent)
    {

    }
    
    void paint(QPainter *painter, const QStyleOptionGraphicsItem * option, QWidget
            *index)
    {
        return QGraphicsEllipseItem::paint(painter, option, index);
    }
    
    void hoverEnterEvent(QGraphicsSceneHoverEvent *e)
    {
        this->setBrush(QBrush(QColor("orange")));
    }
    
    void hoverLeaveEvent(QGraphicsSceneHoverEvent *e)
    {
        this->setBrush(QBrush("red"));
    }
    

    };

    int main(int argc, char **argv)
    {
    QApplication app(argc, argv);
    QGraphicsScene scene;

    // Populates scene
    QGraphicsItem *prev = NULL;
    
    int n = atoi(argv[1]);
    qDebug() << "Adding " << n << " items";
    
    for (int i = 0; i != n; ++i) {
        // Random geometry and position
        int r1 = rand() % 100 + 10;
        int r2 = rand() % 100 + 10;
        int x = rand() % 500;
        int y = rand() % 500;
    
        QGraphicsItem *item = new TestItem(x, y, r1*2, r2*2);
        item->setAcceptsHoverEvents(true);
    
        if (NESTED_ITEMS) {
            // Creates a parent child structure among items
            if (!prev)
                scene.addItem(item);
            else
                item->setParentItem(prev);
            prev = item;
        }
        else
            scene.addItem(item);
    
        if (ITEM_COORD_CACHE)
            item->setCacheMode(QGraphicsItem::ItemCoordinateCache);
        else if (ITEM_DEVICE_CACHE)
            item->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
    }
    
    // Creates View
    QGraphicsView view;
    view.setScene(&scene);
    
    // Sets basic Flags for nice rendering 
    view.setRenderHints(QPainter::Antialiasing |
            QPainter::SmoothPixmapTransform);
    

    // if (NO_INDEX)
    // view.setItemIndexMethod(QGraphicsScene::NoIndex);

    if (OPTIMAZE)
        view.setOptimizationFlags(QGraphicsView::DontAdjustForAntialiasing
                                  | QGraphicsView::DontClipPainter
                                  | QGraphicsView::DontSavePainterState);
    
    view.show();
    return app.exec&#40;&#41;;
    

    }@



  • As for how to improve your specific example... I don't know. One idea might be to not immediately setBrush() on hover/unhover, but set a timer to trigger the change 50ms or so later, which would mean that the cursor might have moved to a different item.

    Stacking order is also an idea, i.e. make it only affect the topmost item, but that's probably going to be complicated.



  • Robin, this may be an aside, but what profiler did you use? Any suggestions?



  • Callgrind from the valgrind suite comes to mind.



  • Again: please: stop necroposting. Start a brand new thread :)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.