title: Qt: Workaround for slow vertical scrolling in QGraphicsView with many items
body: <p><strong>EDIT</strong> I found a surprisingly easy workaround, see end of the post. Nevertheless, I'm still open for comments and suggestion since my "fix" is clearly not how things are intended to work...</p>
  
  <p>My Qt/PyQt Application has a QGraphicsView with a large Scene with many items stacked vertically. Verical Scrolling and other mouse interaction is very slow, but only for the exact upper half of the Scene, after scrolling to the second half, Mouse interaction suddenly becomes snappy. This behaviour makes me think there must be an issue with some BSTree holding the items, but I am not familiar with the Qt internals. I found an old Bug-Report (<a href="https://bugreports.qt.io/browse/QTBUG-11059" rel="nofollow">QTBUG-11059</a>) for Qt 4.7, precisely describing my problem, but it was closed without any fix. I was able to reproduce this in Qt 5.4 &amp; 5.5, confirming that this is a Qt-Problem, not a performance Problem of Python/PyQt.</p>
  
  <p>I am unsure what the best fix/workaround for this issue would be:</p>
  
  <ul>
  <li>Is there some hidden optimisationflag/setting I missed that yields better performance? I tried setItemIndexMethod(QGraphicsScene.NoIndex) but it was very slow. Also switching to QOpenGLWidget was not faster.</li>
  <li>I've read that painting to a QWidget is sometimes faster than QGraphicsView, but it would by a lot of extra work for the mouse handling I think. Are there other kinds of implementation possible?</li>
  <li>The Workaround proposed in the Bugreport was a scene horizontally stacked item, rotated 90°. This looks like a dirty but feasible option. But later on I want to implement more complex scenes with more mouse interaction and being forced to translate every coordinate would be a real pain to develop and to debug. Nevertheless it shows that the GraphicsView is generally capable of taking this complexity. </li>
  <li>is it possible to implement some kind of tiling, where items dynamically get attached and removed from the scene based on the visible area? Would this improve render speed? Is there any example code I can learn from?</li>
  </ul>
  
  <p>I am thankful for any comments and suggestions on this bug.</p>
  
  <p>Minimal code examples:</p>
  
  <p>C++ Qt example, originally posted on <a href="https://bugreports.qt.io/browse/QTBUG-11059" rel="nofollow">https://bugreports.qt.io/browse/QTBUG-11059</a></p>
  
  <pre><code>#include &lt;QApplication&gt;
  #include &lt;QGraphicsScene&gt;
  #include &lt;QGraphicsView&gt;
  #include &lt;QGraphicsRectItem&gt;
  
  int main( int argc, char *argv[] )
  {
  QApplication app( argc, argv );
  
  QGraphicsScene s;
  QGraphicsScene s2;
  
  for( int i=0;i&lt;40000;i++ ) {
      QGraphicsRectItem *ri = new QGraphicsRectItem( 0, 0, 40, 20 );
      ri-&gt;setPos( 0, i*25 );
      s.addItem( ri );
      ri = new QGraphicsRectItem( 0, 0, 20, 40 );
      ri-&gt;setPos( i*25, 0 );
      s2.addItem( ri );
  }
  QGraphicsView v;
  v.setWindowTitle( "BAD" );
  v.setScene( &amp;s );
  v.show();
  QGraphicsView v2;
  v2.setWindowTitle( "GOOD" );
  v2.rotate( 90 );
  v2.setScene( &amp;s2 );
  v2.move( v.pos()+QPoint(v.width()+20,0) );
  v2.show();
  
  return app.exec();
  }
  </code></pre>
  
  <p>My (slightly more complex) Python3/PyQt5 eample:</p>
  
  <p>I am working on Linux64, Python 3.4.3 (Anaconda 2.3.0), Qt 5.5</p>
  
  <pre><code>import random
  from PyQt5.QtWidgets import *
  from PyQt5.QtGui import *
  from PyQt5.QtCore import *
  import sys
  
  
  class  Track(QGraphicsObject):
      def __init__(self, pos=QPointF(),width=800,):
          super(Track, self).__init__()
          self.topleft = pos
          self.setPos(self.topleft)
          self.width= width
          self.height = 20
          self.pressed = False
  
          #caching boundingrect and shape improves speed
          self.br = QRectF(0,0,self.width+1,self.height+1)
          self.sh = QPainterPath()
          self.sh.addRect(0, 0, self.width, self.height)
  
      def mousePressEvent(self, event):
              self.pressed = not self.pressed
              self.update()
              super(Track, self).mousePressEvent(event)
  
      def boundingRect(self):
          return self.br
  
      def shape(self):
          return self.sh
  
      def paint(self, painter, option, widget):
          painter.setPen(QPen(Qt.black,0))
          if self.pressed:
              painter.setBrush(Qt.darkGreen)
          else:
              painter.setBrush(Qt.lightGray)
          painter.drawRect(0, 0,self.width,self.height)
  
  
  class TrackView(QGraphicsView):
      def __init__(self):
          super(TrackView, self).__init__()
          self.gs = QGraphicsScene(self)
          self.setScene(self.gs)
          self.tracks = []
          for i in range(100000):
              rect = Track(QPointF(10,i*30))
              self.tracks.append(rect)
              self.gs.addItem(rect)
          self.gs.setSceneRect(0,0,1200,self.tracks[-1].topleft.y()+50)
          self.resize(1200,800)
  
  def main():
      app = QApplication(sys.argv)
      view = TrackView()
      view.show()
      sys.exit(app.exec_())
  
  
  if __name__ == '__main__':
      main()
  </code></pre>
  
  <p><strong>EDIT: I found a workaround</strong>
  Qt seems to have a problem with the GraphicsScene with height >> width (tall and narrow). The solution would be to set the sceneRect to width=height and disable the horizontal scroll bar. Ie. <code>self.gs.setSceneRect(0,0,1200,self.tracks[-1].topleft.y()+50)</code> becomes <code>self.gs.setSceneRect(0,0,3000050,3000050)</code></p>