tags:

views:

526

answers:

2

I need to customize the drawing process of a QGraphicsView, and so I override the drawItems method like this:

self.graphicsview.drawItems=self.drawer.drawItems

where self.graphicsview is a QGraphicsView, and self.drawer is a custom class with a method drawItems.
In this method I check a few flags to decide how to draw each item, and then call item.paint, like this:

def drawItems(self, painter, items, options):
    for item in items:
        print "Processing", item
        # ... Do checking ...
        item.paint(painter, options, self.target)

self.target is the QGraphicsView's QGraphicsScene.
However, once it reaches item.paint, it breaks out of the loop - without any errors. If I put conditionals around the painting, and for each possible type of QGraphicsItem paste the code that is supposed to be executed (by looking at the Qt git-sources), everything works.
Not a very nice solution though... And I don't understand how it could even break out of the loop?

A: 

The reason why the loop suddenly exits is that an Exception is thrown. Python doesn't handle it (there is no try: block), so it's passed to the called (Qt's C++ code) which has no idea about Python exceptions, so it's lost.

Add a try/except around the loop and you should see the reason why this happens.

Note: Since Python 2.4, you should not override methods this way anymore.

Instead, you must derive a new class from QGraphicsView and add your drawItems() method to this new class. This will replace the original method properly.

Don't forget to call super() in the __init__ method! Otherwise, your object won't work properly.

Aaron Digulla
But... My overriding method is called properly?
carlpett
That's more by chance than by intention.
Aaron Digulla
No, the overriding definitely works, I just tested it out.
Torsten Marek
Well, it only works if you call it on an instance. If you try things like that on a class, it fails.
Aaron Digulla
But he does it to an instance, which is more or less the idea of monkey-patching.
Torsten Marek
+3  A: 

There is an exception that occurs when the items are painted, but it is not reported right away. On my system (PyQt 4.5.1, Python 2.6), no exception is reported when I monkey-patch the following method:

def drawItems(painter, items, options):
    print len(items)
    for idx, i in enumerate(items):
        print idx, i
        if idx > 5:
            raise ValueError()

Output:

45
0 <PyQt4.QtGui.QGraphicsPathItem object at 0x3585270>
1 <PyQt4.QtGui.QGraphicsSimpleTextItem object at 0x356ca68>
2 <PyQt4.QtGui.QGraphicsSimpleTextItem object at 0x356ce20>
3 <PyQt4.QtGui.QGraphicsSimpleTextItem object at 0x356cc88>
4 <PyQt4.QtGui.QGraphicsSimpleTextItem object at 0x356cc00>
5 <PyQt4.QtGui.QGraphicsSimpleTextItem object at 0x356caf0>
6 <PyQt4.QtGui.QGraphicsSimpleTextItem object at 0x356cb78>

However, once I close the application, the following method is printed:

Exception ValueError: ValueError() in <module 'threading' from '/usr/lib/python2.6/threading.pyc'> ignored

I tried printing threading.currentThread(), but it returns the same thread whether it's called in- or outside the monkey-patched drawItems method.

In your code, this is likely due to the fact that you pass options (which is a list of style options objects) to the individual items rather than the respective option object. Using this code should give you the correct results:

def drawItems(self, painter, items, options):
    for item, option in zip(items, options):
        print "Processing", item
        # ... Do checking ...
        item.paint(painter, option, self.target)

Also, you say the self.target is the scene object. The documentation for paint() says:

This function, which is usually called by QGraphicsView, paints the contents of an item in local coordinates. ... The widget argument is optional. If provided, it points to the widget that is being painted on; otherwise, it is 0. For cached painting, widget is always 0.

and the type is QWidget*. QGraphicsScene inherits from QObject and is not a widget, so it is likely that this is wrong, too.

Still, the fact that the exception is not reported at all, or not right away suggests some foul play, you should contact the maintainer.

Torsten Marek
Hm. I don't get that threading error. However, I get "argument 2 of QGraphicsPolygonItem.paint() has an invalid type". Argument 2 is the option-parameter, which is passed unchanged. Doing `type(options)` tells me it is a standard Python list - so it is down-cast, but cannot be "up-cast" again. This must be a bug in the wrappers?
carlpett
Just re-read the docs and played around a bit and realized the same thing (re the list of options). Thanks for your help!It's wierd that the exception does not seem to surface unless I surround it with a try/except though...
carlpett