Trolltech | Documentation | Qt Quarterly | « A QListBox-Based File Browser | Deploying Applications on Mac OS X »

Look 'n' Feel Q & A
by Jasmin Blanchette
This article provides answers to some frequently asked questions about the look and feel of Qt widgets. In particular, we will see how to subclass QStyle to customize the look of Qt's built-in widgets, and how to use event filters to do painting on behalf of another widget.

Q: I would like to change the way that accelerators are shown in my menus. For example, for a menu item of Find Again and the accelerator Ctrl+G, I would like to display [Shift]Ctrl+G in the menu to indicate that Ctrl+Shift+G searches backward. Is there a way to change the rendering of the accelerator in a popup menu?

A: You can use the tab character ('\t') to specify your own menu text. For example:

    editMenu->insertItem(tr("F&ind Again\t[Shift]Ctrl+G"));
    

With QAction, you would write

    findAgainAct->setMenuText(tr("F&ind Again\t[Shift]Ctrl+G"));
    

Menu

A common use of this feature is to specify hard-coded key bindings, that is, key bindings that were supplied by reimplementing QWidget::keyEvent() instead of using QAccel or QAction.


Q: I use Qt for a touch-sensitive panel. How can I change the width of vertical scroll bars and the height of horizontal scroll bars to make QListBox easier to manipulate by touch?

A: Try QApplication::setGlobalStrut(). This function allows you to specify the minimum size that any GUI element that the user can interact with should have. For example, if you call

    QApplication::setGlobalStrut(QSize(30, 30));
    

your vertical scroll bars will be at least 30 pixels wide and your horizontal scroll bars at least 30 pixels high. This will also affect other GUI elements, such as push buttons, menu bars, and list view items.
Scrollview1   Scrollview2
If you only want your change to affect QScrollBar, a solution is to create a custom style and reimplement QStyle::pixelMetric() to return, say, 30 if the argument is QStyle::PM_ScrollBarExtent.


Q: I am trying to customize the look of QTable's horizontal header. I've subclassed QHeader and reimplemented paintSection(), but how can I tell QTable to use my QHeader subclass instead of a standard QHeader?

A: Unfortunately, the current version of Qt does not allow the use of a custom QHeader subclass in combination with a QTable. There are two workarounds:

  1. You can create a custom style by subclassing, say, QWindowsStyle and reimplementing QStyle::drawPrimitive() along these lines:

        void MyStyle::drawPrimitive(PrimitiveElement element,
                QPainter *painter, const QRect &rect,
                const QColorGroup &colorGroup, SFlags flags,
                const QStyleOption &opt) const
        {
            if (element == PE_HeaderSection) {
                // do my painting
            } else
                QWindowsStyle::drawPrimitive(element, painter, rect, colorGroup, flags, opt);
        }
        

  2. You can install an event filter on QTable's horizontal QHeader object and do the painting yourself:

        bool MyTable::eventFilter(QObject *targetObj, QEvent *event)
        {
            if (targetObj == (QObject *)horizontalHeader() && event->type() == QEvent::Paint) {
                QPainter painter(horizontalHeader());
                // do my painting
                return true;
            } else
                return QTable::eventFilter(targetObj, event);
        }
        

It is expected that Qt 4 will provide a better solution.


Q: How can I change the width of a splitter handle?

A: Since Qt 3.2, you can call QSplitter::setHandleWidth(). For older versions, subclass QStyle and reimplement pixelMetric():

    int MyStyle::pixelMetric(PixelMetric metric, const QWidget *widget) const
    {
        if (metric == PM_SplitterWidth) {
            return 6;
        } else {
            return QWindowsStyle::pixelMetric(metric, widget);
        }
    }
    


Q: Our application runs on Windows, Linux, Solaris, and Mac OS X using the native look, but we want to slightly change the look of QTabWidget on all platforms. Is there an alternative to subclassing all the built-in QStyle subclasses (QWindowsStyle, QMotifStyle, etc.) to achieve this?

A: It depends on what you want to do. One solution adopted by some Qt developers is to use a ProxyStyle class that inherits directly from QStyle and that forwards all virtual function calls to the platform-specific style:

    class ProxyStyle : public QStyle
    {
    public:
        ProxyStyle(const QString &baseStyle) { style = QStyleFactory::create(baseStyle); }
    
        void polish(QWidget *w) { style->polish(w); }
        void unPolish(QWidget *w) { style->unPolish(w); }
        int pixelMetric(PixelMetric metric, QWidget *widget) const
            { return style->pixelMetric(metric, widget); }
        ...
    
    private:
        QStyle *style;
    };
    

Then we simply need to subclass ProxyStyle and implement the custom behavior there:

    class MyStyle : public ProxyStyle
    {
    public:
        MyStyle(const QString &baseStyle);
    
        int pixelMetric(PixelMetric metric, const QWidget *widget) const;
    };
    
    int MyStyle::pixelMetric(PixelMetric metric, const QWidget *widget) const
    {
        if (metric == PM_SplitterWidth)
            return 6;
        return ProxyStyle::pixelMetric(metric, widget);
    }
    

We can then use the class like this:

    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
        app.setStyle(new MyStyle(app.style().name()));
        ...
        return app.exec();
    }
    

Thanks to the "proxy" approach, we only need to implement our custom behavior in one class, MyStyle. An instance of this class is then used in place of the native style, and all functions that are not reimplemented in MyStyle result in the native style being called.

Styles

Effectively, the proxy approach works around a limitation of the C++ language: C++ only allows us to derive from classes, not from objects. Proxy classes are explained in detail in Design Patterns (ISBN 0-201-63361-2).

This approach has one issue that we must be aware of. Some virtual functions in Qt's built-in styles are implemented in terms of other virtual functions; for example, QWindowsStyle::drawComplexControl() depends on QWindowsStyle::drawPrimitive() to draw the small arrow of a QComboBox. If we reimplement drawPrimitive() in our MyStyle class (which inherits ProxyStyle), our reimplementation will be ignored by QWindowsStyle, which will keep calling its own drawPrimitive() function. (This behavior occurs because MyStyle doesn't inherit from QWindowsStyle; it only "wraps" it.) Depending on the results we want, this might mean that we must reimplement drawComplexControl() as well.


Q: I have a problem with the Windows XP style. If I set the Button component of a QPushButton's palette to green, it doesn't change the button's color. However, if I change the current Windows theme to "Windows Classic", the expected color change occurs. How can I fix this? Is it a bug in the style?

A: Qt's Windows XP style is based on the native Windows theme engine. The XP theme ignores the palette and uses pixmaps for rendering buttons and other components. This behavior is hard-coded deep in the bowels of Windows XP; there's nothing Qt can do about it. (The same issue arises on Mac OS X with the Mac style.)
Button2   Button1

If you really need a green button, you can always set the style of that specific button to QWindowsStyle using QWidget:: setStyle(), although it won't look exactly the same as the other buttons in your application.


Q: I'm using Qt/Mac, and I was wondering: What is the difference between QAquaStyle and QMacStyle?

A: The QAquaStyle class first appeared in Qt 3.0, when the Qt/Mac port was first released. It emulated Apple's "Aqua" theme. In Qt 3.1, QAquaStyle was obsoleted by QMacStyle, which uses Appearance Manager to perform its drawing. The QAquaStyle class is still provided as part of Qt/Mac as a compatibility class, but it is now left out of the Qt library by default.


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

Copyright © 2004 Trolltech Trademarks Deploying Applications on Mac OS X »