A Better Canvas

by Andreas Aardal Hanssen

Qt 4.2 introduces Graphics View, a new framework for managing a large number of graphical shapes on a 2D canvas. This technology makes full use of Qt 4's powerful paint engine architecture, and is a replacement for Qt 3's QCanvas. The Graphics View API is very similar to QCanvas's, making it straightforward to port existing QCanvas-based code to use Graphics View.

Graphicsview-View

Many of you are probably familiar with QCanvas. With collision detection, transformations (like zooming and rotation), and support for managing millions of items efficiently, QCanvas has found its way into all kinds of applications, including games, charting, vector drawing, and imaging applications. When participating in events and trade shows, our engineers have been positively surprised as customers have shown us how they use QCanvas as the core of their applications.

While many of the core features of QCanvas (notably collision detection and advanced transformations) were tailored specifically for the framework itself, these features became part of Qt 4's paint system, Arthur. With Arthur's powerful rendering architecture at hand, we decided it was time to revamp the canvas and provide a brand new graphics shape framework that could fully exploit the potential of Qt 4.

Graphicsview-Pixmapitem

One of the most noticeable improvements in the new API lies in the coordinate system: We can now support real number coordinates to add a new level of precision to QGraphicsView's visualization capabilities, especially when zooming far in on a detailed scene. This greatly simplifies having to deal with data from external sources, such as topological data, which typically use real coordinates.

In addition, the canvas is no longer restricted to positive coordinates only. Unlike QCanvasItems, QGraphicsItems can reside at a position with negative coordinates, such as (-100, -50). When you construct a scene, you pass a rectangle (i.e., a QRectF) defining its position in the plane to QGraphicsScene's constructor. QGraphicsView will automatically adapt its scrollbars so you can navigate across the scene. For example:

    QGraphicsScene scene(QRectF(-1000, -1000, 2000, 2000));
    QGraphicsView view(&scene);
    view.show();

A long awaited feature that we couldn't easily implement for QCanvas is being able to transform individual items. Whereas in QCanvas each item's coordinates were aligned with the canvas's coordinate system, each QGraphicsItem can now store a local matrix which you can use to rotate, scale, shear, or translate the item.

Elastic-Nodes

For example, to change the orientation for a text item, you can now simply rotate the text item directly:

    QGraphicsTextItem *text = scene.addText(tr("Some Text"));
    text->rotate(90);

To the item itself, its coordinate system appears to be completely unchanged. So for those of you who write custom items, you do not need to worry about what transformation applies to the item as you handle mouse events and painting. The Graphics View framework automatically handles all such transformations for you.

And this brings us to another new feature in Graphics View. In Qt 3, QCanvasItems were all top-level items, and their geometries were relative to the canvas. In contrast, QGraphicsItem allows you to create composite items, or stack items on top of each other. Like QWidget, QGraphicsItem allows items to contain other items, and child items' geometries are relative to their parents. You can, for example, create a framed pixmap item by using composition:

    QPixmap pixmap("picture.png");
    QGraphicsPixmapItem *pixmap = scene.addPixmap(pixmap);
    QGraphicsRectItem *frame = scene.addRect(
            -2, -2, pixmap.width() + 2, pixmap.height() + 2);
    pixmap->setParentItem(frame);

And here's the best part: Even transformations are relative to the parent. If you scale or rotate the frame item, the pixmap child will follow.

Item composition can be used in a variety of ways, especially considering how QGraphicsItems do not clip their children like widgets do. You can use parent--child item relationships to link limbs together for a robot arm, or even for linking planets together in a solar system scene, bringing a new level of relative transformations into play. Item composition pushes Qt 4's rendering architecture to its limits, and makes it fun and easy to manage complex graphical scenes.

When managing a large scene, the graphics subsystem has two challenges: Determining the geometry and locality of items quickly, and rendering a large number of items at the same time. To speed up item discovery, e.g., when hovering the mouse over a group of items, QCanvas provided an index based on partitioning the canvas into a matrix of fixed-size chunks. With QGraphicsScene, the indexing is optional and handled internally, by default using a binary space partitioning (BSP) tree.

Even with fast item discovery, QGraphicsView is sometimes forced to render a large number of items. This is usually the case when zooming out, viewing a whole scene from a distance. QGraphicsView provides each item with level-of-detail information when it is drawn, allowing the items to easily determine how they can simplify their shape when viewed from a distance. Simplifying items' drawing can dramatically increase performance.

For the ultimate performance, QGraphicsView can render using OpenGL. Just use a QGLWidget for your view's viewport. As a custom item author, you then have the option of using QPainter's set of highly-optimized functions, or drawing directly to the GL context using OpenGL calls.

With the old QCanvas, improved item interaction was always one of the top feature requests. To interact with items, you would create a small subclass of QCanvasView and translate mouse events to the scene using the view's world matrix when reimplementing the mouse event handlers. Because different items treat mouse interaction differently, the view could easily end up maintaining item-specific logics.

Although you can still use this approach with Graphics View, the simpler approach is to reimplement mouse event handlers in the items themselves. The framework includes an event propagation architecture that provides interaction capabilities for the items on the scene. Items can handle key events, mouse press, move, release, and double-click events, and they can also track mouse movement.

Graphicsview-Text

Finally, I would like to mention the powerful text edit control provided by the QGraphicsSimpleTextItem and QGraphicsTextItem classes. The simple text item is optimized for drawing plain text fast and with low memory overhead. To display formatted text, you can use the QGraphicsTextItem which provides a rich set of functions giving you full control over the text document as well as its rendering. You can even make the text item editable!


This document is licensed under the Creative Commons Attribution-Share Alike 2.5 license.

Copyright © 2006 Trolltech Trademarks