Spec-Zone.ru › Qt 5.12

Стили и стилизованные виджеты

Стили (классы, наследующие QStyle) выполняют отрисовку от имени виджетов и инкапсулируют внешний вид графического интерфейса. Класс QStyle — это абстрактный базовый класс, который инкапсулирует внешний вид графического интерфейса. Встроенные виджеты Qt используют его для почти всей отрисовки, гарантируя, что они выглядят точно так же, как и аналогичные виджеты нативной платформы.

Qt поставляется с набором встроенных стилей. Некоторые стили доступны только на определённых платформах. Пользовательские стили доступны как плагины или создаются путём создания экземпляра конкретного класса стиля с помощью QStyleFactory::create() и установки его с помощью QApplication::setStyle().

Настройка стиля

Для настройки существующего стиля необходимо унаследовать от QProxyStyle и переопределить необходимые виртуальные методы. QProxyStyle позволяет указать базовый стиль; в противном случае он автоматически использует стиль приложения. Первый вариант даёт полный контроль над базовым стилем и лучше всего подходит, если настройка ожидает определённое поведение стиля, а второй — это платформенно-независимый способ настройки стиля приложения, по умолчанию использующий стиль нативной платформы.

Реализация пользовательского стиля

QCommonStyle предоставляет удобную базу для полной реализации пользовательского стиля. Подход такой же, как и с QProxyStyle, но необходимо унаследовать от QCommonStyle и переопределить соответствующие виртуальные методы. Реализация полностью пользовательского стиля несколько сложна, поэтому мы предоставляем этот обзор. Мы покажем пошаговую инструкцию по стилизации отдельных виджетов Qt. Мы рассмотрим виртуальные функции, переменные-члены и перечисления класса QStyle.

Часть этого документа, не относящаяся к стилизации отдельных виджетов, должна читаться последовательно, поскольку последующие разделы часто зависят от предыдущих. Описание виджетов может использоваться в качестве справочника при реализации стиля. Однако в некоторых случаях может потребоваться обратиться к исходному коду Qt. После прочтения этого документа последовательность процесса стилизации станет ясной, что поможет вам найти соответствующий код.

Для разработки стилизованных виджетов (т.е. виджетов, соответствующих стилю, в котором они отображаются), необходимо рисовать их с использованием текущего стиля. В этом документе показано, как виджеты рисуют себя и какие возможности им предоставляет стиль.

Классы для стилизации виджетов

Эти классы используются для настройки внешнего вида и стиля приложения.

QColor

Цвета на основе значений RGB, HSV или CMYK

QCommonStyle

Инкапсулирует общий внешний вид графического интерфейса

QCursor

Курсор мыши с произвольной формой

QFont

Устанавливает шрифт, используемый для отображения текста

QFontDatabase

Информация о доступных шрифтах в базовой системе окон

QFontInfo

Общая информация о шрифтах

QGraphicsAnchor

Представляет якорь между двумя элементами в QGraphicsAnchorLayout

QGraphicsAnchorLayout

Макет, в котором можно привязать виджеты друг к другу в представлении Graphics View

QPalette

Содержит группы цветов для каждого состояния виджета

QStyle

Абстрактный базовый класс, инкапсулирующий внешний вид графического интерфейса

QStyleFactory

Создаёт объекты QStyle

QStyleHintReturn

Свойства стиля, возвращающие больше, чем основные типы данных

QStyleHintReturnMask

Свойства стиля, возвращающие QRegion

QStyleHintReturnVariant

Свойства стиля, возвращающие QVariant

QStyleOption

Хранит параметры, используемые функциями QStyle

QStylePainter

Удобный класс для отрисовки элементов 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() возвращает 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();
    ~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. Деревовидный виджет является примером последнего, так как в JTree Java нет заголовка.

Стиль не обрабатывает обратные макеты. Мы предполагаем, что направление макета слева направо. 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;
#if 0 // Used to be included in Qt4 for Q_WS_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. Обратите внимание, что навигация с помощью клавиатуры должна быть включена в Qt, чтобы State_HasEditFocus был включён; он не включён по умолчанию.

Помимо установки флагов состояния, QStyleOption содержит другую информацию о виджете: direction - направление макета, rect - ограничивающая прямоугольник виджета (область, в которой необходимо рисовать), palette - QPalette, которая должна использоваться для отрисовки виджета, и fontMetrics - метрики шрифта, используемого виджетом.

Мы приводим изображение флажка и соответствующий ему вариант стиля.

A Java style checkbox

У указанного выше флажка в варианте стиля будут следующие флаги состояния:

Флаг состояния Установлен
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 центрирует содержимое кнопки и при наличии значка. Вы можете рассмотреть эту реализацию, если вам нужен пример повторной реализации отрисовки метки.

Теперь мы рассмотрим реализацию CE_CheckBoxIndicator в стиле Java в 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. Они указывают, из каких подэлементов управления состоит сложное управление и какие из этих элементов управления в настоящее время активны.

Как уже упоминалось, стиль вычисляет размер содержимого виджета, от которого виджеты вычисляют свои подсказки размера. Кроме того, сложные элементы управления также используют стиль для проверки, над каким подэлементом управления находится курсор мыши.

Справочник по виджетам

Без дальнейших задержек мы представляем обзор виджетов; у каждого виджета есть свой подраздел.

Кнопки

Структура стиля для кнопок показана ниже. Выполняя обход дерева сверху вниз, вы получаете последовательность, в которой должны быть нарисованы элементы.

The style structure for push buttons

Расположение кнопок, относительно границ элементов, варьируется в зависимости от стиля. Это затрудняет демонстрацию концептуальных изображений. Кроме того, элементы могут — даже должны — иметь одинаковые границы; 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. Ее члены перечислены в следующей таблице. Параметр используется всеми QAbstractSliders; здесь мы описываем только члены, относящиеся к полосам прокрутки.

Член Содержимое
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, как и все QAbstractSliders. Мы представляем таблицу с членами, которые влияют на 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 Булево значение, устанавливающее, будет ли отрисоваться рамка поля ввода со списком.
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 в дополнение к общим настройкам с 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_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-5.12/style-reference.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API