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

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.brun...@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>
> 

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

Reply via email to