PyQt: Overriding QGraphicsView.drawItems
Question
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?
Solution
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.
OTHER TIPS
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.