Learning Graphics View the Slow Way
I wanted to draw a simple network diagram: a few hundred boxes, some labels, and lines joining them. My old version used a custom widget with a large paintEvent(). It worked until I added selection, dragging and zooming. Then every mouse press became a small geometry examination, and every repaint redrew the universe.
Qt 4.2 already had Graphics View, but I postponed learning it because the three principal classes sounded like bureaucracy. This week I finally moved the diagram to QGraphicsScene, QGraphicsView and a handful of QGraphicsItem subclasses. Naturally, my first implementation reproduced most of the mistakes from the custom widget, only with newer nouns.
I created every item at viewport coordinates and moved them whenever the scroll bars changed. That made scrolling jitter, selection rectangles miss their targets, and zooming move the diagram away from the mouse. I then tried compensating with more coordinate conversions. By noon I had code converting a point from the view to the scene, back to the view, and possibly into a neighbouring dimension.
The useful idea is that the scene owns a stable logical coordinate system. Items live in scene coordinates, while the view is merely one camera onto that scene. Scrolling and zooming alter the camera transform; they should not rearrange the objects. Mouse positions arriving at the viewport can be mapped into the scene once, at the boundary, and item-local coordinates can be left to the item.
After removing my scroll-bar corrections, panning became smooth. Scaling the view around an anchor also stopped corrupting the stored positions. More importantly, a second view could display the same scene at a different zoom without duplicating the graph. That one experiment explained the separation better than the class names did.
Painting required another correction. My naive item painted outside its boundingRect() to make room for a shadow. Sometimes the shadow remained on screen after the item moved, producing a trail that looked like a very cheap supernatural effect. The scene uses the bounding rectangle to decide what may need repainting and which items intersect an area. If the paint escapes that promise, the scene has no reason to clean it up.
I expanded the rectangle to include the pen and shadow, then supplied a tighter shape() for hit testing. Those two concepts need not be identical. The bounding rectangle should be cheap and conservative; the shape can describe the clickable outline more accurately. With that split, selecting a line no longer required clicking somewhere in its enormous rectangular aura.
Performance was my next concern. I assumed one item per node, label and connector would necessarily be slower than one giant paint function. At first it was, because each node recomputed text dimensions and connector paths during every paint. Caching those values when the data changed made a larger difference than reducing the item count. The view can already avoid painting items outside the exposed region, something my giant widget did not do intelligently.
There are still choices rather than magic switches. Device-coordinate caching helps items that do not change but are viewed repeatedly; it is less attractive for items constantly transformed. Indexing the scene accelerates lookup in a mostly stable diagram, but updating an index while thousands of objects move can cost more than a simple scan. The correct setting depends on whether the scene behaves like a map or like a box of agitated insects.
The new event handling is also cleaner. Instead of asking every object whether a mouse point belongs to it, I let the scene deliver events to the topmost suitable item. Movable and selectable flags cover ordinary interaction. A custom item only handles a mouse event when its behavior is genuinely special. This removed an entire home-grown selection system, including one bug where hidden nodes could still be dragged.
Qt 4.3 adds useful refinements, but the important lesson is already present: Graphics View is not a faster canvas. It is a retained scene with explicit geometry, coordinate mapping, event delivery and multiple views. It rewards honest descriptions of where an item is and what it paints. Lie about either and it retaliates with artifacts that appear random but are, annoyingly, deserved.
My practical conclusion is to use a plain custom widget for drawings that are truly one object and have simple interaction. Once the drawing contains independent things that move, select, overlap or need more than one view, Graphics View earns its three-class introduction. Start in scene coordinates, keep bounding rectangles accurate, cache computations rather than blindly caching pixels, and let the framework perform the hit testing. It is less code, but only after deleting the code written to defeat it.