Стили и стилизованные виджеты
Стили (классы, наследующие QStyle) выполняют отрисовку от имени виджетов и инкапсулируют внешний вид графического интерфейса. Класс QStyle — это абстрактный базовый класс, инкапсулирующий внешний вид графического интерфейса. Встроенные виджеты Qt используют его для почти всей отрисовки, гарантируя, что они выглядят точно так же, как эквивалентные родные виджеты.
Qt поставляется с набором встроенных стилей. Некоторые стили доступны только на определённых платформах. Пользовательские стили предоставляются в виде плагинов или путём создания экземпляра конкретного класса стиля с помощью QStyleFactory::create() и его установки с помощью QApplication::setStyle().
Настройка стиля
Для настройки существующего стиля необходимо унаследовать от QProxyStyle и переопределить нужные виртуальные методы. QProxyStyle позволяет указать базовый стиль, или он автоматически использует стиль приложения, если базовый стиль не указан. Первый вариант даёт полный контроль над базовым стилем и лучше всего подходит, если настройка ожидает определённого поведения стиля, а второй предоставляет платформенно-независимый способ настройки стиля приложения, по умолчанию использующего стиль родной платформы.
Реализация пользовательского стиля
QCommonStyle предоставляет удобную основу для реализации полностью пользовательских стилей. Подход такой же, как и с QProxyStyle, но необходимо унаследовать от QCommonStyle и переопределить соответствующие виртуальные методы. Реализация полностью пользовательского стиля достаточно сложна, поэтому мы предоставляем это общие сведения. Мы дадим пошаговое руководство по стилизации отдельных виджетов Qt. Мы рассмотрим виртуальные функции, переменные-члены и перечисления класса QStyle.
Часть этого документа, не связанная со стилизованными отдельными виджетами, предназначена для последовательного чтения, поскольку последующие разделы часто зависят от предыдущих. Описание виджетов может быть использовано для справок при реализации стиля. Однако в некоторых случаях может потребоваться обратиться к исходному коду Qt. После прочтения этого документа станет понятен порядок процесса стилизации, что поможет вам найти соответствующий код.
Для разработки стилизованных виджетов (т.е. виджетов, которые соответствуют стилю, в котором они отрисовываются), необходимо отрисовывать их с помощью текущего стиля. В этом документе показано, как виджеты отрисовывают сами себя и какие возможности предоставляет стиль.
Классы для стилизации виджетов
Эти классы используются для настройки внешнего вида и стиля приложения.
Цвета на основе RGB, HSV или CMYK значений |
|
Абстракция пространства цветов |
|
Преобразование между цветовыми пространствами |
|
Инкапсулирует общий внешний вид графического интерфейса |
|
Курсор мыши произвольной формы |
|
Определяет запрос шрифта, используемого для отрисовки текста |
|
Информация о доступных шрифтах в системе окон |
|
Общие сведения о шрифтах |
|
Представляет якорь между двумя элементами в QGraphicsAnchorLayout |
|
Макет, где можно привязать виджеты друг к другу в представлении Graphics View |
|
Содержит группы цветов для каждого состояния виджета |
|
Абстрактный базовый класс, инкапсулирующий внешний вид графического интерфейса |
|
Создаёт объекты QStyle |
|
Подсказки стиля, возвращающие больше, чем базовые типы данных |
|
Подсказки стиля, возвращающие QRegion |
|
Подсказки стиля, возвращающие QVariant |
|
Хранит параметры, используемые функциями QStyle |
|
Удобный класс для отрисовки элементов QStyle внутри виджета |
Реализация QStyle
API QStyle содержит функции, которые рисуют виджеты, статические вспомогательные функции для выполнения общих и сложных задач (например, вычисления положения ползунков ползунков), и функции для выполнения различных вычислений во время рисования (например, для вычисления подсказок размера виджетов). Стиль также помогает некоторым виджетам с макетом их содержимого. Кроме того, он создаёт QPalette, содержащую QBrushes для рисования.
QStyle рисует графические элементы; элемент — это виджет или часть виджета, например, отступ кнопки, рамка окна или полоса прокрутки. Большинство функций рисования теперь принимают четыре аргумента:
- значение перечисления, определяющее, какой графический элемент следует отрисовать
- QStyleOption, определяющий, как и где отрисовать этот элемент
- QPainter, который должен использоваться для отрисовки элемента
- QWidget, на котором выполняется отрисовка (необязательно)
Когда виджет просит стиль отрисовать элемент, он предоставляет стилю QStyleOption, который является классом, содержащим информацию, необходимую для отрисовки. Благодаря QStyleOption возможно сделать QStyle отрисовать виджеты без связывания какого-либо кода для виджета. Это позволяет использовать функции рисования QStyle на любом устройстве рисования, то есть вы можете нарисовать раскрывающийся список на любом виджете, а не только на QComboBox.
Виджет передаётся в качестве последнего аргумента, если стилю требуется выполнить специальные эффекты (например, анимированные кнопки по умолчанию в macOS), но это не обязательно.
В этом разделе мы рассмотрим элементы стиля, параметры стиля и функции QStyle. В заключение, мы опишем, как используется палитра.
Элементы в представлениях элементов отрисовываются делегатами в Qt. Заголовки представления элементов всё ещё отрисовываются стилем. По умолчанию делегат Qt, QStyledItemDelegate, частично отрисовывает свои элементы с помощью текущего стиля; он отрисовывает индикаторы флажков и вычисляет ограничивающие прямоугольники для элементов, из которых состоит элемент. В этом документе мы описываем только то, как реализовать подкласс QStyle. Если вы хотите добавить поддержку других типов данных, чем те, которые поддерживаются QStyledItemDelegate, вам необходимо реализовать пользовательский делегат. Обратите внимание, что делегаты должны быть установлены программно для каждого отдельного виджета (т.е. делегаты по умолчанию не могут быть предоставлены в виде плагинов).
Элементы стиля
Элемент стиля — это графическая часть графического интерфейса. Виджет состоит из иерархии (или дерева) элементов стиля. Например, когда стиль получает запрос на отрисовку кнопки (например, из QPushButton), он отрисовывает метку (текст и значок), отступ кнопки и рамку фокуса. Отступ кнопки, в свою очередь, состоит из рамки вокруг отступа и двух других элементов, которые мы рассмотрим позже. Ниже представлена концептуальная иллюстрация дерева элементов кнопки.
Виджеты не обязательно рисуются путём запроса к стилю только одного элемента. Виджеты могут вызывать стиль несколько раз для отрисовки разных элементов. Примером является QTabWidget, который отрисовывает свои вкладки и рамку по отдельности.
Существует три типа элементов: примитивные элементы, элементы управления и сложные элементы управления. Элементы определяются перечислениями ComplexControl, ControlElement и PrimitiveElement. Значения каждого перечисления имеют префикс для идентификации их типа: CC_ для сложных элементов, CE_ для элементов управления и PE_ для примитивных элементов. В следующих трёх разделах мы рассмотрим, что определяет различные элементы, и увидим примеры виджетов, которые их используют.
Описание класса QStyle содержит список этих элементов и их роли в стилизации виджетов. Мы увидим, как они используются при стилизации отдельных виджетов.
Примитивные элементы
Примитивные элементы — это графические элементы, которые являются общими и часто используются несколькими виджетами. Примеры включают рамки, отступы кнопок и стрелки для спин-боксов, полос прокрутки и выпадающих списков. Примитивные элементы не могут существовать сами по себе: они всегда являются частью более крупного объекта. Они не принимают участия во взаимодействии с пользователем, а являются пассивными декорациями в графическом интерфейсе.
Элементы управления
Элемент управления выполняет действие или отображает информацию пользователю. Примеры элементов управления — кнопки, флажки и заголовки секций в таблицах и древовидных представлениях. Элементы управления не обязательно являются полными виджетами, такими как кнопки, но могут также быть частями виджетов, такими как вкладки панели вкладок и ползунки полосы прокрутки. Они отличаются от примитивных элементов тем, что не являются пассивными, а выполняют функцию во взаимодействии с пользователем. Элементы управления, состоящие из нескольких элементов, часто используют стиль для вычисления границ элементов. Доступные дочерние элементы определяются перечислением SubElement. Это перечисление используется только для вычисления границ; дочерние элементы не являются графическими элементами для рисования, как примитивные, управляющие и сложные элементы.
Сложные элементы управления
Сложные элементы управления содержат дочерние элементы управления. Сложные элементы ведут себя по-разному в зависимости от того, где пользователь взаимодействует с ними с помощью мыши и каких клавиш на клавиатуре нажаты. Это зависит от того, над каким дочерним элементом (если таковой имеется) наведена мышь или нажата кнопка мыши. Примерами сложных элементов управления являются полосы прокрутки и комбинированные поля. С помощью полосы прокрутки вы можете перемещать ползунок с помощью мыши и нажимать кнопки «Вверх» и «Вниз». Доступные дочерние элементы управления определяются перечислением SubControl.
Помимо рисования, стиль должен предоставлять виджетам информацию о том, на какой дочерний элемент управления (если таковой имеется) было произведено нажатие мышью. Например, QScrollBar должен знать, нажал ли пользователь на ползунок, желобок ползунка или одну из кнопок.
Обратите внимание, что дочерние элементы управления не являются теми же элементами управления, что и описанные в предыдущем разделе. Вы не можете использовать стиль для рисования дочернего элемента управления; стиль будет только вычислять прямоугольник, в котором должен быть нарисован дочерний элемент управления. Однако часто сложные элементы используют элементы управления и примитивные элементы для рисования своих дочерних элементов управления, что является подходом, часто используемым встроенными стилями в Qt и также стилем Java. Например, стиль Java использует PE_IndicatorCheckBox для рисования флажка в группах (который является дочерним элементом управления CC_GroupBox). У некоторых дочерних элементов управления есть эквивалентный элемент управления, например, ползунок полосы прокрутки (SC_SCrollBarSlider и CE_ScrollBarSlider).
Другие задачи QStyle
Элементы и виджеты стиля, как уже упоминалось, используют стиль для вычисления границ дочерних элементов и дочерних элементов управления. Для измерений при рисовании также используются пиксельные метрики, которые являются зависящими от стиля размерами в пикселях экрана. Доступные прямоугольники и пиксельные метрики представлены тремя перечислениями в QStyle: SubElement, SubControl и PixelMetric. Значения перечислений легко идентифицировать, так как они начинаются с SE_, SC_ и PM_.
Стиль также содержит набор подсказок стиля, которые представлены значениями в перечислении StyleHint. Все виджеты не имеют одинаковой функциональности и внешнего вида в разных стилях. Например, когда пункты меню в меню не помещаются в один столбец на экране, некоторые стили поддерживают прокрутку, а другие рисуют более одного столбца, чтобы поместить все пункты.
Стиль обычно имеет набор стандартных изображений (например, предупреждения, вопроса и ошибки) для диалоговых окон с сообщениями, диалоговых окон с файлами и т. д. QStyle предоставляет перечисление StandardPixmap. Его значения представляют собой стандартные изображения. Виджеты Qt используют их, поэтому при реализации пользовательского стиля вы должны предоставить изображения, используемые стилем, который вы реализуете.
Стиль вычисляет отступы между виджетами в макетах. Есть два способа, которыми стиль может обрабатывать эти вычисления. Вы можете установить PM_LayoutHorizontalSpacing и PM_LayoutVerticalSpacing, что является способом работы стиля Java (через QCommonStyle). В качестве альтернативы, вы можете реализовать QStyle::layoutSpacing() и QStyle::layoutSpacingImplementation(), если вам нужен больший контроль над этой частью макета. В этих функциях вы можете вычислить отступы на основе типов элементов управления (QSizePolicy::ControlType) для различных политик размера (QSizePolicy::Policy), а также опции стиля для рассматриваемого виджета.
Опции стиля
Подклассы QStyleOption содержат всю необходимую информацию для стилизации отдельных элементов. Опции стиля создаются — обычно в стеке — и заполняются вызывающим функцией QStyle. В зависимости от того, что рисуется, стиль будет ожидать другой класс опций стиля. Например, элемент QStyle::PE_FrameFocusRect ожидает аргумент QStyleOptionFocusRect, и можно создать пользовательские подклассы, которые может использовать пользовательский стиль. Опции стиля сохраняют публичные переменные для повышения производительности.
Виджеты могут находиться в нескольких различных состояниях, которые определяются перечислением State. Некоторые флаги состояния имеют разное значение в зависимости от виджета, но другие являются общими для всех виджетов, например, State_Disabled. Именно QStyleOption устанавливает общие состояния с помощью QStyleOption::initFrom; остальные состояния устанавливаются отдельными виджетами.
Наиболее заметно, что опции стиля содержат палитру и ограничивающие прямоугольники виджетов, которые должны быть нарисованы. У большинства виджетов есть специализированные опции стиля. QPushButton и QCheckBox, например, используют QStyleOptionButton в качестве опции стиля, которая содержит текст, значок и размер значка. Точное содержимое всех опций описывается при рассмотрении отдельных виджетов.
При повторной реализации функций QStyle, которые принимают параметр QStyleOption, вам часто нужно привести QStyleOption к подклассу (например, QStyleOptionFocusRect). Для безопасности можно использовать qstyleoption_cast() для проверки того, что тип указателя является правильным. Если объект не является нужного типа, qstyleoption_cast() возвращает nullptr. Например:
const QStyleOptionFocusRect *focusRectOption =
qstyleoption_cast<const QStyleOptionFocusRect *>(option);
if (focusRectOption) {
...
} Следующий фрагмент кода иллюстрирует использование QStyle для рисования прямоугольника фокуса из пользовательского виджета в событии paintEvent():
void MyWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
...
QStyleOptionFocusRect option(1);
option.init(this);
option.backgroundColor = palette().color(QPalette::Window);
style().drawPrimitive(QStyle::PE_FrameFocusRect, &option, &painter,
this);
} Следующий пример демонстрирует, как производятся от существующего стиля для настройки внешнего вида графического элемента:
class CustomStyle : public QProxyStyle
{
Q_OBJECT
public:
CustomStyle(const QWidget *widget);
~CustomStyle() {}
void drawPrimitive(PrimitiveElement element, const QStyleOption *option,
QPainter *painter, const QWidget *widget) const override;
};
void CustomStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
QPainter *painter, const QWidget *widget) const
{
if (element == PE_IndicatorSpinUp || element == PE_IndicatorSpinDown) {
QPolygon points(3);
int x = option->rect.x();
int y = option->rect.y();
int w = option->rect.width() / 2;
int h = option->rect.height() / 2;
x += (option->rect.width() - w) / 2;
y += (option->rect.height() - h) / 2;
if (element == PE_IndicatorSpinUp) {
points[0] = QPoint(x, y + h);
points[1] = QPoint(x + w, y + h);
points[2] = QPoint(x + w / 2, y);
} else { // PE_SpinBoxDown
points[0] = QPoint(x, y);
points[1] = QPoint(x + w, y);
points[2] = QPoint(x + w / 2, y + h);
}
if (option->state & State_Enabled) {
painter->setPen(option->palette.mid().color());
painter->setBrush(option->palette.buttonText());
} else {
painter->setPen(option->palette.buttonText().color());
painter->setBrush(option->palette.mid());
}
painter->drawPolygon(points);
} else {
QProxyStyle::drawPrimitive(element, option, painter, widget);
}
} Функции QStyle
Класс QStyle определяет три функции для рисования примитивных, управляющих и сложных элементов: drawPrimitive(), drawControl() и drawComplexControl(). Функции принимают следующие параметры:
- значение перечисления элемента для рисования.
- QStyleOption, содержащий информацию, необходимую для рисования элемента.
- QPainter для рисования элемента.
- указатель на QWidget, обычно виджет, на котором нарисован элемент.
Не все виджеты отправляют указатель на себя. Если опция стиля, отправленная в функцию, не содержит необходимой вам информации, вы должны проверить реализацию виджета, чтобы увидеть, отправляет ли он указатель на себя.
Класс QStyle также предоставляет вспомогательные функции, которые используются при рисовании элементов. Функция drawItemText() рисует текст в заданном прямоугольнике, принимая QPalette в качестве параметра. Функция drawItemPixmap() помогает выровнять пиксель внутри заданного ограничивающего прямоугольника.
Другие функции QStyle выполняют различные вычисления для функций, которые выполняют рисование. Виджеты также используют эти функции для вычисления размеров подсказок и ограничивающих прямоугольников, если сами рисуют несколько элементов стиля. Как и функции, рисующие элементы, вспомогательные функции обычно принимают те же аргументы.
- Функция subElementRect() принимает значение перечисления SubElement и вычисляет ограничивающий прямоугольник для дочернего элемента. Стиль использует эту функцию, чтобы знать, где рисовать разные части элемента. Это в основном делается для повторного использования; если вы создаете новый стиль, вы можете использовать то же расположение дочерних элементов, что и у родительского класса.
- Функция subControlRect() используется для вычисления ограничивающих прямоугольников для дочерних элементов управления в сложных элементах управления. При реализации нового стиля вы переопределяете
subControlRect()и вычисляете прямоугольники, отличные от родительского класса. - Функция pixelMetric() возвращает пиксельную метрику, которая представляет собой размер, зависящий от стиля, заданный в пикселях экрана. Она принимает значение перечисления PixelMetric и возвращает правильное значение измерения. Обратите внимание, что пиксельные метрики не обязательно должны быть статическими измерениями, а могут быть вычислены, например, с помощью опции стиля.
- Функция hitTestComplexControl() возвращает дочерний элемент управления, над которым находится указатель мыши в сложном элементе управления. Обычно это просто вопрос использования subControlRect() для получения ограничивающих прямоугольников дочерних элементов управления, а затем проверки того, какой прямоугольник содержит положение курсора.
QStyle также имеет функции polish() и unpolish(). Все виджеты отправляются в функцию polish() перед отображением и в функцию unpolish() при их скрытии. Вы можете использовать эти функции для установки атрибутов на виджеты или выполнения других операций, необходимых вашему стилю. Например, если вам нужно знать, когда курсор находится над виджетом, вам необходимо установить атрибут виджета WA_Hover. Флаг состояния State_MouseOver затем будет установлен в опциях стиля виджета.
QStyle имеет несколько статических вспомогательных функций, которые выполняют некоторые общие и сложные задачи. Они могут вычислять положение ручки ползунка по значению ползунка и преобразовывать прямоугольники и текст с учетом обратной компоновки; см. документацию класса QStyle для получения более подробной информации.
Обычно при переопределении виртуальных функций QStyle выполняется работа над элементами, которые отличаются от родительского класса; для всех остальных элементов вы можете просто использовать реализацию родительского класса.
Палитра
Каждый стиль предоставляет палитру цветов — то есть QBrush — которая должна использоваться для рисования виджетов. Существует один набор цветов для различных состояний виджета (QPalette::ColorGroup): активное (виджеты в окне, имеющие фокус клавиатуры), неактивное (виджеты, используемые для других окон) и отключенное (виджеты, которые отключены). Состояния можно найти, запросив флаги состояния State_Active и State_Enabled. Каждый набор содержит определенные роли цветов, заданные перечислением QPalette::ColorRole. Роли описывают ситуации, в которых цвета должны использоваться (например, для рисования фонов виджетов, текста или кнопок).
Как используются роли цвета, зависит от стиля. Например, если стиль использует градиенты, можно использовать цвет из палитры и сделать его темнее или светлее с помощью QColor::darker() и QColor::lighter() для создания градиента. В общем случае, если вам нужен кисть, которая не предоставляется палитрой, вы должны попытаться получить её из палитры.
QPalette, которая предоставляет палитру, хранит цвета для различных состояний виджетов и ролей цветов. Палитра для стиля возвращается функцией standardPalette(). Стандартная палитра не устанавливается автоматически, когда новый стиль устанавливается для приложения (QApplication::setStyle()) или виджета (QWidget::setStyle()), поэтому вы должны установить палитру самостоятельно с помощью (QApplication::setPalette()) или (QWidget::setPalette()).
Не рекомендуется жёстко кодировать цвета, так как приложения и отдельные виджеты могут устанавливать свои собственные палитры, а также использовать палитру стиля для рисования. Обратите внимание, что ни один из виджетов Qt не устанавливает свою собственную палитру. Стиль Java жёстко кодирует некоторые цвета, но только по решению автора; это не рекомендуется. Конечно, не предполагается, что стиль будет выглядеть хорошо с любой палитрой.
Проблемы реализации
При реализации стилей необходимо учитывать несколько проблем. Здесь мы дадим некоторые подсказки и рекомендации по реализации.
При реализации стилей необходимо просмотреть код виджетов и код базового класса и его предков. Это связано с тем, что виджеты используют стиль по-разному, так как реализация в различных виртуальных функциях стилей может влиять на состояние отрисовки (например, путём изменения состояния QPainter без его восстановления и отрисовки некоторых элементов без использования соответствующих метрик пикселей и подэлементов).
Рекомендуется, чтобы стили не изменяли предлагаемый размер виджетов с помощью функции QStyle::sizeFromContents, а вместо этого позволили реализации QCommonStyle обрабатывать это. Если изменения необходимы, вы должны пытаться сохранять их минимальными; разработка приложений может быть затруднена, если макет виджетов выглядит существенно по-разному в различных стилях.
Стиль Java
Мы реализовали стиль, который напоминает стандартный внешний вид Java (ранее известный как Metal). Мы сделали это, так как его относительно просто реализовать, и мы хотели создать стиль для этого обзорного документа. Для простоты и краткости мы несколько упростили стиль, но Qt может создать точную копию стиля. Однако конкретных планов по реализации стиля как части Qt нет.
В этом разделе мы рассмотрим некоторые проблемы реализации. Наконец, мы увидим полный пример стилизации виджета Java. Мы будем продолжать использовать стиль Java в документе для примеров и изображений виджетов. Сама реализация несколько сложна, и не предполагается, что вы должны её прочитать полностью.
Проектирование и реализация
Первым шагом при проектировании стиля было выбор базового класса. Мы выбрали подкласс QCommonStyle. Этот класс реализует большую часть необходимой функциональности, за исключением фактического рисования.
Стиль реализован в одном классе. Мы сделали это, потому что считаем удобным хранить весь код в одном файле. Кроме того, это выгодно с точки зрения оптимизации, так как мы создаём меньше объектов. Мы также стараемся минимизировать количество функций, используя переключатели для определения элемента, который нужно нарисовать, в функциях. Это приводит к большим функциям, но поскольку мы разделяем код для каждого элемента в переключателях, код всё ещё должен быть легко читаемым.
Ограничения и различия со стилем Java
Мы не полностью реализовали каждый элемент в стиле Java. Таким образом, мы уменьшили объём и сложность кода. В целом, стиль предназначался как практический пример для этого обзорного документа по стилям, а не как часть самого Qt.
Не все виджеты имеют реализованное каждое состояние. Это относится к состояниям, которые являются общими, например, State_Disabled. Однако каждое состояние реализовано, по крайней мере, для одного виджета.
Мы реализовали только отметки под ползунком. Плоские кнопки также опущены. Мы не обрабатываем случай, когда заголовки панелей и заголовки окон док-станции становятся слишком маленькими для своего содержимого, а просто рисуем управляющие элементы друг поверх друга.
Мы не пытались эмулировать шрифты Java. Java и Qt используют очень разные движки шрифтов, поэтому мы не считаем это оправданным усилием, поскольку мы используем стиль только как пример для этого обзора.
Мы жёстко запрограммировали цвета (мы не используем QPalette) для линейных градиентов, которые используются, например, для фасок кнопок, панелей инструментов и флажков. Это связано с тем, что палитра Java не может генерировать эти цвета. Java также не изменяет эти цвета в зависимости от группы цветов или роли виджета (они не зависят от палитры), поэтому это не проблема в любом случае.
Стиль применяется к виджетам Qt. Некоторые виджеты вообще не существуют в Java, например, QToolBox. Другие содержат элементы, которых нет в виджетах Java. Деревовидный виджет является примером последнего, в котором у Java JTree нет заголовка.
Стиль не обрабатывает обратные макеты. Мы предполагаем, что направление макета слева направо. QCommonStyle обрабатывает обратные виджеты; если мы реализовали бы обратные макеты, виджеты, которые мы изменяем положение подэлементов или обрабатываем выравнивание текста в метках самостоятельно, потребовали бы обновления.
Стилизация флажков Java
В качестве примера мы рассмотрим стилизацию флажков в стиле Java. Мы опишем весь процесс и напечатаем весь код, как в стиле Java, так и в классах Qt, которые вовлечены. В остальной части этого документа мы не будем рассматривать исходный код отдельных виджетов. Надеюсь, это даст вам представление о том, как искать в коде, если вам нужно проверить определённые детали реализации; большинство виджетов следуют той же структуре, что и флажки.
Начнём с того, как QCheckBox создаёт свой вариант стиля, который является QStyleOptionButton для флажков:
opt.initFrom(q);
if (down)
opt.state |= QStyle::State_Sunken;
if (tristate && noChange)
opt.state |= QStyle::State_NoChange;
else
opt.state |= checked ? QStyle::State_On :
QStyle::State_Off;
if (q->testAttribute(Qt::WA_Hover) && q->underMouse()) {
if (hovering)
opt.state |= QStyle::State_MouseOver;
else
opt.state &= ~QStyle::State_MouseOver;
}
opt.text = text;
opt.icon = icon;
opt.iconSize = q->iconSize(); Сначала мы позволяем QStyleOption настроить вариант с информацией, которая является общей для всех виджетов с initFrom(). Мы рассмотрим это вскоре.
Переменная QStyleOption's down равна true, когда пользователь нажимает на поле; это верно для флажка, независимо от того, установлен ли флажок или нет. Состояние State_NoChange устанавливается, когда у нас есть флажок со тремя состояниями, и он частично установлен. Оно имеет State_On, если флажок установлен, и State_Off, если он снят. State_MouseOver устанавливается, если мышь находится над флажком, и у виджета установлен атрибут Qt::WA_Hover — вы устанавливаете это в QStyle::polish(). Кроме того, вариант стиля также содержит текст, значок и размер значка кнопки.
initFrom() устанавливает вариант стиля с атрибутами, которые являются общими для всех виджетов. Мы напечатаем его реализацию здесь:
state = QStyle::State_None;
if (widget->isEnabled())
state |= QStyle::State_Enabled;
if (widget->hasFocus())
state |= QStyle::State_HasFocus;
if (widget->window()->testAttribute(Qt::WA_KeyboardFocusChange))
state |= QStyle::State_KeyboardFocusChange;
if (widget->underMouse())
state |= QStyle::State_MouseOver;
if (widget->window()->isActiveWindow())
state |= QStyle::State_Active;
#ifdef QT_KEYPAD_NAVIGATION
if (widget->hasEditFocus())
state |= QStyle::State_HasEditFocus;
#endif
direction = widget->layoutDirection();
rect = widget->rect();
palette = widget->palette();
fontMetrics = widget->fontMetrics(); State_Enabled устанавливается, когда виджет включён. Когда виджет имеет фокус, устанавливается флаг State_HasFocus. Аналогично, флаг State_Active устанавливается, когда виджет является дочерним элементом активного окна. Флаг State_MouseOver будет установлен только в том случае, если у виджета установлен флаг окна WA_HoverEnabled. Обратите внимание, что для включения State_HasEditFocus в Qt необходимо включить навигацию с помощью клавиатуры; она не включена по умолчанию.
В дополнение к установке флагов состояния QStyleOption содержит другую информацию о виджете: direction — направление макета, rect — ограничительная прямоугольная область виджета (область, в которой нужно рисовать), palette — QPalette, которая должна использоваться для рисования виджета, и fontMetrics — метрики шрифта, используемого виджетом.
Мы приводим изображение флажка и соответствующий ему вариант стиля.
У приведенного выше флажка в его варианте стиля будут следующие флаги состояний:
| Флаг состояния | Установлен |
|---|---|
State_Sunken |
Да |
State_NoChange |
Нет |
State_On |
Да |
State_Off |
Нет |
State_MouseOver |
Да |
State_Enabled |
Да |
State_HasFocus |
Да |
State_KeyboardFocusChange |
Нет |
State_Active |
Да |
QCheckBox рисует себя в QWidget::paintEvent() с вариантом стиля opt и QStylePainter p. Класс QStylePainter — это удобный класс для рисования элементов стиля. В первую очередь, он обволакивает методы QStyle, используемые для рисования. QCheckBox рисует себя следующим образом:
QStylePainter p(this);
QStyleOptionButton opt = d->getStyleOption();
p.drawControl(QStyle::CE_CheckBox, opt); QCommonStyle обрабатывает элемент CE_CheckBox. QCheckBox имеет два подэлемента: SE_CheckBoxIndicator (индикатор установленного флажка) и SE_CheckBoxContents (содержимое, используемое для метки флажка). QCommonStyle также реализует ограничительные прямоугольные области этих подэлементов. Далее мы рассмотрим код QCommonStyle:
QStyleOptionButton subopt = *btn;
subopt.rect = subElementRect(SE_CheckBoxIndicator, btn, widget);
drawPrimitive(PE_IndicatorCheckBox, &subopt, p, widget);
subopt.rect = subElementRect(SE_CheckBoxContents, btn, widget);
drawControl(CE_CheckBoxLabel, &subopt, p, widget);
if (btn->state & State_HasFocus) {
QStyleOptionFocusRect fropt;
fropt.QStyleOption::operator=(*btn);
fropt.rect = subElementRect(SE_CheckBoxFocusRect, btn, widget);
drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
} Как видно из фрагмента кода, общий стиль получает ограничительные прямоугольные области двух подэлементов CE_CheckBox и затем рисует их. Если флажок имеет фокус, также рисуется рамка фокуса.
Стиль Java рисует CE_CheckBoxIndicator, а QCommonStyle обрабатывает CE_CheckboxLabel. Мы рассмотрим каждую реализацию и начнём с CE_CheckBoxLabel:
const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt);
uint alignment = visualAlignment(btn->direction, Qt::AlignLeft | Qt::AlignVCenter);
if (!styleHint(SH_UnderlineShortcut, btn, widget))
alignment |= Qt::TextHideMnemonic;
QPixmap pix;
QRect textRect = btn->rect;
if (!btn->icon.isNull()) {
pix = btn->icon.pixmap(btn->iconSize, btn->state & State_Enabled ? QIcon::Normal : QIcon::Disabled);
drawItemPixmap(p, btn->rect, alignment, pix);
if (btn->direction == Qt::RightToLeft)
textRect.setRight(textRect.right() - btn->iconSize.width() - 4);
else
textRect.setLeft(textRect.left() + btn->iconSize.width() + 4);
}
if (!btn->text.isEmpty()){
drawItemText(p, textRect, alignment | Qt::TextShowMnemonic,
btn->palette, btn->state & State_Enabled, btn->text, QPalette::WindowText);
} visualAlignment() корректирует выравнивание текста в соответствии с направлением компоновки. Затем мы рисуем значок, если он существует, и корректируем оставшееся место для текста. drawItemText() рисует текст, учитывая выравнивание, направление компоновки и мненю. Также используется палитра для рисования текста в правильном цвете.
Отрисовка меток часто бывает довольно сложной. К счастью, её обычно можно выполнить в базовом классе. Стиль Java реализует свою собственную метку кнопки, так как Java центрирует содержимое кнопок и при наличии значка. Вы можете изучить эту реализацию, если вам нужен пример переопределения рисования метки.
Теперь мы рассмотрим реализацию Java для CE_CheckBoxIndicator в drawControl():
case PE_IndicatorCheckBox: {
painter->save();
drawButtonBackground(option, painter, true);
if (option->state & State_Enabled &&
option->state & State_MouseOver &&
!(option->state & State_Sunken)) {
painter->setPen(option->palette.color(QPalette::Button));
QRect rect = option->rect.adjusted(1, 1, -2, -2);
painter->drawRect(rect);
rect = rect.adjusted(1, 1, -1, -1);
painter->drawRect(rect);
}
if (option->state & State_On) {
QImage image(":/images/checkboxchecked.png");
painter->drawImage(option->rect.topLeft(), image);
}
painter->restore();
break; Сначала сохраняем состояние рисовальщика. Это не всегда необходимо, но в этом случае QCommonStyle требует рисовальщика в том же состоянии, что и при вызове PE_IndicatorCheckBox (конечно, мы также можем установить состояние с помощью вызовов функций). Затем используем drawButtonBackground() для рисования фона индикатора флажка. Это вспомогательная функция, которая рисует фон, а также рамку кнопок и флажков. Мы рассмотрим эту функцию ниже. Затем проверяем, наведён ли курсор мыши на флажок. Если это так, мы рисуем рамку, которая есть у флажков Java, когда флажок не нажат, а курсор мыши над ним. Обратите внимание, что Java не обрабатывает трёхсостоятельные флажки, поэтому мы не реализовали его.
Здесь мы используем PNG-изображение для нашего индикатора. Мы также можем проверить, отключён ли виджет. Тогда нам нужно будет использовать другое изображение с индикатором в отключённом цвете.
void JavaStyle::drawButtonBackground(const QStyleOption *option,
QPainter *painter, bool isCheckbox) const
{
QBrush buttonBrush = option->palette.button();
bool sunken = option->state & State_Sunken;
bool disabled = !(option->state & State_Enabled);
bool on = option->state & State_On;
if (!sunken && !disabled && (!on || isCheckbox))
buttonBrush = gradientBrush(option->rect);
painter->fillRect(option->rect, buttonBrush);
QRect rect = option->rect.adjusted(0, 0, -1, -1);
if (disabled)
painter->setPen(option->palette.color(QPalette::Disabled,
QPalette::WindowText));
else
painter->setPen(option->palette.color(QPalette::Mid));
painter->drawRect(rect);
if (sunken && !disabled) {
drawSunkenButtonShadow(painter, rect,
option->palette.color(QPalette::Mid),
option->direction == Qt::RightToLeft);
}
} Мы видели, как флажки стилизуются в стиле Java, от момента получения виджетом запроса на отрисовку до завершения рисования стилем. Чтобы подробно изучить, как отрисовывается каждый виджет, необходимо просмотреть код пошагово, как мы сделали здесь. Однако, обычно достаточно знать, какие элементы стиля виджеты рисуют. Виджет создаёт опцию стиля и вызывает стиль один или несколько раз для отрисовки элементов стиля, из которых он состоит. Обычно достаточно знать состояния, в которых может находиться виджет, и другое содержимое опции стиля, т. е. то, что мы перечислим в следующем разделе.
Обзор виджетов
В этом разделе мы рассмотрим, как стилизуются большинство виджетов Qt. Надеемся, это сэкономит вам время и усилия при разработке собственных стилей и виджетов. Здесь вы не найдете информации, недоступной в других местах (т. е. просмотре исходного кода или описаний классов, связанных со стилем).
В основном мы используем виджеты стиля Java в качестве примеров. Стиль Java не рисует каждый элемент в дереве элементов. Это связано с тем, что они не видны для этого виджета в стиле Java. Мы по-прежнему гарантируем, что все элементы реализованы в соответствии со стилем Java, поскольку пользовательские виджеты могут их потребовать (хотя это не исключает оставлять реализацию для QCommonStyle).
Для каждого виджета приводится:
- Таблица членов (переменных и т. д.) его опции стиля.
- Таблица флагов состояния (QStyle::StateFlag), которые могут быть установлены на виджете и когда состояния устанавливаются.
- Дерево его элементов (см. раздел Элементы стиля).
- Изображение виджета, на котором элементы выделены.
Дерево элементов содержит примитивные, управляющие и сложные элементы стиля. Выполняя обход дерева элементов сверху вниз, вы получаете последовательность, в которой должны отрисовываться элементы. В узлах мы написали прямоугольники дочерних элементов, дочерние элементы управления и пиксельные метрики, которые следует учитывать при рисовании элемента узла.
Наш подход к стилизации сосредоточен на отрисовке виджетов. Вычисления прямоугольников дочерних элементов, дочерних элементов управления и пиксельных метрик, используемые во время отрисовки, перечислены только как содержимое в деревьях элементов. Обратите внимание, что есть прямоугольники и пиксельные метрики, используемые только виджетами. Это оставляет эти вычисления без обработки в обзоре. Например, функции subControlRect() и sizeFromContents() часто вызывают subElementRect() для расчёта ограничивающих прямоугольников. Мы могли бы рисовать и деревья для этого. Однако способ выполнения этих вычислений полностью зависит от отдельных стилей, и они не должны следовать определённой структуре (Qt не накладывает специфической структуры). Тем не менее, вы по-прежнему должны убедиться, что используете соответствующие пиксельные метрики. Чтобы ограничить размер документа, мы поэтому выбрали не включать деревья или описывать вычисления, выполненные стилем Java (или любым другим).
Вы можете запутаться в том, как следует использовать различные пиксельные метрики, прямоугольники дочерних элементов и прямоугольники дочерних элементов управления при изучении деревьев. Если у вас возникли сомнения после прочтения описаний перечислений QStyle, мы рекомендуем вам изучить реализацию QCommonStyle.
Некоторые ограничивающие прямоугольники, которые мы выделяем на изображениях виджетов, совпадают. Причинами являются то, что некоторые элементы рисуют фоны, а другие — рамки и метки. При сомнениях проверьте описание каждого элемента в QStyle. Кроме того, некоторые элементы предназначены для компоновки, т. е. для определения места для рисования других элементов.
Общие свойства виджетов
Некоторые состояния и переменные являются общими для всех виджетов. Они устанавливаются с помощью QStyleOption::initFrom(). Не все элементы используют эту функцию; это виджеты, создающие опции стиля, а для некоторых элементов информация из initFrom() не требуется.
Ниже приведена таблица общих состояний:
| Состояние | Когда состояние устанавливается |
|---|---|
State_Enabled |
Устанавливается, если виджет не отключён (см. QWidget::setEnabled()) |
State_Focus |
Устанавливается, если виджет имеет фокус (см. QWidget::hasFocus()) |
State_KeyobordFocusChange |
Устанавливается, когда пользователь меняет фокус с помощью клавиатуры (см. Qt::WA_KeyboardFocusChange) |
State_MouseOver |
Устанавливается, если курсор мыши находится над виджетом |
State_Active |
Устанавливается, если виджет является дочерним элементом активного окна. |
State_HasEditFocus |
Устанавливается, если виджет имеет фокус редактирования |
Другие общие члены для виджетов:
| Член | Содержимое |
|---|---|
| rect | Ограничительный прямоугольник элемента, который необходимо отрисовать. Он устанавливается в прямоугольник виджета (QWidget::rect()). |
| direction | Направление компоновки; значение перечисления Qt::LayoutDirection. |
| palette | QPalette для использования при отрисовке элемента. Устанавливается в палитру виджета (QWidget::palette()). |
| fontMetrics | QFontMetrics для использования при отрисовке текста на виджете. |
Сложные опции стиля (классы, наследующие от QStyleOptionComplex) для сложных элементов стиля используют две переменные: subControls и activeSubControls. Обе переменные представляют собой битовое ИЛИ-соединение значений перечисления QStyle::SubControl. Они указывают, из каких подэлементов управления состоит сложное управление и какие из этих элементов управления в настоящее время активны.
Как уже упоминалось, стиль вычисляет размер содержимого виджета, от которого виджеты получают свои подсказки размера. Кроме того, сложные элементы управления также используют стиль для проверки того, над какой подконтролью находится курсор мыши.
Справочник виджетов
Без дальнейших задержек мы представляем обзор виджетов; каждый виджет имеет свой подраздел.
Кнопки
Структура стиля для кнопок показана ниже. Выполняя обход дерева сверху вниз, вы получите последовательность, в которой должны отрисовываться элементы.
Компоновка кнопок, в отношении границ элементов, отличается в зависимости от стиля. Это затрудняет показ концептуальных изображений. Кроме того, элементы могут — и даже должны — иметь одинаковые границы; PE_PushButtonBevel, например, используется в QCommonStyle для рисования содержащихся в нём элементов: PE_FrameDefaultButton, PE_FrameButtonBevel, и PE_PanelButtonCommand, все из которых имеют общую границу в общем стиле. PE_PushButtonBevel также отвечает за отрисовку индикатора меню (QCommonStyle рисует PE_IndicatorArrowDown).
Ниже приведено изображение кнопки в стиле Java, показывающее ограничивающие прямоугольники элементов. Цвета используются для разделения ограничивающих прямоугольников на изображении; они не выполняют никакой другой цели. То же самое относится к аналогичным изображениям для других виджетов.
Стиль Java, а также все остальные стили, реализованные в Qt, не используют PE_FrameButtonBevel. Обычно кнопка с PE_DefaultFrame корректирует прямоугольник PE_PanelButtonCommand на PM_ButtonDefaultIndicator. CE_PushButtonLabel находится путём корректировки прямоугольника на PM_DefaultFrameWidth.
Теперь мы рассмотрим опцию стиля для кнопок — QStyleOptionButton. Далее приведена таблица состояний, которые QPushButton может установить в опции стиля:
| Состояние | Когда состояние устанавливается |
|---|---|
State_Sunken |
Кнопка нажата или показано нажатое меню |
State_On |
Кнопка отмечена |
State_Raised |
Кнопка не плоская и не нажата |
Другие члены QStyleOptionButton:
| Элемент | Содержание |
|---|---|
| features | Флаги перечисления QStyleOptionButton::ButtonFeatures, описывающие различные свойства кнопки (см. перечисление) |
| icon | Иконка кнопки QIcon (если есть) |
| iconSize | Размер QSize иконки |
| text | QString с текстом кнопки |
Кнопки "галочки" и радиокнопки
Структуры для радио- и "галочных" кнопок идентичны. Мы демонстрируем структуру на примере элемента QCheckBox и названий пиксельных метрик:
QStyleOptionButton используется в качестве стиля для обеих кнопок. Сначала приведём таблицу состояний, которые можно задать в опции:
| Состояние | Состояние устанавливается при |
|---|---|
State_sunken |
Кнопка нажата |
State_NoChange |
Кнопка частично отмечена (для кнопок с тремя состояниями.) |
State_On |
Кнопка отмечена |
State_Off |
Кнопка не отмечена |
См. Кнопки для таблицы других членов класса QStyleOptionButton.
Вкладки
В Qt, QTabBar использует стиль для отрисовки вкладок. Вкладки существуют либо в QTabWidget, который содержит QTabBar, либо как отдельный бар. Если бар не является частью виджета вкладок, он рисует свою собственную основу.
QTabBar выстраивает вкладки, поэтому стиль не контролирует расположение вкладок. Однако, выстраивая свои вкладки, бар запрашивает у стиля PM_TabBarTabHSpace и PM_TabBarTabVSpace, что представляет собой дополнительную ширину и высоту по отношению к минимальному размеру метки вкладки (иконки и текста). Стиль также может влиять на размер вкладки до ее выстраивания, так как таб бар запрашивает CT_TabBarTab. Граница прямоугольника бара определяется виджетом вкладок, когда он является частью виджета (при этом учитывается CT_TabBarTab).
Бар вкладок отвечает за отрисовку кнопок, которые появляются на баре вкладок, когда все вкладки не помещаются. Их расположение не контролируется стилем, но кнопки являются QToolButton и поэтому отрисовываются стилем.
Вот структура стиля для QTabWidget и QTabBar:
Пунктирные линии указывают, что QTabWidget содержит вкладку, но не рисует её сам. QTabBar рисует только свою базовую линию, когда не является частью виджета вкладок, и сохраняет две кнопки инструментов для прокрутки бара, когда все вкладки не помещаются; см. Кнопки инструментов для их структуры элементов. Также обратите внимание, что, поскольку кнопки являются дочерними элементами бара вкладок, они отрисовываются после бара. Границы прямоугольников вкладок перекрывают базу на PM_TabBarBaseOverlap.
Вот виджет вкладок в стиле Java:
В стиле Java форма и метка вкладки имеют ту же область прямоугольника, что и CE_TabBarTab. Обратите внимание, что вкладки перекрывают рамку виджета вкладок. Основание бара вкладок (если отрисовано) — это область, где вкладки и рамка перекрываются.
Опция стиля для вкладок (QStyleOptionTab) содержит необходимую информацию для отрисовки вкладок. Опция содержит положение вкладки в баре вкладок, положение выделенной вкладки, форму вкладки, текст, иконку и размер иконки.
Так как вкладки в стиле Java не перекрываются, мы также представим изображение виджета вкладок в общем стиле. Обратите внимание, что если вы хотите, чтобы вкладки перекрывались горизонтально, вы делаете это при отрисовке вкладок в CE_TabBarTabShape; прямоугольники границ вкладок не будут изменены баром вкладок. Вкладки отрисовываются слева направо в форме бара вкладок вверху, сверху вниз в форме бара вкладок справа и т. д. Выделенная вкладка отрисовывается последней, чтобы её легко было нарисовать поверх других вкладок (если она должна быть больше).
Ниже приводится таблица состояний, которые бар вкладок может устанавливать на своих вкладках:
| Состояние | Состояние устанавливается при |
|---|---|
State_Sunken |
Вкладка нажата мышью. |
State_Selected |
Если это текущая вкладка. |
State_HasFocus |
У бара вкладок фокус и вкладка выделена. |
Обратите внимание, что отдельные вкладки могут быть отключены, даже если сам бар вкладок не отключён. Вкладка будет активной, если бар вкладок активен.
Следующая таблица содержит члены QStyleOptionTab:
| Элемент | Содержание |
|---|---|
| cornerWidgets | Флаги перечисления CornerWidget, указывающие, есть ли и какие виджеты углов у бара вкладок. |
| icon | Иконка QIcon вкладки. |
| iconSize | Размер QSize иконки. |
| position | Значение перечисления TabPosition, указывающее положение вкладки на баре относительно других вкладок. |
| row | Указывает строку, в которой находится вкладка. |
| selectedPosition | Значение перечисления SelectedPosition, указывающее, расположена ли выделенная вкладка рядом с или является текущей вкладкой. |
| shape | Значение перечисления QTabBar::Shape, указывающее, имеют ли вкладки закругленные или треугольные углы, и ориентацию вкладки. |
| text | Текст вкладки. |
Рамка для виджетов вкладок использует QStyleOptionTabWidgetFrame в качестве опции стиля. Мы перечисляем здесь её члены. Она не имеет установленных состояний помимо общих флагов.
| Элемент | Содержание |
|---|---|
| leftCornerWidgetSize | Размер QSize виджета левого угла (если есть). |
| rightCornerWidgetSize | Размер QSize виджета правого угла (если есть). |
| lineWidth | Ширина линии для отрисовки панели. |
| midLineWith | Это значение всегда равно 0. |
| shape | Форма вкладок на баре вкладок. |
| tabBarSize | Размер QSize бара вкладок. |
Скроллбары
Вот структура стиля для скроллбаров:
QScrollBar просто создаёт свою опцию стиля, а затем рисует CC_ScrollBar. Некоторые стили рисуют фон страницы добавления и страницы удаления с помощью PE_PanelButtonBevel, а также используют индикаторные стрелки для отрисовки стрелок в индикаторах следующей и предыдущей строк; мы не включили их в дерево, так как их использование зависит от конкретного стиля. PM_MaximumDragDistance стиля — это максимальное расстояние в пикселях, на которое мышь может отойти от границ скроллбара и при этом перемещать ползунок.
Вот изображение скроллбара в стиле Java:
Вы можете заметить, что скроллбар немного отличается от скроллбара Java, так как он имеет два индикатора строк. Мы сделали это, чтобы показать, что у вас могут быть два отдельных прямоугольника границ для одного подэлемента. Скроллбар — это пример виджета, который полностью реализован стилем Java — QCommonStyle не участвует в отрисовке.
Давайте рассмотрим различные состояния, которые скроллбар может установить в опции стиля:
| Состояние | Состояние устанавливается при |
|---|---|
State_Horizontal |
Скроллбар горизонтальный. |
Опция стиля для QScrollBar — QStyleOptionSlider. Её члены перечислены в следующей таблице. Опция используется всеми QAbstractSlider; мы описываем здесь только члены, относящиеся к скроллбарам.
| Элемент | Содержание |
|---|---|
| maximum | Максимальное значение скроллбара. |
| minimum | Минимальное значение скроллбара. |
| notchTarget | Количество пикселей между рисками. |
| orientation | Значение перечисления Qt::Orientation, указывающее, вертикальный или горизонтальный скроллбар. |
| pageStep | Число, на которое увеличивается или уменьшается значение ползунка при шагах страницы (относительно размера ползунка и диапазона его значений). |
| singleStep | Число, на которое увеличивается или уменьшается значение ползунка при отдельных (или линейных) шагах. |
| sliderValue | Значение ползунка. |
| sliderPosition | Положение ползунка. Это то же, что и sliderValue если скроллбар QAbstractSlider::tracking. Если нет, скроллбар не обновляет своё значение до отпускания мыши. |
| upsideDown | Указывает направление, в котором скроллбар увеличивает своё значение. Используется вместо QStyleOption::direction для всех абстрактных ползунков. |
Ползунки
При расчёте размера подсказки ползунка запрашиваются PM_SliderThickness и PM_SliderLength у стиля. Как и с скроллбарами, QSlider позволяет пользователю перемещать ползунок только если мышь находится в пределах PM_MaximumDragDistance от границ ползунка. При отрисовке он создаёт опцию стиля и вызывает drawComplexControl() с CC_Slider:
Мы также покажем изображение ползунка в стиле Java. Мы покажем области прямоугольников подэлементов, так как вся отрисовка выполняется в CC_Slider.
QSlider использует QStyleOptionSlider, как и все QAbstractSliderы. Мы представляем таблицу с членами, которые влияют на QSlider:
| Член | Содержимое |
|---|---|
| maximum | Максимальное значение ползунка. |
| minimum | Минимальное значение ползунка. |
| notchTarget | Это количество пикселей между каждой риской. |
| orientation | Значение перечисления Qt::Orientation, указывающее, является ли ползунок вертикальным или горизонтальным. |
| pageStep | Число, на которое увеличивается или уменьшается значение ползунка при нажатии на кнопки "Страница". |
| singleStep | Число, на которое увеличивается или уменьшается значение ползунка при нажатии на кнопки "Шаг". |
| sliderValue | Значение ползунка. |
| sliderPosition | Положение ползунка, заданное как значение ползунка. Оно будет равно sliderValue если ползунок следит за курсором; если нет, значение ползунка не изменится до тех пор, пока обработчик не будет отпущен кнопкой мыши. |
| upsideDown | Этот член используется вместо QStyleOption::direction для всех абстрактных ползунков. |
Обратите внимание, что ползунок не использует направление для обратных макетов; он использует upsideDown.
Поле ввода с прокруткой
Когда QSpinBox отрисовывает себя, он создаёт QStyleOptionSpinBox и просит стиль нарисовать CC_SpinBox. Поле редактирования — это поле ввода, которое является дочерним элементом поля ввода с прокруткой. Размеры поля вычисляются стилем с помощью SC_SpinBoxEditField.
Ниже приведён древовидный вид стиля для полей ввода с прокруткой. Необязательно, чтобы стиль использовал примитив панели кнопок для отрисовки фонов индикаторов. Вы можете увидеть изображение ниже дерева, показывающее подэлементы в QSpinBox в стиле Java.
QStyleOptionSpinBox, это опция стиля для полей ввода с прокруткой. Она может установить следующие состояния на поле ввода с прокруткой:
| Состояние | Установка состояния |
|---|---|
State_Sunken |
Устанавливается, если на одну из подконтролей CC_SpinUp или CC_SpinDown нажата кнопка мыши. |
Остальные члены в опциях стиля поля ввода с прокруткой:
| Свойство | Функция |
|---|---|
| frame | Булево значение, true если нужно нарисовать рамку поля ввода с прокруткой. |
| buttonSymbols | Значение перечисления ButtonSymbols, определяющее символ на кнопках увеличения/уменьшения. |
| stepEnabled | Значение перечисления StepEnabled, указывающее, какие кнопки поля ввода с прокруткой нажаты. |
Панель заголовка
Сложная управляющая панель панели заголовка, CC_TitleBar, используется для отрисовки панелей заголовков внутренних окон в QMdiArea. Как правило, она состоит из заголовка окна и кнопок «Закрыть», «Свернуть», «Меню системы», «Развернуть». Некоторые стили также предоставляют кнопки для затемнения окна и кнопки для контекстной справки.
Панель отрисовывается в CC_TitleBar без использования каких-либо подэлементов. Как отдельные стили рисуют свои кнопки, зависит от них, но есть стандартные пиксельные изображения для кнопок, которые должен предоставить стиль.
На изображении над панелью заголовка в стиле Java мы показываем ограничивающие прямоугольники подэлементов, поддерживаемых стилем Java (все они отрисовываются с помощью стандартных пиксельных изображений). Обычно фоны кнопок рисуются с использованием PE_PanelButtonTool, но это не обязательно.
Опция стиля для панелей заголовков — QStyleOptionTitleBar. Её члены:
| Член | Содержимое |
|---|---|
| icon | Иконка панели заголовка. |
| text | Текст для метки панели заголовка. |
| windowFlags | Флаги перечисления Qt::WindowFlag. Флаги окна, используемые QMdiArea для управления окнами. |
| titleBarState | Это QWidget::windowState() окна, содержащего панель заголовка. |
Комбо-бокс
QComboBox использует стиль для отрисовки кнопки и метки нередактируемых элементов с использованием CC_ComboBox и CE_ComboBoxLabel.
Список, который появляется при нажатии на комбо-бокс, отрисовывается делегатом делегата, что не рассматривается в этом обзоре. Тем не менее, вы можете использовать стиль для управления размером и положением списка с помощью подэлемента SC_ComboBoxListBoxPopup. Стиль также определяет, где должно располагаться поле редактирования для редактируемых элементов с помощью SC_ComboBoxEditField; само поле является QLineEdit, которое является дочерним элементом комбо-бокса.
Комбо-боксы Java не используют прямоугольник фокуса; они изменяют цвет фона при фокусе. Поле SC_ComboBoxEdit используется как QComboBox для вычисления размера поля редактирования, так и стилем для вычисления размера метки комбо-бокса.
Опция стиля для комбо-боксов — QStyleOptionComboBox. Она может установить следующие состояния:
| Состояние | Установка состояния |
|---|---|
State_Selected |
Элемент не редактируемый и имеет фокус. |
State_Sunken |
SC_ComboBoxArrow активен. |
State_on |
Контейнер (список) элемента видимый. |
Другие члены опций стиля:
| Член | Содержимое |
|---|---|
| currentIcon | Иконка текущего (выбранного) элемента комбо-бокса. |
| currentText | Текст текущего элемента в комбо-боксе. |
| editable | Указывает, является ли комбо-бокс редактируемым. |
| frame | Указывает, есть ли рамка у комбо-бокса. |
| iconSize | Размер иконки текущего элемента. |
| popupRect | Ограничивающий прямоугольник всплывающего списка комбо-бокса. |
Групповые рамки
При вычислении размера подсказки QGroupBox запрашивает три значения пикселей у стиля: PM_IndicatorWidth, PM_CheckBoxLabelSpacing, и PM_IndicatorHeight. QGroupBox имеет следующий древовидный вид элемента стиля:
Qt не накладывает ограничений на то, как отрисовывается флажок; стиль Java рисует его с помощью CE_IndicatorCheckBox. См. Флажки и радиокнопки для полного дерева.
Мы также предоставляем изображение виджета с подконтролями и прямоугольниками подконтролей:
Опция стиля для групповых рамок — QStyleOptionGroupBox. Можно установить следующие состояния:
| Состояние | Установка состояния |
|---|---|
State_On |
Флажок установлен. |
State_Sunken |
Флажок нажат. |
State_Off |
Флажок не установлен (или флажка нет). |
Остальные члены QStyleOptionGroupBox:
| Член | Содержимое |
|---|---|
| features | Флаги перечисления QStyleOptionFrame::FrameFeatures, описывающие рамку групповой рамки. |
| lineWidth | Ширина линии для отрисовки панели. Она всегда равна 1. |
| text | Текст групповой рамки. |
| textAlignment | Выравнивание текста заголовка групповой рамки. |
| textColor | Цвет текста в формате QColor. |
Разделители
Поскольку структура разделителей проста и не содержит подэлементов, мы не включаем изображения разделителей. CE_Splitter не использует никаких других элементов или метрик.
Для своей опции стиля разделитель использует базовый класс QStyleOption. Он может установить следующие флаги состояния:
| Состояние | Установка состояния |
|---|---|
State_Horizontal |
Установлено, если это горизонтальный разделитель. |
QSplitter не использует initFrom() для настройки своей опции; он устанавливает флаги State_MouseOver и State_Disabled самостоятельно.
Полоса прогресса
Элемент CE_ProgressBar используется QProgressBar и является единственным элементом, используемым этим виджетом. Начинаем со структуры стиля:
Вот полоса прогресса в общем стиле (прямоугольники ограничивающих областей стиля Java совпадают):
Опция стиля для QProgressBar — QStyleOptionProgressBar. Полоса не устанавливает никаких флагов состояния, но другие члены опции:
| Элемент | Описание |
|---|---|
| minimum | Минимальное значение полосы. |
| maximum | Максимальное значение полосы. |
| progress | Текущее значение полосы. |
| textAlignment | Выравнивание текста в метке. |
| textVisible | Отображение метки. |
| text | Текст метки. |
| orientation | Полосы прогресса могут быть вертикальными или горизонтальными. |
| invertedAppearance | Инвертированное отображение прогресса (например, справа налево в горизонтальной полосе). |
| bottomToTop | Логическое значение, которое, если true, поворачивает метку вертикальных полос прогресса на 90 градусов. |
Кнопки инструментов
Кнопки инструментов существуют либо независимо, либо как часть панели инструментов. Их отрисовка одинакова в обоих случаях. QToolButton рисует только один элемент стиля: CC_ToolButton.
Ниже представлено дерево структуры стиля виджета:
Обратите внимание, что PE_FrameButtonTool и PE_IndicatorArrowDown включены в дерево, так как Java стиль их рисует, но их можно безопасно опустить, если это предпочтительнее. Структура также может отличаться. QCommonStyle, например, рисует как PE_IndicatorButtonDropDown, так и PE_IndicatorArrowDown в CE_ToolButton.
У нас также есть изображение кнопки инструмента, где мы обвели прямоугольники границ подэлементов и подэлементы управления.
Вот таблица состояний для кнопок инструментов:
| Состояние | Условие |
|---|---|
State_AutoRise |
Кнопка инструмента имеет свойство autoRise. |
State_Raised |
Кнопка не утоплена (например, не отмечена или не нажата мышкой). |
State_Sunken |
Кнопка нажата. |
State_On |
Кнопка переключаемая и отмечена. |
QStyleOptionToolButton также содержит следующие элементы:
| Элемент | Описание |
|---|---|
| arrowType | Значение перечисления Qt::ArrowType, содержащее направление стрелки кнопки (если стрелка используется вместо значка). |
| features | Флаги перечисления QStyleOptionToolButton::ButtonFeature, описывающие, имеет ли кнопка стрелку, меню и/или задержку всплытия. |
| font | QFont метки кнопки. |
| icon | QIcon кнопки инструмента. |
| iconSize | Размер значка кнопки. |
| pos | Позиция кнопки, заданная QWidget::pos() |
| text | Текст кнопки. |
| toolButtonStyle | Значение перечисления Qt::ToolButtonStyle, определяющее, отображает ли кнопка значок, текст или оба. |
Панели инструментов
Панели инструментов являются частью фреймворка главного окна и взаимодействуют с QMainWindow, к которому они принадлежат, во время построения его опций стиля. Главное окно имеет 4 области, в которых могут размещаться панели инструментов. Они размещаются рядом с четырьмя сторонами окна (т.е., север, юг, восток и запад). В каждой области может быть более одной строки панелей инструментов; строка состоит из панелей инструментов с одинаковой ориентацией (вертикальной или горизонтальной), расположенных рядом друг с другом.
Панели инструментов в Qt состоят из трех элементов: CE_ToolBar, PE_IndicatorToolBarHandle, и PE_IndicatorToolBarSeparator. Это QMainWindowLayout, который рассчитывает ограничивающие прямоугольники (т.е., позицию и размер панелей инструментов и их содержимого. Главное окно также использует sizeHint() элементов в панелях инструментов при расчете размера панелей.
Вот дерево элементов для QToolBar:
Пунктирные линии указывают, что QToolBar хранит экземпляр QToolBarLayout, а QToolBarSeparators хранятся QToolBarLayout. Когда панель инструментов плавающая (т.е., имеет собственное окно), рисуется элемент PE_FrameMenu, в противном случае QToolBar рисует CE_ToolBar.
Вот изображение панели инструментов в стиле Java:
QToolBarSaparator использует QStyleOption для своей опции стиля. Он устанавливает флаг State_Horizontal если панель инструментов горизонтальна. В остальном они используют initFrom().
Опция стиля для QToolBar — это QStyleOptionToolBar. Единственный флаг состояния (кроме общих флагов) — State_Horizontal если панель горизонтальна (т.е., в области панели инструментов север или юг). Переменные-члены опции стиля:
| Элемент | Описание |
|---|---|
| features | Содержит значение, указывающее, перемещаема ли панель в перечислении ToolBarFeature, которое может быть Movable или None. |
| lineWidth | Ширина рамки панели инструментов. |
| midLineWidth | Эта переменная в настоящее время не используется и всегда равна 0. |
| positionOfLine | Позиция линии панели инструментов в области панели инструментов, к которой она принадлежит. |
| positionWithinLine | Позиция панели инструментов в строке панели инструментов. |
| toolBarArea | Область панели инструментов, в которой находится панель инструментов. |
Меню
Меню в Qt реализованы в QMenu. QMenu хранит список действий, которые отображаются как пункты меню. Когда QMenu получает события рисования, он рассчитывает размер каждого пункта меню и рисует их индивидуально с помощью CE_MenuItem. Пункты меню не имеют отдельного элемента для метки (содержимого), поэтому вся отрисовка выполняется в CE_MenuItem. Меню также рисует рамку меню с помощью PE_FrameMenu. Также рисует CE_MenuScroller, если стиль поддерживает прокрутку. CE_MenuTearOff рисуется, если меню слишком велико для своего ограничивающего прямоугольника.
В дереве структуры стиля мы также включаем QMenu, так как он также выполняет работу по стилизации. Ограничивающие прямоугольники пунктов меню рассчитываются для размера подсказки меню и при отображении или изменении размера меню.
Элементы CE_MenuScroller и CE_MenuTearOff обрабатываются QCommonStyle и не отображаются, если меню слишком велико для экрана. PE_FrameMenu рисуется только для всплывающих меню.
QMenu рассчитывает прямоугольники на основе своих действий и вызывает CE_MenuItem и CE_MenuScroller если стиль это поддерживает.
Также обычно использовать PE_IndicatorCheckBox (вместо PE_IndicatorMenuCheckMark) и PE_IndicatorRadioButton для рисования переключаемых пунктов меню; мы не включили их в дерево стиля, так как это необязательно и варьируется от стиля к стилю.
Опция стиля для элементов меню — QStyleOptionMenuItem. Следующие таблицы описывают флаги состояния и другие элементы.
| Состояние | Условие |
|---|---|
State_Selected |
Курсор над действием, и это действие не разделитель. |
State_Sunken |
Нажата кнопка мыши на элементе меню. |
State_DownArrow |
Устанавливается, если пункт меню — элемент прокрутки меню и он прокручивает меню вниз. |
| Элемент | Описание |
|---|---|
| checkType | Значение перечисления CheckType, которое может быть NotCheckable, Exclusive или NonExclusive. |
| checked | Логическое значение, равное true если элемент меню отмечен. |
| font | QFont для использования в тексте элемента меню. |
| icon | QIcon элемента меню. |
| maxIconWidth | Максимальная ширина, разрешенная для значка. |
| menuHasChecableItem | Логическое значение, равное true если хотя бы один элемент в меню переключаемый. |
| menuItemType | Тип элемента меню. Это значение перечисления MenuItemType. |
| menuRect | Ограничивающий прямоугольник QMenu, в котором находится элемент меню. |
| tabWidth | Расстояние между текстом элемента меню и сокращением. |
| text | Текст элемента меню. |
Настройка опции стиля для CE_MenuTearOff и CE_MenuScroller также использует QStyleOptionMenuItem; они устанавливают только переменную menuRect в дополнение к общим настройкам с initFrom() QStyleOption.
Панель меню
QMenuBar использует стиль для рисования каждого элемента панели меню и пустой области панели меню. Всплывающие меню сами по себе являются QMenu (см. Меню). Дерево элементов стиля для панели меню следующее:
Панель и пустая область рисуются после пунктов меню. QPainter, который QMenuBar отправляет стилю, имеет ограничивающие прямоугольники элементов, вырезанные (т. е., область обрезки), поэтому вам не нужно беспокоиться о рисовании поверх элементов. Метрики пикселей в QMenuBar используются при вычислении ограничивающих прямоугольников элементов меню.
QStyleOptionMenuItem используется для элементов панели меню. Члены, используемые QMenuBar, описаны в следующей таблице:
| Член | Содержимое |
|---|---|
| menuRect | Ограничивающий прямоугольник всего меню, к которому принадлежит элемент. |
| text | Текст элемента. |
| icon | Иконка элемента меню (нечасто стили рисуют эту иконку). |
QStyleOptionMenuItem также используется для рисования CE_EmptyMenuBarArea.
QStyleOptionFrame используется для рисования рамки панели. lineWidth устанавливается в PM_MenuBarPanelWidth. midLineWidth в настоящее время всегда устанавливается в 0.
Заголовки элементов
Стиль рисует заголовки элементов представлений Qt. Представления элементов сохраняют размеры отдельных разделов. Также обратите внимание, что делегаты могут использовать стиль для рисования декораций и рамок вокруг элементов. QItemDelegate, например, рисует PE_FrameFocusRect и PE_IndicatorItemViewItemCheck.
Вот QTableWidget, показывающий ограничивающие прямоугольники заголовка Java:
QHeaderView использует CT_HeaderSection, PM_HeaderMargin и PM_HeaderGripMargin для вычислений размера и попадания в область. PM_HeaderMarkSize в настоящее время не используется Qt. QTableView рисует кнопку в верхнем левом углу (т. е., в области пересечения вертикального и горизонтального заголовков) как CE_Header.
Стиль-опция для заголовков представлений — QStyleOptionHeader. Представление рисует один раздел заголовка за раз, поэтому данные относятся к разделу, который рисуется. Его содержимое:
| Член | Содержимое |
|---|---|
| icon | Иконка заголовка (для раздела, который рисуется). |
| iconAlignment | Выравнивание (Qt::Alignment) иконки в заголовке. |
| orientation | Значение Qt::Orientation, определяющее, является ли заголовок горизонтальным заголовком над представлением или вертикальным заголовком слева. |
| position | Значение QStyleOptionHeader::SectionPosition, указывающее положение раздела заголовка относительно других разделов. |
| section | Содержит раздел, который рисуется. |
| selectedPosition | Значение QStyleOptionHeader::SelectedPosition, указывающее положение выделенного раздела относительно раздела, который рисуется. |
| sortIndicator | Значение QStyleOptionHeader::SortIndicator, описывающее направление, в котором следует рисовать индикатор сортировки раздела. |
| text | Текст текущего раздела. |
| textAlignment | Выравнивание Qt::Alignment текста в разделе заголовка. |
Индикаторы ветвей дерева
Индикаторы ветвей в представлении дерева рисуются стилем с помощью PE_IndicatorBranch. Мы рассматриваем индикаторы здесь как индикаторы, описывающие отношения узлов в дереве. Для рисования этих элементов в стиль отправляется общий QStyleOption. Различные типы ветвей описываются состояниями. Поскольку нет специальной опции стиля, мы просто представим таблицу состояний:
| Состояние | Устанавливается, когда |
|---|---|
State_Sibling |
Узел в дереве имеет брата (т. е., существует другой узел в том же столбце). |
State_Sibling |
Узел в дереве имеет брата (т. е., существует другой узел в том же столбце). |
State_Item |
Этот индикатор ветви имеет элемент. |
State_Children |
Ветка имеет потомков (т. е., поддерево может быть открыто в ветке). |
State_Open |
Индикатор ветви имеет открытое поддерево. |
Представление дерева (и виджет дерева) использует стиль для рисования ветвей (узлов) дерева.
QStyleOption используется как стиль для PE_IndicatorBranch с установками флагов состояния в зависимости от того, какой ветвью она является.
Поскольку для индикаторов ветвей нет структуры дерева, мы представляем только изображение дерева в стиле Java. Каждое состояние отмечено на изображении прямоугольником определенного цвета (т. е., прямоугольники не являются ограничивающими прямоугольниками). Все комбинации состояний, о которых необходимо знать, представлены на изображении.
Панели инструментов
PM_SmallIconSize для подсказок размера.
QToolBox — это контейнер, который хранит набор виджетов. У него есть одна вкладка для каждого виджета, и он отображает один из них за раз. Панель инструментов выкладывает отображаемые компоненты (кнопки панели инструментов и выбранный виджет) в QVBoxLayout.
Мы показываем изображение панели инструментов в стиле Plastique:
Все элементы имеют одинаковые ограничивающие прямоугольники в стиле Plastique, а также в других встроенных стилях Qt.
Опция стиля для панелей инструментов — QStyleOptionToolBox. Она содержит текст и иконку содержимого панели инструментов. Единственное состояние, устанавливаемое QToolBox, — State_Sunken, которое устанавливается, когда пользователь нажимает вкладку мышью. Остальные члены QStyleOptionToolBox:
| Член | Содержимое |
|---|---|
| icon | Иконка на вкладке панели инструментов. |
| text | Текст на вкладке панели инструментов. |
Маркер размера
Маркер размера вычисляет свою подсказку размера с помощью CT_SizeGrip. Метрика пикселей PM_SizeGripSize в настоящее время не используется Qt. Дерево элементов для изображения в стиле Plastique QSizeGrip имеет следующий вид:
Мы показываем маркер размера в правом нижнем углу QMainWindow.
Опция стиля маркера размера, QStyleOptionSizeGrip, помимо общих членов из QStyleOption имеет один член:
| Член | Содержимое |
|---|---|
| corner | Значение Qt::Corner, описывающее угол окна (или эквивалента), в котором расположен маркер. |
Резиновая полоса
Дерево стиля QRubberBand состоит из двух узлов.
Мы представляем изображение перемещения окна в стиле Java в QMdiArea с резиновой полосой:
Опция стиля для резиновых полос — QStyleOptionRubberBand. Ее члены:
| Член | Содержимое |
|---|---|
| opaque | Булево значение, которое true в том случае, если резиновая полоса должна быть нарисована непрозрачным стилем (т. е., цветом). |
| shape | Значение перечисления QRubberBand::Shape, которое содержит форму полосы (прямоугольник или линия). |
Виджеты доков
При разметке содержимого виджета доков он запрашивает у стиля эти метрики пикселей: PM_DockWidgetSeparatorExtent, PM_DockWidgetTitleBarButtonMargin, PM_DockWidgetFrameWidth, и PM_DockWidgetTitleMargin. Он также вычисляет ограничивающие прямоугольники кнопок плавания и закрытия с помощью SE_DockWidgetCloseButton и SE_DockWidgetFloatButton.
Пунктирные линии указывают, что отправитель сохраняет экземпляры получателя стрелки (т. е., это не элемент стиля для рисования). Виджет доков рисует PE_frameDockWidget только тогда, когда он отделен от основного окна (т. е., это окно верхнего уровня). Если он прикреплен, он рисует индикатор ручки изменения размера виджета дока. Мы показываем виджет дока в прикрепленном и плавающем состоянии в стиле plastique:
Опция стиля — QStyleOptionDockWidget:
| Член | Содержимое |
|---|---|
| closeable | Булево значение, которое указывает, может ли окно дока быть закрыто. |
| floatable | Булево значение, которое указывает, может ли окно дока плавать (т. е., отделяться от основного окна). |
| movable | Булево значение, которое указывает, может ли окно перемещаться (т. е., перемещаться в другие области виджетов доков). |
| title | Текст заголовка окна дока. |
Для кнопок используется QStyleOptionButton (см. Кнопки инструментов для описания содержимого). Ручка изменения размера виджета дока имеет обычный QStyleOption.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.2/style-reference.html