Синтаксис таблицы стилей
Терминология и синтаксические правила таблиц стилей 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)
- игнорируйте псевдоэлементы [например, subcontrols].
Объединение трёх чисел 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 Style Sheets, будут вести себя так, как будто пользователь вручную вызвал соответствующие методы QWidget::setPalette() и QWidget::setFont() для всех QWidgets, на которые направлен стиль. Если бы это вызвало распространение в C++, это вызовет распространение в стилевых листах и наоборот.
Виджеты внутри C++ пространств имён
Селектор типа можно использовать для стилизации виджетов определённого типа. Например,
class MyPushButton : public QPushButton {
// ...
}
// ...
qApp->setStyleSheet("MyPushButton { background: yellow; }"); Qt Style Sheet использует 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, вы должны ссылаться на его константы по имени, а не по числовому значению.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/archives/qt-5.11/stylesheet-syntax.html