Introduction to QProxyStyle

There are many times when it is necessary to change specific details of our graphical interface in Qt (usually the appearance of a control). Qt offers many options in this regard:

 – Use widget properties. Unfortunately, not all rendering parameters of the control are always exposed.
 – Inherit control and spice your drawing methods. It is usually the most complicated of the options.
 – Inherit and expose protected variables. Here are two problems: first, that by not being part of the API it is easier for future versions of Qt to break the compilation; second, that many of the controls use pimpl to hide internal details.
– Use [style sheets] (https://doc.qt.io/qt-5/stylesheet.html): it is usually my preferred option, although it has its limitations: the interaction between the style sheet and the current state of The application is limited, and if we use a QStyle there may be unsupported elements. I will talk more about style sheets in Qt at another time.
 – Create your own style (QStyle), but that, for specific changes, is usually quite laborious.
 – Use a QProxyStyle, which brings a little the best of several worlds and which is the center of this article.

As I mentioned before, I am very friendly with style sheets, but there are times when it is simply not possible to make certain changes with them. It is when the QProxyStyle come into action.

A class that inherits QProxyStyle simply has to indicate a base style and reimplement the methods it needs, and the proxy will redirect all the others father style. While it is necessary to know what part of the style to reimplement, it is not difficult to get that information from the documentation of the styles ([these examples] (https://doc.qt.io/qt-5/qtwidgets-widgets-styles-example .html) are very good to start) or even examining the Qt’s source code for more extreme cases.

Example: QMessageBox with custom icon

The following code shows a use case: change the QMessageBox icon (we will use the warning to simplify the example). This is what a message box looks like with the default style (in Windows 10) and with the fusion style:

Let’s start with the definition of the proxy:

class MyProxyStyle : public QProxyStyle {
public:
  explicit MyProxyStyle(const QString& name)
    : QProxyStyle(name), // parent style
      m_warning_icon(":/resources/warning.png") {
  }

public:
  virtual QIcon standardIcon(StandardPixmap standard_icon,
                             const QStyleOption* option,
                             const QWidget* widget) const override {
    switch (standard_icon) { // change only icon of Warning MB
    case SP_MessageBoxWarning: return m_warning_icon;
    }

    // Default behavior
    return QProxyStyle::standardIcon(standard_icon, option, widget);
  }

private:
  QIcon m_warning_icon; // cache the icon to avoid reloading it each time
};

Next, we simply set the style of our application at some time before using the message boxes:

int main(int argc, char* argv[]) {
  QApplication a(argc, argv);
  // ...

  qApp->setStyle(new MyProxyStyle("fusion")); // use "fusion" as parent style

  // ...
}

Ready! From this moment on, all QMessageBox::warning(...) will show our icon instead of the default one.

The complete project (VS) of this example is available at GitHub.

Another example: remove the focus frame from all controls

For this we will need to reimplement QProxyStyle::drawPrimitive and avoid drawing the PE_FrameFocusRect elements:

void MyProxyStyle::drawPrimitive(PrimitiveElement element, const QStyleOption* option,
                                 QPainter* painter, const QWidget* widget) const
{
  if (element != PE_FrameFocusRect) { // draw everything but focus ring
    QProxyStyle::drawPrimitive(element, option, painter, widget);
  }
}

As you can see, style proxies are simple techniques to extend the way in which the graphical interface is rendered in Qt.

Related links