Cross-platform application on Qt: Style Sheets

  • Tutorial
Today I would like to consider the question of customizing the Qt program interface. The main option is to use Qt Style Sheets. As the name implies, this is a slightly specific analogue of the usual cascading style sheets ( CSS2 ), without which the modern Internet is unthinkable. QSS are most often used for two things: either make a widget look more native, or vice versa, make the interface more elegant, stand out, possibly the same on all platforms.


The screenshot shows one of the dialogs of our student organizer , the entire interface of which is implemented on QSS.

In the same screenshot, we see the main elements of the interface, customization of which today we will analyze:
  • Segment Buttons
  • Regular buttons
  • Tables
  • Scrollbars

The basics


But first, let's deal with some basic things. We can apply styles both locally (to a specific widget) and globally, taking all the styles into a separate file. The advantages of the second approach are obvious to everyone who has at least once worked with CSS, so we will stop on it.
I strongly recommend using the Qt resource system, which allows you not to bother and compile images and style files directly into the binary.
So, we create the stylesheet, apply it globally to the application:
QFile styleF;
styleF.setFileName(":/qss/style.css");
styleF.open(QFile::ReadOnly);
QString qssStr = styleF.readAll();
qApp->setStyleSheet(qssStr);

There is an excellent translation of the official documentation on the syntax of stylesheets, so I’ll just notice that more often than others there are three ways to describe widgets to which style will be applied (selectors):

Class reference , for example, such a rule will be applied to all buttons of your application, background color will be changed to red:
QPushButton { background-color: red; }

Calling by name , then this rule will be applied only to those buttons that have the name "okButton":
QPushButton#okButton  { background-color: red; }
Moreover, if on the same form there are several elements to which you want to apply the same style, you can use the setObjectName method:
ui->pbRed1->setObjectName("myRedButton");
ui->pbRed2->setObjectName("myRedButton");

Referring to the hierarchy of widgets on the form , then the rule will be applied only to those buttons that lie inside the frame with the name "mainFrame":
QFrame#mainFrame QPushButton{ background-color: red; }

Separately, it needs to be said about setting styles for subelements and pseudo-states:

Pseudo-states are indicated at the end of the selector, separated by a colon (:). For example, the following rule applies when the mouse is over a QPushButton:
QPushButton:hover { background-color: white }

Complex widgets contain subelements , therefore, to change the design, you can access each of them, for this the operator "::" is used. For example, we want to change / remove the arrow indicating that the button is assigned a menu, in which case the rule will look like this:
QPushButton#menuButton::menu-indicator {
    image: url(:/img/other/myindicator.png);
}

Unfortunately, to describe all the features you will need a full-blooded CSS tutorial, so let's move on to the examples.

Segment Buttons


I have two news: one is good and the other is not very. The sad thing is that QSS does not support shadow rendering. Absolutely. Therefore, if you make the normal state of the segment buttons in general is not difficult, then the pressed ... in general, only pictures.
Here are the styles responsible for this disgrace:
QPushButton#pbDelRight {
    image: url(:/img/buttons//pbDelRight.png);
    border-top-right-radius: 4px;
    border-bottom-right-radius: 4px;
    height: 26px;
    width: 40px;
}
QPushButton#pbDelRight:pressed {
    image: url(:/img/buttons/pbDelRightPressed.png);
}
QPushButton#pbDelRight:checked {
    image: url(:/img/buttons/pbDelRightPressed.png);
}

State: pressed must be specified so that there is no lag during its passage.
OS X bug : If you do not set the buttons to flat, they will overlap each other.

Buttons are ordinary


I know only two options for displaying a pressed state without using shadows - reflect the gradient and invert colors. Consider the second option:
 QPushButton#pbReady {
    padding:4px;
    color: #fff;
    font-size: 14px;
    border-radius: 2px;
    border: 1px solid #3873d9;
    background-color: qlineargradient( x1: 0, y1: 0, x2: 0, y2: 1,
                                        stop: 0 #4287ff, stop: 1 #356ccc);
 }
QPushButton#pbReady:pressed {
    color: #111;
    border: 1px solid #3873d9;
    background: #fff;
 }


Tables


Customization of the tables is done very soundly, styles for the header are set separately, separately for the table itself. It is possible to set styles for certain columns (sections) of the header through the :: section subitem. The pseudo-states are implemented for this: first,: last,: only-one,: next-selected,: previous-selected,: selected,: horizontal,: vertical and: checked.
QHeaderView {
    background-color: #fff;
    font-size:13px;
}
QHeaderView::section:horizontal {
    color: #fff;
    border-style: solid;
    background-color: qlineargradient( x1: 0, y1: 0, x2: 0, y2: 1,
                                        stop: 0 #4287ff, stop: 1 #356ccc);
 }
QTableView {
    border: 2px solid #3873d9;
    border-top-color: #4287ff;
    border-radius: 4px;
    background-color: #fff;
    background-image: url(:/img/other/background.png);
    gridline-color: #777;
    selection-background-color: #ccdfff;
    color:#333;
    font-size:12px;
 }

Scrollbars


The main customizable elements are two arrows at the edges and a handle, for which, in fact, you can grab hold of the mouse:
 QScrollBar:vertical {
     background: #e4e4e4;
     border-top-right-radius: 4px;
     border-bottom-right-radius: 4px;
     width: 12px;
     margin: 0px;
 }
 QScrollBar::handle:vertical {
    background-color: qlineargradient( x1: 0, y1: 0, x2: 1, y2: 0,
                                    stop: 0 #4287ff, stop: 1 #356ccc);
     border-radius: 4px;
     min-height: 20px;
     margin: 0px 2px 0px 2px;
 }
 QScrollBar::add-line:vertical {
     background: none;
     height: 0px;
     subcontrol-position: right;
     subcontrol-origin: margin;
 }
 QScrollBar::sub-line:vertical {
     background: none;
     height: 0px;
     subcontrol-position: left;
     subcontrol-origin: margin;
 }

On OS X, starting with 10.9, magic disappearing scrollbars are used, Qt supports them, so you should not redefine them.
A cross-platform bug is associated with scrollbars , due to which the bottom / right edge of the scrollbar is drawn on top of the scroll object if it has a stroke through QSS.

Table of Contents

PS Examples of using QSS for customizing each particular widget can be found here , a directory of possible properties, states and sub-elements here . Well, an article on the hub with an attempt to implement the Ribbon interface, it may be interesting to read.
PPS An interesting tool for editing QSS with real-time display.

Also popular now: