Введение
Qt Style Sheets (QSS) — язык стилизации виджетов Qt, синтаксически близкий к CSS, но со своими особенностями, учитывающими специфику виджетной модели Qt. QSS позволяет менять внешний вид стандартных виджетов без написания C++ кода — цвета, шрифты, отступы, рамки — что делает его удобным инструментом для дизайнеров и разработчиков, не погружающихся в детали отрисовки виджетов. В статье разберём базовый синтаксис и основные виды селекторов.
Концепция
Правило QSS состоит из селектора (указывающего, к каким виджетам применяется правило) и блока свойств в фигурных скобках, аналогично CSS. Селекторы могут быть по типу виджета (QPushButton), по имени объекта (#myButton, используя objectName), по классу через свойство (QPushButton[flat="true"]), и комбинированные через иерархию (QDialog QPushButton — все кнопки внутри диалога). Применить QSS можно к конкретному виджету (widget->setStyleSheet(...)) или ко всему приложению (qApp->setStyleSheet(...)), при этом стили наследуются по дереву виджетов, если не переопределены более специфичным правилом.
Пример кода: Основы Qt Style Sheets
#include <QApplication>
#include <QPushButton>
#include <QWidget>
#include <QVBoxLayout>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
auto *window = new QWidget();
window->setObjectName("mainWindow");
auto *primaryButton = new QPushButton("Сохранить", window);
primaryButton->setObjectName("primaryButton");
auto *secondaryButton = new QPushButton("Отмена", window);
secondaryButton->setProperty("buttonRole", "secondary");
auto *layout = new QVBoxLayout(window);
layout->addWidget(primaryButton);
layout->addWidget(secondaryButton);
// Применение стилей ко всему приложению — наследуется всеми виджетами,
// если не переопределено более специфичным правилом
app.setStyleSheet(R"(
/* Селектор по имени класса виджета — применяется ко всем QPushButton */
QPushButton {
padding: 8px 16px;
border-radius: 4px;
font-size: 14px;
}
/* Селектор по objectName (через #) — самый специфичный, переопределяет общий стиль */
#primaryButton {
background-color: #2563eb;
color: white;
border: none;
}
#primaryButton:hover {
background-color: #1d4ed8;
}
/* Селектор по динамическому свойству — полезно для логических групп виджетов
без необходимости задавать уникальное имя каждому */
QPushButton[buttonRole="secondary"] {
background-color: transparent;
color: #2563eb;
border: 1px solid #2563eb;
}
/* Иерархический селектор — кнопки именно внутри #mainWindow */
#mainWindow QPushButton {
min-width: 100px;
}
)");
window->show();
return app.exec();
}
Пояснения к коду
Правило QPushButton { ... } применяется ко всем кнопкам приложения без исключения — это базовый, наименее специфичный уровень стилизации. Правило #primaryButton { ... } нацелено только на конкретный виджет с заданным через setObjectName() именем и переопределяет более общие свойства для этого конкретного виджета — это поведение прямо аналогично специфичности селекторов в обычном CSS, где более конкретный селектор побеждает более общий при конфликте свойств.
Селектор QPushButton[buttonRole="secondary"] демонстрирует использование произвольного динамического свойства (setProperty("buttonRole", "secondary")), не являющегося стандартным свойством Qt — это удобный способ группировать виджеты по логической роли без необходимости задавать каждому уникальное objectName, особенно когда таких «вторичных» кнопок в приложении много. Иерархический селектор #mainWindow QPushButton показывает контекстную стилизацию — правило применяется только к кнопкам, находящимся где-то внутри виджета с именем mainWindow, а не ко всем кнопкам приложения вообще.
Подводные камни
- Применение
setStyleSheet()к родительскому виджету полностью переопределяет стиль для всех потомков, в том числе сбрасывая платформенный нативный вид виджетов на полностью кастомный — даже минимальный QSS, заданный для одного-единственного свойства (например, только цвета фона), на некоторых платформах может неожиданно изменить и другие визуальные аспекты виджета (форму рамки, поведение фокуса), которые до этого предоставлялись нативным стилем ОС. - Дублирование и конфликт правил при использовании одновременно глобального стиля приложения (
qApp->setStyleSheet) и локальных стилей отдельных виджетов (widget->setStyleSheet) — оба применяются одновременно, и итоговый эффект определяется правилами специфичности и порядком наложения, что может приводить к неожиданным результатам, особенно по мере роста количества правил в проекте; для предсказуемости стоит придерживаться единой, осознанной стратегии (например, весь основной стиль — на уровне приложения, а локальные стили — только для редких, точечных исключений). - Производительность парсинга QSS не бесплатна — при каждом вызове
setStyleSheet()Qt заново разбирает всю переданную строку стилей; вызов этого метода в цикле или слишком часто (например, при каждом изменении состояния виджета вместо использования псевдосостояний из статьи 261) создаёт ненужную нагрузку, которая особенно заметна на слабом embedded-железе (основной цикл статей про embedded, статья 242). - QSS-синтаксис похож на CSS, но не идентичен ему — не все CSS-свойства и селекторы, привычные веб-разработчикам, поддерживаются в QSS, и наоборот, в QSS есть специфичные для Qt-виджетов псевдоэлементы (статья 261), не имеющие прямого аналога в CSS — разработчики с опытом веб-разработки иногда интуитивно пытаются использовать веб-специфичный синтаксис (flexbox, grid), который в QSS просто не существует.