User Tools

Site Tools


qt:icons_draft

Icons (Linux-centric)

This page presents an as-yet untried Linux-centric approach to robust icon implementation in Qt desktop apps. Approaches for other operating systems are also discussed.

Icons and actions using Qt Resources

Normally, Qt uses its Resources system1) to get and display icons. The support for Qt Resource files built into Qt Creator/Designer makes placing icons into resource files and binding icons to actions very easy.

FIXME[howto]

Problems with using just this approach

Using the support for Qt Resources and actions built into Qt Creator/Designer delivers functionally acceptable results, but it's not optimal for a couple reasons.

Icon sizes

In a typical application, icons bound to actions will be rendered at different sizes. For example, toolbar icons may be rendered at 24×24 pixels while menu item icons will be rendered at 16×16. As far as I can tell, Qt Creator/Designer (and possibly all of Qt) does not let you set icons for menu items and toolbar items independently. Rather, Qt will automatically shrink (but not grow) icons to fit the desired space.2)

The classic solution is to provide an icon in a resource that is as large as the application is likely to require, and then let Qt resize it as needed. Thus, if you bind a 32×32 icon to an action, Qt will shrink it to 24×24 for the toolbar and 16×16 for the menu item. While this works, the shrunk icons often appear fuzzy and otherwise ugly or suboptimal. FIXME [example]

You can supply Windows-style multisize *.ico files as Qt Resources and bind them to actions. I tried this on Linux, and I only ever got it to show the smallest icon in the set. In other words, Qt didn't automatically select the best icon size for the given situation. This may work on Windows (and OS X?) but requires testing FIXME.

System icon themes

Another limitation with the way Qt Creator/Designer supports icons at the moment is that it doesn't provide support for existing system icons and icon themes. Qt itself does provide support for system icons, but this is not (yet?) reflected in Qt Creator/Designer.

A proposed approach to robust icon support

In what follows, I outline an approach that uses fast-and-easy Qt Creator/Designer resources support for rapid development combined with manual code additions to implement multi-sized system theme icons. It is important to understand that Qt has two notions of “system icons” and two different mechanisms for using them. The method below uses an approach that provides the widest possible set of icons and icon scalings, but it only works on X11 systems. Alternatives for Windows and OS X are discussed later.

For purposes of demonstration, I will assume we have a main window with three actions: action_Open that opens a file, action_Convert that converts the open file and saves it, and action_Quit that exits the application.

Step One: Provide Qt Resources a set of icons

When designing your application in Qt Creator/Designer, begin with using the built-in support for icon resources and binding to actions. In addition to giving your app an “if all else fails” fallback, this will accelerate development time. Note that the icons you use here should probably be a subset of the ones you will use in Step Three below.

The implementation of our example's MainWindow constructor at this point would look something like:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

Step Two: Implement system icon support using QIcon::fromTheme()

On X11 systems, Qt lets you access the icons defined by your system icon theme using the static function QIcon::fromTheme(). The icons so accessed will be selected as needed based on the target size. See:

In our example, after adding QIcon::fromTheme() support for the three actions the constructor looks like:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    if (QIcon::hasThemeIcon("document-open"))
        ui->action_Open->setIcon(QIcon::fromTheme("document-open"));
    if (QIcon::hasThemeIcon("document-save-as"))
        ui->action_Convert->setIcon(QIcon::fromTheme("document-save-as"));
    if (QIcon::hasThemeIcon("application-exit"))
        ui->action_Quit->setIcon(QIcon::fromTheme("application-exit"));
}

In the code above, if an icon isn't provided by the current icon theme, the app will use the fallback you set in Step One above. Note that we should consider writing a MainWindow member function that encapsulates the checking and setting for us. This is left as an exercise to the reader.

Step Three: Provide a fallback icon theme (with multiple sizes?)

The problem with our use of QIcon::fromTheme() above is that while it degrades to something that is still functional (and graphic), it will degrade to the ugly scaled icons we were trying to avoid in the first place. The solution is to provide a small, local custom theme that is used when a system icon resource isn't available.3) Note that you can also use this approach (and sidestep the above step) if you want to provide fully custom (i.e., not using the system icon theme) and scaled icon sets to your app.

To do this we need to do two things: Create a custom icon theme and implement the code needed to use the custom theme.

Creating a custom icon theme

FIXME

Code implementation

FIXME

Caveats

Qt's determination of the system theme is at the time of this writing not reliable under Linux – at least under Openbox; GNOME and Xfce are yet to be tested. A possible workaround is to use as yet undefined HeroicMeasures()™ to suss out the actual icon theme and then set it in the constructor with:

QString iconTheme = My::HeroicMeasures();
QIcon::setThemeName(iconTheme);

A limited alternative

Another approach to accessing system icons in Qt is the standardIcon() function that is part of QStyle. The icons that are available to standardIcon() are enumerated in StandardPixmap-enum. See:

The icon enumerated in StandardPixmap-enum should be available on all platforms, so testing for their existence isn't required. The main issue with the standardIcon() approach is that on my platform at least (Linux, Ubuntu 10.10) it does not provide multi-sized icons. You'll get the system theme, but the icons will be shrunk and fuzzy at times. I haven't tested the behavior on Windows and OS X, so it might not be the case on those platforms – in which case you might want to use this approach to set icons that are defined in StandardPixmap-enum on those platforms.

In our example, appropriate StandardPixmap-enum icons are available for action_Open and action_Convert. So if you wanted to use this approach for these icons, the resulting MainWindow constructor would look like:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->action_Open->setIcon(this->style()->standardIcon(QStyle::SP_DialogOpenButton));
    ui->action_Convert->setIcon(this->style()->standardIcon(QStyle::SP_DialogSaveButton));
    ...
}

or if you want to use these only as a fallback, you could do something like:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
 
    // use theme icon or a QStyle::StandardPixmap if none found:
    ui->action_Open->setIcon(QIcon::fromTheme("document-open",
                     this->style()->standardIcon(QStyle::SP_DialogOpenButton)));
 
    // use theme icon or a QStyle::StandardPixmap if none found:
    ui->action_Convert->setIcon(QIcon::fromTheme("document-save-as",
                     this->style()->standardIcon(QStyle::SP_DialogSaveButton)));
 
    // there is no exit icon in QStyle::StandardPixmap, so use resource as fallback:
    if (QIcon::hasThemeIcon("application-exit"))
        ui->action_Quit->setIcon(QIcon::fromTheme("application-exit"));
 
}

Choices, damn choices

Given the above it becomes clear that there is no perfect setup for all situations. There are at least four places where icons can come from. They are listed below along with their respective cons:

  1. A resource file
    • Doesn't scale well (at least on Linux, even when using mult-sized *.ico files)
    • Not consistent with system theme
  2. The system icon theme
    • Doesn't exist for Windows or OS X
    • Requires manual intervention
    • Covers a large subset all available icons
  3. Custom local icon theme
    • Not consistent with system theme
    • Requires lots of manual intervention
  4. QStyle standardIcon()
    • Doesn't scale well (at least on Linux)
    • Requires manual intervention
    • Covers a small subset all available icons

Depending on the priorities associated with any given case, you can design a workflow similar to the one described above that delivers the best solution. For example, you may decide that system integration is not at all important and provide icons using only a custom local icon theme (with resource file based icons as a fallback.) You may decide that system integration is more important than crisp icons and use QStyle standardIcon() as the primary means to set icons. In short, there's appears to be no solution that optimizes everything, so you will need to prioritize (1) development time, (2) system integration, and (3) icon rendering quality. Some policies that make sense are:

The X11 policy (detailed above)

  • Set resource file based icons as fallback
  • Use system theme icons if available
  • Use custom theme icons for remainder

The Windows/OS X policy

  • Set resource file based icons as fallback
  • Use StandardPixmap-enum icons if available
  • Use custom theme icons for remainder

The "I don't need system integration" policy

  • Set resource file based icons as fallback
  • Use custom theme icons (to solve ugly resizing problem)

What about conditional compilation?

I don't like the idea of conditional compilation (and I sense the Qt devs don't either) but this may be a case where it is justified. See http://doc.qt.nokia.com/4.7/qtglobal.html for a list of available predefined Q_OS_* macros.

qt/icons_draft.txt · Last modified: 2011/05/11 09:29 by mithat