Trolltech | Documentation | Qt Quarterly

Transparent Backgrounds in Qt 4.1
by Andreas Aardal Hanssen
Qt 4.1 radically improves widget drawing through its new "backing store", allowing semi-transparent (alpha-blended) child widgets and faster widget painting, as well as solving long-standing issues with nonrectangular widgets.

As a custom widget or custom style writer, Qt 4.1 should make your life easier, especially if you want to draw non-rectangular widgets. When doing the transition to the backing store in preparation for the Qt 4.1 release, we discovered that we could both simplify and improve our widget and style code. The new features have also enabled us to address issues concerning the Plastique style which in its initial release did not adequately support textures, gradients, and "dark colored backgrounds".

To better understand what has changed in Qt 4.1, we will start by reviewing how backgrounds work in Qt 3.x and 4.0 and how to obtain child widget transparency. Much of this is still applicable in Qt 4.1, so read on!

The System's Background

A widget represents a distinct region of desktop real estate in which we can paint shapes and colors. Widgets can be stacked on top of each other, and the topmost widget covers the widget underneath it. If a child widget covers half of its parent widget, the parent widget can only draw onto its own exposed areas (available as QPaintEvent::region()). Painting operations on the child widgets are clipped away.

On all desktop platforms, a Qt widget is also an independent object known to the window system. By default, the system provides a background based on the widget's background role and palette, which is used whenever a new area of the widget is shown for the first time.

For example, the background role of a QLabel is QPalette::Background, which traditionally corresponds to a gray color in the palette; for a QLineEdit, the background role is QPalette::Base, which is usually white.

If we call update() or repaint() to repaint a widget, Qt automatically erases the widget using the background color, relieving us from painting all the widget's pixels. This is very convenient, but it comes at a price:

Let's suppose we want to draw a graphing widget on a Base background. We'd start by adding this call to the constructor:

    setBackgroundRole(QPalette::Base);

In paintEvent(), we could now get away with drawing only the foreground. If we don't draw anything in paintEvent(), the widget will be totally white (or whatever color or brush Base stands for). Without having to draw anything, we can alter the widget's background color by changing the palette or setting a different background role.

If paintEvent() paints all the widget's pixels, we can tell Qt not to erase the widget (or the pixmap) with the background color by setting the Qt::WNoAutoErase (Qt 3) or Qt::WA_NoBackground (Qt 4) flag. In Qt 3, this is a common anti-flicker technique; in Qt 4, this is a minor speed optimization.

As a further optimization, we can also disable the window system background by calling setBackgroundMode(Qt::NoBackground) (Qt 3) or setAttribute(Qt::NoSystemBackground) (Qt 4). Now, the widget is truly uninitialized, and if we don't paint all the pixels in paintEvent(), we can see some interesting "artwork" as we move the window around.

Contents Propagation

The purpose of Qt::WA_NoSystemBackground is to speed up drawing by removing unnecessary background initialization, but it is often misinterpreted as a way to make the widget background transparent. Keep in mind that although the widgets are stacked on top of each other, the parent widget does not actually paint underneath child widgets.

We need a different approach if we want the parent widget's background to shine through. This is called "contents propagation", and Qt 4.0 supports this through its Qt::WA_ContentsPropagated widget attribute. In Qt 3, there are several ways to simulate this feature:

Although Qt 3 doesn't support semi-transparency for widgets, we can simulate it using QImage if necessary.

Window Opacity

Qt 3.3 introduced a new widget property called windowOpacity for making top-level widgets (windows) transparent. This property can't be used to make child widgets semi-transparent, and it only works on window systems that support this feature (Mac OS X and Windows 2000 or later). In addition, the windowOpacity property applies to the widget as a whole, not to individual shapes or pixels.

No matter which approach is taken, contents propagation takes its toll on the application's performance. Exposing a child widget will trigger a repaint for the parent widget. For complex hierarchies, the results can be unbearably slow. For every region with n overlapping widgets, the bottommost widget gets n repaints, and for every repaint we may have layouts, font metrics, clipping, and perhaps some complex operations such as an image-to-pixmap conversion or gradient generation. Contents propagation can result in n paint events, even for the simplest widget exposures.

If two semi-transparent widgets overlap (e.g., a semi-transparent rubber band covers a semi-transparent graph widget background), things get even worse. Because of this, Qt::WA_ContentsPropagated in Qt 4.0 does not support overlapping siblings.

New in Qt 4.1: The Backing Store

Qt's paint system has taken a big step forward in Qt 4.1 with the introduction of a backing store on all platforms. The backing store uses a persistent off-screen buffer for each window, and all raster paint operations are done directly to the backing store.

The backing store results in faster drawing on Windows and faster exposes on all platforms, and makes semi-transparent child widgets possible.

Composition

On Mac OS X, the backing store is built into the window system; on other platforms, Qt provides its own backing store. The backing store opens up a whole new world of possibilities:

How does all of this affect widget backgrounds? Here's a list of the most significant changes in 4.1: In some very rare cases, the change might affect the semantics of existing programs. If one of your custom widgets goes transparent, calling setAutoFillBackground(true) on the widget will fix the problem.

The potential for performance using the backing store architecture is far beyond what we had before. What's more, our users now have the option of writing modern widgets that stand out from the competition, on all platforms, without requiring advanced graphics support on their deployed platforms (such as XRender on X11). See the Fading Effects with Qt 4.1 article for a real-world application of semi-transparent child widgets.

With Qt 4.1, you can now use QPainter to its full potential, with semi-transparent backgrounds and antialiased curves blended against the parent widget's background. And it won't just work on Mac OS X and Windows 2000 or better. It will work on all platforms, which is exactly what you'd expect from your favorite cross-platform GUI framework.

Note: More information about contents propagation and child widget transparency can be found in the Transparency and Double Buffering section of the QWidget documentation.


[1] In Qt 4, this is not an issue because of double-buffering. When we paint on a widget, we're actually drawing on a pixmap that will be copied to the screen.


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

Copyright © 2006 Trolltech Trademarks