Синтаксис таблицы стилей
Терминология и синтаксические правила таблиц стилей Qt практически идентичны таковым в HTML CSS. Если вы уже знакомы с CSS, вы, вероятно, сможете быстро просмотреть этот раздел.
Правила стилей
Таблицы стилей состоят из последовательности правил стилей. Правило стиля состоит из селектора и объявления. Селектор определяет, какие виджеты будут затронуты правилом; объявление определяет, какие свойства должны быть установлены на виджете. Например:
QPushButton { color: red } В приведенном выше правиле стиля QPushButton является селектором, а { color: red } — объявлением. Правило определяет, что QPushButton и его подклассы (например, MyPushButton) должны использовать красный цвет в качестве цвета переднего плана.
Таблица стилей Qt, как правило, нечувствительна к регистру (то есть color, Color, COLOR, и cOloR относятся к одному и тому же свойству). Исключение составляют имена классов, имена объектов и имена свойств Qt, которые чувствительны к регистру.
Для одного объявления можно указать несколько селекторов, используя запятые (,) для разделения селекторов. Например, правило
QPushButton, QLineEdit, QComboBox { color: red } эквивалентно этой последовательности из трех правил:
QPushButton { color: red }
QLineEdit { color: red }
QComboBox { color: red } Часть объявления правила представляет собой список property: value пар, заключенных в фигурные скобки ({}) и разделенных точками с запятой. Например:
QPushButton { color: red; background-color: white } См. раздел Список свойств ниже для списка свойств, предоставляемых виджетами Qt.
Типы селекторов
Все предыдущие примеры использовали самый простой тип селектора — селектор типа. Таблицы стилей Qt поддерживают все селекторы, определенные в CSS2. В таблице ниже обобщены наиболее полезные типы селекторов.
| Селектор | Пример | Описание |
|---|---|---|
| Универсальный селектор | * |
Соответствует всем виджетам. |
| Селектор типа | QPushButton |
Соответствует экземплярам QPushButton и его подклассам. |
| Селектор свойства | QPushButton[flat="false"] |
Соответствует экземплярам QPushButton, которые не являются плоскими. Вы можете использовать этот селектор для проверки любого свойства Qt, которое поддерживает QVariant::toString() (см. документацию функции toString() для получения подробной информации). Кроме того, поддерживается специальное свойство class, для имени класса.Этот селектор также можно использовать для проверки динамических свойств. Для получения дополнительной информации о настройке с помощью динамических свойств обратитесь к Настройка с помощью динамических свойств. Вместо Предупреждение: Если значение свойства Qt изменяется после установки таблицы стилей, может потребоваться принудительно пересчитать таблицу стилей. Один из способов достижения этого — сбросить таблицу стилей и установить ее снова. |
| Селектор класса | .QPushButton |
Соответствует экземплярам QPushButton, но не его подклассам. Это эквивалентно |
| Селектор ID | QPushButton#okButton |
Соответствует всем экземплярам QPushButton, чье имя объекта равно okButton. |
| Селектор потомка | QDialog QPushButton |
Соответствует всем экземплярам QPushButton, являющимся потомками (детьми, внуками и т. д.) QDialog. |
| Селектор ребенка | QDialog > QPushButton |
Соответствует всем экземплярам QPushButton, являющимся непосредственными детьми QDialog. |
Подконтроли
Для стилизации сложных виджетов необходимо получить доступ к подконтролям виджета, например, к кнопке раскрывающегося списка QComboBox или к стрелкам вверх и вниз QSpinBox. Селекторы могут содержать подконтроли, которые позволяют ограничить применение правила к конкретным подконтролям виджета. Например:
QComboBox::drop-down { image: url(dropdown.png) } Вышеприведенное правило стилизует кнопку раскрывающегося списка всех QComboBoxes. Хотя синтаксис с двойными двоеточиями (::) напоминает CSS3 псевдоэлементы, подконтроли Qt концептуально отличаются от них и имеют разные семантики каскадирования.
Подконтроли всегда располагаются относительно другого элемента — элемента-ссылки. Этим элементом-ссылкой может быть виджет или другой подконтроль. Например, ::drop-down QComboBox по умолчанию размещается в верхнем правом углу прямоугольника отступа QComboBox. ::drop-down по умолчанию размещается в центре прямоугольника содержимого подконтроля ::drop-down. См. раздел Список стилизуемых виджетов ниже для подконтролей, используемых для стилизации виджета, и их расположения по умолчанию.
Прямоугольник начала, который будет использоваться, можно изменить с помощью свойства subcontrol-origin. Например, если мы хотим разместить раскрывающийся список в прямоугольнике отступа QComboBox вместо прямоугольника отступа по умолчанию, мы можем указать:
QComboBox {
margin-right: 20px;
}
QComboBox::drop-down {
subcontrol-origin: margin;
} Выравнивание раскрывающегося списка внутри прямоугольника отступа изменяется с помощью свойства subcontrol-position.
Свойства width и height можно использовать для управления размером подконтроля. Обратите внимание, что установка image неявным образом задает размер подконтроля.
Схема относительного позиционирования (position : relative) позволяет сместить положение подконтроля от его начального положения. Например, при нажатии кнопки раскрывающегося списка QComboBox, нам может потребоваться сместить стрелку внутри, чтобы получить эффект «нажатия». Для этого можно указать:
QComboBox::down-arrow {
image: url(down_arrow.png);
}
QComboBox::down-arrow:pressed {
position: relative;
top: 1px; left: 1px;
} Схема абсолютного позиционирования (position : absolute) позволяет изменять положение и размер подконтроля относительно элемента-ссылки.
После позиционирования они обрабатываются так же, как виджеты, и могут быть стилизованы с помощью модели области.
См. раздел Список подконтролей ниже для списка поддерживаемых подконтролей и Настройка подконтроля указателя меню QPushButton для реального примера.
Примечание: В случае со сложными виджетами, такими как QComboBox и QScrollBar, если одно свойство или подконтроль настраивается, все остальные свойства или подконтроли также должны быть настроены.
Псевдосостояния
Селекторы могут содержать псевдосостояния, которые обозначают, что применение правила ограничено состоянием виджета. Псевдосостояния появляются в конце селектора с двоеточием (:) между ними. Например, следующее правило применяется, когда курсор мыши наводится на QPushButton:
QPushButton:hover { color: white } Псевдосостояния могут быть отрицательными с использованием оператора восклицания. Например, следующее правило применяется, когда курсор мыши не наводится на QRadioButton:
QRadioButton:!hover { color: red } Псевдосостояния могут быть объединены, в этом случае подразумевается логическое И. Например, следующее правило применяется, когда курсор мыши наводится на отмеченный QCheckBox:
QCheckBox:hover:checked { color: white } Отрицательные псевдосостояния могут появляться в цепочках псевдосостояний. Например, следующее правило применяется, когда курсор мыши наводится на QPushButton, который не нажат:
QPushButton:hover:!pressed { color: blue; } При необходимости логическое ИЛИ можно выразить с помощью оператора запятой:
QCheckBox:hover, QCheckBox:checked { color: white } Псевдосостояния могут сочетаться с подконтролями. Например:
QComboBox::drop-down:hover { image: url(dropdown_bright.png) } См. раздел Список псевдосостояний ниже для списка псевдосостояний, предоставляемых виджетами Qt.
Разрешение конфликтов
Конфликты возникают, когда несколько правил стиля задают одни и те же свойства с разными значениями. Рассмотрим следующую таблицу стилей:
QPushButton#okButton { color: gray }
QPushButton { color: red } Оба правила соответствуют экземплярам QPushButton с именем okButton и существует конфликт для свойства color. Для разрешения этого конфликта необходимо учитывать специфичность селекторов. В приведенном выше примере QPushButton#okButton считается более специфичным, чем QPushButton, поскольку он (как правило) относится к одному объекту, а не ко всем экземплярам класса.
Аналогично, селекторы с псевдосостояниями более специфичны, чем селекторы без псевдосостояний. Таким образом, следующая таблица стилей указывает, что у QPushButton должен быть белый текст, когда курсор мыши наведен на него, а в противном случае — красный:
QPushButton:hover { color: white }
QPushButton { color: red } Вот еще один интересный случай:
QPushButton:hover { color: white }
QPushButton:enabled { color: red } Здесь оба селектора имеют одинаковую специфичность, поэтому, если курсор мыши наведен на кнопку, а она активна, второе правило имеет приоритет. Если мы хотим, чтобы текст был белым в этом случае, мы можем переупорядочить правила следующим образом:
QPushButton:enabled { color: red }
QPushButton:hover { color: white } В качестве альтернативы мы можем сделать первое правило более специфичным:
QPushButton:hover:enabled { color: white }
QPushButton:enabled { color: red } Аналогичная проблема возникает в сочетании с селекторами типа. Рассмотрим следующий пример:
QPushButton { color: red }
QAbstractButton { color: gray } Оба правила применяются к экземплярам QPushButton (поскольку QPushButton наследует от QAbstractButton), и существует конфликт для свойства color. Поскольку QPushButton наследует от QAbstractButton, может показаться, что QPushButton более конкретно, чем QAbstractButton. Однако, для вычислений стилей все селекторы типа имеют одинаковую специфичность, и правило, которое встречается последним, имеет приоритет. Другими словами, color устанавливается в gray для всех QAbstractButton, включая QPushButton. Если мы действительно хотим, чтобы у QPushButton был красный текст, мы всегда можем изменить порядок правил.
Для определения специфичности правила, таблицы стилей Qt следуют спецификации CSS2:
Специфичность селектора вычисляется следующим образом:
- подсчитайте количество атрибутов ID в селекторе (= a)
- подсчитайте количество других атрибутов и псевдоклассов в селекторе (= b)
- подсчитайте количество имён элементов в селекторе (= c)
- проигнорируйте псевдоэлементы [например, подэлементы].
Объединение трёх чисел a-b-c (в системе счисления с большой базой) даёт специфичность.
Некоторые примеры:
* {} /* a=0 b=0 c=0 -> specificity = 0 */ LI {} /* a=0 b=0 c=1 -> specificity = 1 */ UL LI {} /* a=0 b=0 c=2 -> specificity = 2 */ UL OL+LI {} /* a=0 b=0 c=3 -> specificity = 3 */ H1 + *[REL=up]{} /* a=0 b=1 c=1 -> specificity = 11 */ UL OL LI.red {} /* a=0 b=1 c=3 -> specificity = 13 */ LI.red.level {} /* a=0 b=2 c=1 -> specificity = 21 */ #x34y {} /* a=1 b=0 c=0 -> specificity = 100 */
Каскадирование
Таблицы стилей могут быть установлены на QApplication, на родительских виджетах и на дочерних виджетах. Эффективная таблица стилей произвольного виджета получается путём объединения таблиц стилей, установленных на предках виджета (родитель, прародитель и т.д.), а также любой таблицы стилей, установленной на QApplication.
Когда возникают конфликты, собственная таблица стилей виджета всегда имеет приоритет перед любой унаследованной таблицей стилей, независимо от специфичности конфликтующих правил. Аналогично, таблица стилей родительского виджета имеет приоритет перед таблицей стилей прародителя и т.д.
Одним из следствий этого является то, что установка правила стиля на виджет автоматически обеспечивает ему приоритет над другими правилами, указанными в таблицах стилей родительских виджетов или таблице стилей QApplication. Рассмотрим следующий пример. Сначала мы устанавливаем таблицу стилей на QApplication:
qApp->setStyleSheet("QPushButton { color: white }"); Затем мы устанавливаем таблицу стилей на объект QPushButton:
myPushButton->setStyleSheet("* { color: blue }"); Таблица стилей для QPushButton принудительно задаёт для QPushButton (и любого дочернего виджета) синий цвет текста, несмотря на более конкретное правило, установленное в глобальной таблице стилей приложения.
Результат был бы таким же, если бы мы написали
myPushButton->setStyleSheet("color: blue"); за исключением того, что если у QPushButton были бы дочерние элементы (что маловероятно), таблица стилей не повлияла бы на них.
Каскадирование таблиц стилей — сложная тема. Обратитесь к CSS2 Specification за подробностями. Имейте в виду, что Qt в настоящее время не реализует !important.
Наследование
В классическом CSS, когда шрифт и цвет элемента не заданы явно, они автоматически наследуются от родителя. По умолчанию, при использовании таблиц стилей Qt, виджет не автоматически наследует настройки шрифта и цвета от родительского виджета.
Например, рассмотрим QPushButton внутри QGroupBox:
qApp->setStyleSheet("QGroupBox { color: red; } "); QPushButton не имеет явного цвета. Поэтому вместо наследования цвета от родительского QGroupBox, он использует системный цвет. Если мы хотим задать цвет для QGroupBox и его дочерних элементов, мы можем написать:
qApp->setStyleSheet("QGroupBox, QGroupBox * { color: red; }"); В противоположность этому, установка шрифта и палитры с помощью QWidget::setFont() и QWidget::setPalette() распространяется на дочерние виджеты.
Если вы предпочтёте, чтобы шрифт и палитра распространялись на дочерние виджеты, вы можете установить флаг Qt::AA_UseStyleSheetPropagationInWidgetStyles, как это:
Использование:
QCoreApplication::setAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles, true);
Когда распространение шрифта и палитры для виджетов включено, изменения шрифта и палитры, внесённые через таблицы стилей Qt, будут вести себя так, как если бы пользователь вручную вызвал соответствующие методы QWidget::setPalette() и QWidget::setFont() для всех QWidgets, на которые направлен стиль. Если это вызвало бы распространение в C++, это вызовет распространение в таблицах стилей и наоборот.
Виджеты внутри C++-пространств имён
Селектор типа может использоваться для стилизации виджетов определённого типа. Например,
class MyPushButton : public QPushButton {
// ...
}
// ...
qApp->setStyleSheet("MyPushButton { background: yellow; }"); Таблица стилей Qt использует QObject::className() виджета, чтобы определить, когда применять селектор типа. Когда пользовательские виджеты находятся внутри пространств имён, QObject::className() возвращает <namespace>::<classname>. Это конфликтует с синтаксисом для подэлементов. Чтобы решить эту проблему, при использовании селектора типа для виджетов внутри пространств имён, мы должны заменить "::" на "--". Например,
namespace ns {
class MyPushButton : public QPushButton {
// ...
}
}
// ...
qApp->setStyleSheet("ns--MyPushButton { background: yellow; }"); Установка свойств QObject
Начиная с версии 4.3 и выше, любое настраиваемое свойство Q_PROPERTY можно задать с помощью синтаксиса qproperty-<название свойства>.
Например,
MyLabel { qproperty-pixmap: url(pixmap.png); }
MyGroupBox { qproperty-titleColor: rgb(100, 200, 100); }
QPushButton { qproperty-iconSize: 20px 20px; } Если свойство ссылается на перечисление, объявленное с помощью Q_ENUMS, вы должны ссылаться на его константы по имени, а не по их числовому значению.
Примечание: Используйте синтаксис qproperty с осторожностью, так как он изменяет виджет, который отрисовывается. Кроме того, синтаксис qproperty оценивается только один раз, когда виджет полируется стилем. Это означает, что любая попытка использовать их в псевдосостояниях, таких как QPushButton:hover, не сработает.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-5.14/stylesheet-syntax.html