Should you still be using QGraphicsView?

There was a time when the Qt Graphics View Framework was seen as the solution to create modern user interfaces with Qt. Now that Qt Quick 2 has matured does the Graphics View Framework still have a place in Qt?

At the Qt World Summit 2016 I gave a presentation entitled “The Curse of Choice.” It gives an overview of the various GUI technologies available in Qt 5 as well as descriptions of which you should use for different use cases. I would recommend you watch the recording of the presentation, unfortunately there was a technical issue with the recording and while the talk is available for viewing online, it is incomplete.  I did give the same talk in webinar format though which you can view here.

After each time giving the talk, I got many questions about the Graphics View framework, which I was quite harsh on. My conclusion was that users should avoid QGraphicsView for new applications and instead use Qt Quick. This proved to be a controversial claim because Graphics View is used in many applications shipping today. So in the interest of answering the question “Should you still be using QGraphicsView in 2017?” This post will give the longer form of the answer.

Prophetic comment on a post about Qt Scene Graph

Prophetic comment on a post about Qt Scene Graph

Does Qt Quick really replace Graphics View?

Yes and No.

Qt Quick 1 (QDeclarative* API) was built on top of Graphics View, so comparing Graphics View to Qt Quick 2 is like comparing apples to oranges.

The better question is: “Does the Qt Quick Scenegraph replace Graphics View?” The lessons learned from using Graphics View for Qt Quick 1 were applied to create its successor. An API optimized for rendering Qt Quick scenes on devices with a Graphical Processor Unit. That API is the Qt Quick Scenegraph.

Qt Quick and its Scenegraph API can do most of what Graphics View could do and much more, but there are still features that got left behind:

Graphics View enables Multiple Views of the same scene

This powerful feature was shamelessly flaunted in the 40000 Chips demo:

40000 Chips Demo

The Power of Qt Graphics View!

Because all items in Graphics View can live in a shared QGraphicsScene, it is possible to have more than one QGraphicsView widget each of which could display different views of the shared scene. This is quite difficult to replicate in Qt Quick 2 because each view would need to create its own copy of the scene.

A cool as the feature seems, in practice it was not used so much by customers. The feature was also less practical on mobile and embedded platforms where only one view would be used per screen. This proved to be an unfortunate design decision because it also limited opportunities for optimization.

It makes the code really complex and optimization hard. — Alexis Menard ( Qt DevDays 2010 )

If you need to show the same scene multiple ways when migrating to Qt Quick, then you will need to separate the data you would like to visualize from the scene defined in QML. Populate each Qt Quick scene with only the data present in the view of your logical scene. This is more complicated but does provide more flexibility in how you store the state of your shared scene. I concede that it was easier to do in QGraphicsView.

Graphics View depends on QPainter

This is both a blessing and a curse. One of the reasons that Graphics View lived on so long through Qt Quick 1 in Qt 5 is because of its use of QPainter. When Qt Quick 2 was released with Qt 5.0 it came with the requirement that it could only be rendered with OpenGL(ES) 2 or higher. This minimum requirement did not fit everyone's needs, and the solution was to keep Qt Quick 1 around for those customers.

In Qt 4 QPainter had a couple of different graphics systems (backends) that could be used. This meant that if you were rendering with QPainter you could either choose to do so in software (raster), or with the GPU (opengl). In the case of QGraphicsView if you wanted to render your scene with the OpenGL paint engine you just needed to set the viewport of QGraphicsView to be backed by a QGLWidget.

In general this would improve the rendering performance of Graphics View. However, under the hood QPainter is generating loads of state changes and rendering the scene back to front leading to a poor use of the GPU. This is a large reason why the Qt Quick Scenegraph exists. In Qt Quick the renderer can, depending on the scene’s content, determine the best way to arrange the scene for rendering on the GPU.

Qt Quick as of 5.8 no longer requires the use of OpenGL 2 thanks to the Software rendering adaption.

Graphics View has collision detection

What! Are you making a game now?

Angry Birds

They use Box2D, and so should you.

Graphics View supports embedding QWidgets

This is a neat feature and became a customer favorite shortly after its debut. The intention was to enable users to reuse controls that already existed. For simple widgets like QPushButton and QSlider the cost in extra memory usage and proxying was worth it for the convenience gained. However users did not stop there and would place complex widget hierarchies into their scenes like their entire QMainWindow or QDialog.

What was convenient for customers was also problematic for performance. QWidgets can not be embedded into a Graphics View scene directly. Instead they must be proxied. Each time an item in the scene that represents a QWidget requires an update, it has to request a full update of the QWidget via QWidget::render(…). The contents of the QWidget would then be rendered and copied into Graphics View. With Graphics View there is a shared QPainter between all items which renders to a single target QPaintDevice. QWidgets however are themselves QPaintDevices which require their own QPainter instances.

The key to the high performance of Graphics View is reducing how much is painted each frame. QGraphicsWidget and QGraphicsProxyWidget together are huge performance killers because they can not be rendered in a efficient way.

If you still choose to use Graphics View today, do yourself a favor and avoid embedding QWidgets into your scene.

QGraphicsProxyWidget: Not even once

Seriously

Graphics View has Shape Items

Graphics View Items

As of Qt 5.8, Qt Quick still only has one built in shape, the Rectangle. The Rectangle Item can also be used to create circles, but that is little comfort to anyone porting Qt Graphics View shapes to Qt Quick.

You can create lines in Qt Quick with a thin and long Rectangle. Line segments become a series of Rectangles. If you wanted more exotic shapes or paths, then you have a couple of options:

Canvas Item: With the Canvas Item you can use the imperative Context2D Javascript API to render whatever shape or content you want.

QQuickPaintedItem: This is a custom C++ item that has a virtual paint method much like a custom QGraphicsViewItem. Here you can render whatever content you want but via QPainter making a very easy porting path from QGraphicsItem. Both this and the Canvas Item create an offscreen rendering surface the size of the area you want to render to. This can either be a QImage or an QOpenGLFramebufferObject.

Keep in mind that if you are drawing a large shape, there is also a large area of memory behind the scenes being used that will be the size of your shapes bounding rectangle.

Custom QSGGeometryNode: This is likely the most optimal method performance-wise, but also the most difficult. Rather than drawing the shape you desire though an imperative painting API, you would embrace the Qt Quick Scenegraph and generate custom geometry for your item. This however means turning the shape you want to render into geometrical primitives like triangles. The details of this are a bit much for this article, and likely best left to a future post. For more details check out this example.

There may be several ways to render arbitrary shapes in Qt Quick scenes, but none as convenient as QGraphicsPathItem.

Laszlo Agocs is currently researching this topic though, so as soon as Qt 5.10 there could be an easy way to use shapes and paths in Qt Quick. Stay tuned.

But I don’t want to write QML or Javascript!

Time to address the gorilla in the room.

Its very easy to breeze over the detail that not everything has a public C++ API in Qt Quick. If you are porting an application using Graphics View to Qt Quick, you will very soon realize that you have to write QML code. While you can (and should) use C++ APIs as much as possible with Qt Quick as well, you will find that creating sub-classes of existing components via C++ is not possible.

Lets consider the TextEdit QML component. If you wanted to create an item that extended the existing functionality of TextEdit you would have to do so from QML. The TextEdit QML component is implemented in C++, but that class is not public API, so you can’t use that class in your custom C++ item via aggregation nor composition.

Much of the Qt Quick QML APIs are like this. Even if you start with the approach of: “Code only in C++”, in practice this is challenging. We hope to address this in future releases of Qt. The original reason given to keep these classes private was to keep our options open, and not lock down these APIs too soon. As we are now releasing Qt 5.8 and looking towards Qt 6, its time to reconsider this policy.

That said, QML is awesome, and in practice extending items in QML is super easy. QML stands for the Qt Meta-object Language and should be looked at as an easy way to automate the process of setting up a hierarchy of QObjects and their signal/slot connections. If you're holding out on Qt Quick because of QML, I understand, but recommend you still give it a chance. Check out the free online QML Book for more details on getting started.

Is Graphics View deprecated?

The short answer is no, not officially. The real answer is more nuanced. And it starts with the fact that Qt Quick 1 is now deprecated. This is important because it was Qt Quick 1 that drove forward the development of Graphics View. Much like the rest of the Widgets module, Graphics View is considered “done” and isn’t being given new features. You can see this if you check out the commit log for those classes.

There is some maintenance, but much of those commits are from various refactoring that covers all of the QtBase module.

Because of the larger than not overlap between Qt Quick 2 and Graphics View, I would expect to see Graphics View moved into a “Compatibility” module for Qt 6.

Conclusion

I am not so naive to believe that this answers all the questions you have about the future of Graphics View. I would like to continue the discussion because if there is a reason you still can not migrate from Graphics View, then we still have work to do.

If you want me to write more articles about porting from Graphics View to Qt Quick, please let me know in the comments.  Also if you recall seeing this post elsewhere, it also appeared on my own blog.  In the interest of making the Dev Loop of the Qt Blog more awesome, I'll be posting all future blog articles here. So stay tuned!


Blog Topics:

Comments