Стили и стилизованные виджеты
Стили (классы, наследующие QStyle) рисуют от имени виджетов и инкапсулируют внешний вид графического интерфейса пользователя. Класс QStyle — это абстрактный базовый класс, инкапсулирующий внешний вид графического интерфейса пользователя. Встроенные виджеты Qt используют его для выполнения почти всех своих операций рисования, гарантируя, что они выглядят точно так же, как эквивалентные родные виджеты.
Qt поставляется со множеством встроенных стилей. Некоторые стили доступны только на определённых платформах (таких как Mac, GTK+ и Windows Vista). Пользовательские стили предоставляются в качестве плагинов или путём создания экземпляра определённого класса стиля с помощью 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, содержащий QBrush для рисования.
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() возвращает 0. Например:
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();
~CustomStyle() {}
void drawPrimitive(PrimitiveElement element, const QStyleOption *option,
QPainter *painter, const QWidget *widget) const;
};
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(). Мы рассмотрим это вскоре.
Переменная down из QStyleOption равна 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 Q_DEAD_CODE_FROM_QT4_MAC
extern bool qt_mac_can_clickThrough(const QWidget *w); //qwidget_mac.cpp
if (!(state & QStyle::State_Active) && !qt_mac_can_clickThrough(widget))
state &= ~QStyle::State_Enabled;
#endif
#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 находится, путём корректировки rect на 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 |
Кнопка имеет свойство checkable и выбрана. |
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 в дополнение к общим настройкам с QStyleOption's initFrom().
Панель меню
QMenuBar использует стиль для рисования каждого элемента панели меню и пустой области панели меню. Всплывающие меню сами являются QMenu (см. Меню). Дерево элементов стиля для панели меню приведено ниже:
Панель и пустая область рисуются после пунктов меню. QPainter, который QMenuBar отправляет стилю, имеет ограничительные прямоугольники элементов, вырезанные (т. е. область обрезки), поэтому вам не нужно беспокоиться о рисовании поверх элементов. Метрики пикселей в QMenuBar используются при вычислении ограничительных прямоугольников элементов меню.
QStyleOptionMenuItem используется для элементов меню. Члены, которые используются QMenuBar, описаны в следующей таблице:
| Член | Содержание |
|---|---|
| menuRect | Ограничительный прямоугольник всего меню, к которому принадлежит элемент. |
| text | Текст элемента. |
| icon | Иконка пункта меню (нечасто стили рисуют эту иконку). |
QStyleOptionMenuItem также используется для рисования CE_EmptyMenuBarArea.
QStyleOptionFrame используется для рисования рамки панели. lineWidth устанавливается в PM_MenuBarPanelWidth. midLineWidth в настоящее время всегда устанавливается в 0.
Заголовки представлений элементов
Стиль рисует заголовки представлений элементов Qt. Представления элементов сохраняют размеры отдельных секций. Обратите также внимание, что делегаты могут использовать стиль для рисования декораций и рамок вокруг элементов. Например, QItemDelegate рисует PE_FrameFocusRect и PE_IndicatorViewItemCheck.
Вот 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_Item |
Этот индикатор ветви имеет элемент. |
State_Children |
Ветвь имеет дочерние элементы (т. е. в ветви можно открыть новое поддерево). |
State_Open |
У индикатора ветви открыто поддерево. |
Представление дерева (и виджет дерева) использует стиль для рисования ветвей (узлов) дерева.
QStyleOption используется в качестве стиля для PE_IndicatorBranch, у которого установлены флаги состояния в зависимости от типа ветви.
Поскольку для индикаторов ветвей нет структуры дерева, мы представляем только изображение дерева в стиле Java. Каждое состояние на изображении отмечено прямоугольником определенного цвета (т. е. прямоугольники не являются ограничивающими прямоугольниками). На изображении представлены все комбинации состояний, о которых необходимо знать.
Панели инструментов
PM_SmallIconSize для sizeHints.
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_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/archives/qt-5.6/style-reference.html