[prev in list] [next in list] [prev in thread] [next in thread] 

List:       pykde
Subject:    Re: [PyQt] QGraphicsView very slow under Linux and Mac OS X
From:       Clemens Brunner <clemens.brunner () tugraz ! at>
Date:       2013-07-18 9:10:25
Message-ID: A69DF924-8064-40D2-810A-018AF16FBB72 () tugraz ! at
[Download RAW message or body]

Hi!

I investigated the problem a bit more, and it seems like it occurs only =
when I'm running KDE -- in Gnome, everything works as expected (the =
difference is 1). Since it is now working on both Windows, Mac OS X, and =
non-KDE systems, it is probably related to KDE (maybe the window =
manager). Funny though, since KDE is based on Qt :-).

So if anyone running KDE wants to confirm the problem, I've attached the =
test program again.

Concerning the C++ version, the problem is also there on KDE, only you =
don't see it when just looking at the timer intervals (because they're =
always 25ms irrespective of the window size). However, the exposedRect =
on KDE is still wrong, so I assume that the C++ is just a lot faster so =
that performance is not (yet) affected in my example.

Clemens


["graphicsviewtest.py" (graphicsviewtest.py)]

import sys
from PyQt4 import QtGui, QtCore

import random
import math

class SignalItem(QtGui.QGraphicsItem):
    def __init__(self, nr):
        super(SignalItem, self).__init__()
        self.setFlags(QtGui.QGraphicsItem.ItemUsesExtendedStyleOption)
        
        self.nr = nr
        self.fr = random.randint(3, 50)
        self.bg = QtGui.QColor(random.randint(200,255),random.randint(200,255),random.randint(200,255))
    
    def boundingRect(self):
        return QtCore.QRectF(0, self.nr*50, 10000, 50)
    
    def paint(self, painter, option, widget):
        painter.fillRect(option.exposedRect, self.bg)
        self.prevY = math.sin((int(option.exposedRect.left()) - 1)/float(self.fr))*10 + 25 + self.nr*50
        print(int(option.exposedRect.right()) - int(option.exposedRect.left()))
        for x in range(int(option.exposedRect.left()), int(option.exposedRect.right())):
            y = math.sin(x/float(self.fr))*10 + 25 + self.nr*50 
            painter.drawLine(x-1, self.prevY, x, y)
            self.prevY = y


class Dummy(QtCore.QObject):
    def __init__(self):
        super(Dummy, self).__init__()
        self.scene = QtGui.QGraphicsScene()

        for i in range(40):
            item = SignalItem(i)
            self.scene.addItem(item)

        self.view = QtGui.QGraphicsView(self.scene)
        self.view.show()

        self.startTimer(25)
        self.x = 0
        self.lastTimerEvent = None

    def timerEvent(self,event):
        self.x += 1
        self.view.horizontalScrollBar().setValue(self.x)
        #currentTime = QtCore.QTime.currentTime()
        #if self.lastTimerEvent:
        #    print self.lastTimerEvent.msecsTo(currentTime)
        #self.lastTimerEvent = currentTime


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    d = Dummy()
    sys.exit(app.exec_())


On 04/05/2013 08:33 PM, Clemens Brunner wrote:
> Hi!
> 
> I think I found the problem: https://bugreports.qt-project.org/browse/QTBUG-13573
> 
> The exposedRect returns the whole window width on Linux and Mac OS X, whereas it \
> correctly returns only the area that needs to be updated on Windows. 
> To illustrate this, just add the following line to my example code within the \
> paint() function just before the for loop: 
> print int(option.exposedRect.right()) - int(option.exposedRect.left())
> 
> On Windows, this is always 1, as expected. On Linux, this corresponds to the window \
> width. 
> This bug explains why it is so damn slow. However, it doesn't seem to occur on all \
> Linux platforms, since you guys get timer intervals as expected. Maybe you want to \
> check the output of the print statement above, but I assume it is 1 on your \
> platform. Which leads me to the question: how can we fix this bug? Or is there at \
> least a workaround? 
> I suspect that it might have something to do with the Intel graphics chip, which I \
> have in both my Linux and Mac machine. 
> [Update:] I just tested the behavior on my Mac again: in the first second, I get \
> the whole width for the exposedRect and large timer intervals (and therefore slow \
> performance). However, after about a second, I get the correct value of 1, because \
> Mac OS X switches from the Intel onboard chip to the nVidia graphics chip. This is \
> just a speculation, because disabling graphics switching (which should permanently \
> enable the nVidia chip) yields the same behavior. Strangely though, it does work on \
> my Mac now after this initial second (maybe some updated got installed that fixed \
> the problem?). 
> Any more ideas?
> 
> Clemens
> 
> 
> 
> On Apr 4, 2013, at 17:15 , Clemens Brunner <clemens.brunner@tugraz.at> wrote:
> 
> > On 04/03/2013 06:46 PM, Hans-Peter Jansen wrote:
> > 
> > > Which graphic driver do you use? (that doesn't tell us much, since the C++
> > > version behave with the same driver, just for the record..)
> > 
> > xf86-video-intel 2.21.5-1
> > intel-dri 9.1.1-1
> > 
> > > Might be worth to compare the C++ version (that you should publish hereš)
> > > and the Python versions with perf. Of course, they differ...
> > 
> > Attached.
> > 
> > > python versions, perf running for about 10 sec.
> > > 
> > > QT_GRAPHICSSYSTEM=raster perf record -f python graphicsviewtest.py
> > > 
> > > [...]
> > > 
> > > 
> > > QT_GRAPHICSSYSTEM=opengl perf record -f python graphicsviewtest.py
> > > 
> > > [...]
> > > 
> > > 
> > > The former looks nice, it's a great example, why PyQt rocks. The hottest
> > > areas are there, where they should be: down under, moving bits. Great.
> > > 
> > > But the latter looks strange indeed.
> > > 
> > > Phil, do you have any idea, why PyEval_EvalFrameEx is the top sucker in
> > > this scenario? This looks, like in opengl mode, it is evaluating some
> > > python expression in its hottest path (data type conversions or the like?).
> > 
> > My raster perf report doesn't look nice at all:
> > 
> > 14.30%  python2  libpython2.7.so.1.0       [.] PyEval_EvalFrameEx
> > 9.39%  python2  libQtGui.so.4.8.4         [.] 0x00000000001bf673
> > 8.99%  python2  sip.so                    [.] 0x000000000000b086
> > 6.10%  python2  libpython2.7.so.1.0       [.] lookdict_string
> > 4.02%  python2  libpython2.7.so.1.0       [.] PyDict_GetItem
> > 3.94%  python2  libpython2.7.so.1.0       [.] _PyType_Lookup
> > 3.01%  python2  libm-2.17.so              [.] 0x00000000000105e0
> > 2.27%  python2  libm-2.17.so              [.] feraiseexcept
> > 2.23%  python2  libpython2.7.so.1.0       [.] _PyObject_GenericGetAttrWithDict
> > 1.71%  python2  libpython2.7.so.1.0       [.] binary_op1
> > 1.50%  python2  libpython2.7.so.1.0       [.] PyType_IsSubtype
> > 1.40%  python2  libpython2.7.so.1.0       [.] PyErr_Restore
> > 1.33%  python2  QtGui.so                  [.] 0x000000000036120b
> > 1.27%  python2  libpython2.7.so.1.0       [.] PyObject_Malloc
> > 1.19%  python2  libc-2.17.so              [.] malloc
> > 
> > Same thing but even worse with opengl:
> > 
> > 15.49%  python2  i965_dri.so               [.] 0x000000000003ae99
> > 6.08%  python2  libpython2.7.so.1.0       [.] PyEval_EvalFrameEx
> > 5.96%  python2  libdrm_intel.so.1.0.0     [.] 0x0000000000007468
> > 5.60%  python2  libQtOpenGL.so.4.8.4      [.] 0x0000000000031262
> > 4.96%  python2  sip.so                    [.] 0x000000000000b055
> > 4.32%  python2  libdricore9.1.1.so.1.0.0  [.] 0x00000000001ea2b4
> > 2.76%  python2  libpython2.7.so.1.0       [.] lookdict_string
> > 2.11%  python2  libpython2.7.so.1.0       [.] PyDict_GetItem
> > 1.95%  python2  libpython2.7.so.1.0       [.] _PyType_Lookup
> > 1.42%  python2  libm-2.17.so              [.] 0x00000000000105c0
> > 1.32%  python2  libc-2.17.so              [.] __memcmp_sse4_1
> > 1.03%  python2  libc-2.17.so              [.] _int_malloc
> > 1.01%  python2  libm-2.17.so              [.] feraiseexcept
> > 0.93%  python2  libc-2.17.so              [.] __memcpy_ssse3_back
> > 
> > In both cases, PyEval_EvalFrameEx is at or near the top, and so are other Python \
> > things. 
> > For the sake of completeness, here's the perf output for the C++ version (which \
> > runs perfectly): 
> > 43.56%  graphicsviewtes  libQtGui.so.4.8.4         [.] 0x00000000001c0bb2         \
> > q 20.58%  graphicsviewtes  libm-2.17.so              [.] feraiseexcept
> > 15.48%  graphicsviewtes  libm-2.17.so              [.] 0x0000000000015622
> > 4.47%  graphicsviewtes  graphicsviewtest          [.] \
> > SignalItem::paint(QPainter*, QStyleOptionGraphicsItem 3.09%  graphicsviewtes  \
> > libQtGui.so.4.8.4         [.] QPen::dashPattern() const 1.17%  graphicsviewtes  \
> > libQtGui.so.4.8.4         [.] QTransform::map(QPointF const&) const 0.77%  \
> > graphicsviewtes  libc-2.17.so              [.] free 0.72%  graphicsviewtes  \
> > libQtGui.so.4.8.4         [.] QPainter::drawLines(QLine const*, int) 0.46%  \
> > graphicsviewtes  libpthread-2.17.so        [.] __pthread_mutex_unlock_usercnt \
> > 0.45%  graphicsviewtes  libpthread-2.17.so        [.] pthread_mutex_lock 
> > I tested this program on openSUSE (in a VirtualBox), and in contrast to Vincent, \
> > it doesn't work for me there either (same behavior as in my native Arch Linux). \
> > BTW, I use KDE and not Gnome, but I doubt that this is relevant. Furthermore, I \
> > still have the same bad behavior on my Mac. 
> > Clemens
> > <graphicsviewtest.cpp><graphicsviewtest.pro>
> 


["graphicsviewtest.py" (graphicsviewtest.py)]

import sys
from PySide import QtGui, QtCore

import random
import math

class SignalItem(QtGui.QGraphicsItem):
    def __init__(self, nr):
        super(SignalItem, self).__init__()
        self.setFlags(QtGui.QGraphicsItem.ItemUsesExtendedStyleOption)
        
        self.nr = nr
        self.fr = random.randint(3, 50)
        self.bg = QtGui.QColor(random.randint(200,255),random.randint(200,255),random.randint(200,255))
    
    def boundingRect(self):
        return QtCore.QRectF(0, self.nr*50, 10000, 50)
    
    def paint(self, painter, option, widget):
        painter.fillRect(option.exposedRect, self.bg)
        self.prevY = math.sin((int(option.exposedRect.left()) - 1)/float(self.fr))*10 + 25 + self.nr*50
        print int(option.exposedRect.right()) - int(option.exposedRect.left())
        for x in range(int(option.exposedRect.left()), int(option.exposedRect.right())):
            y = math.sin(x/float(self.fr))*10 + 25 + self.nr*50 
            painter.drawLine(x-1, self.prevY, x, y)
            self.prevY = y


class Dummy(QtCore.QObject):
    def __init__(self):
        super(Dummy, self).__init__()
        self.scene = QtGui.QGraphicsScene()

        for i in range(40):
            item = SignalItem(i)
            self.scene.addItem(item)

        self.view = QtGui.QGraphicsView(self.scene)
        self.view.show()

        self.startTimer(25)
        self.x = 0
        self.lastTimerEvent = None

    def timerEvent(self,event):
        self.x += 1
        self.view.horizontalScrollBar().setValue(self.x)
        #currentTime = QtCore.QTime.currentTime()
        #if self.lastTimerEvent:
        #    print self.lastTimerEvent.msecsTo(currentTime)
        #self.lastTimerEvent = currentTime


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    d = Dummy()
    sys.exit(app.exec_())


_______________________________________________
PyQt mailing list    PyQt@riverbankcomputing.com
http://www.riverbankcomputing.com/mailman/listinfo/pyqt

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic