Spec-Zone.ru › Qt 5.13

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

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

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

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

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

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

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

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

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

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

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

QCursor

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

QPalette

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

QColor

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

QFont

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

QFontDatabase

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

QFontInfo

Общие сведения о шрифтах

QGraphicsAnchor

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

QGraphicsAnchorLayout

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

QCommonStyle

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

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. Деревовидный виджет является примером последнего, в котором у 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;
#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 окон. Обратите внимание, что для включения State_HasEditFocus в Qt необходимо включить навигацию с помощью клавиатуры; она не включена по умолчанию.

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

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

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

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

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

Кнопки

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

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 находится путем корректировки прямоугольника на 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 Ящик не выбран

См. Кнопки для таблицы других элементов в QStyleOptionButtonClass.

Вкладки

В 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 Булево значение, равное true , если поле ввода со списком должно нарисовать рамку.
buttonSymbols Значение перечисления ButtonSymbols, определяющее символ на кнопках вверх/вниз.
stepEnabled Значение перечисления StepEnabled, указывающее, какие кнопки поля ввода со списком нажаты.

Панель заголовка

Комплексный элемент управления панелью заголовка, CC_TitleBar, используется для отрисовки панелей заголовков внутренних окон в QMdiArea. Обычно она состоит из заголовка окна, а также кнопок закрытия, сворачивания, системного меню и разворачивания. Некоторые стили также предоставляют кнопки для затемнения окна, а также кнопку для контекстно-зависимой справки.

Панель рисуется в CC_TitleBar без использования каких-либо подэлементов. Способ, которым отдельные стили рисуют свои кнопки, зависит от них, но для кнопок существуют стандартные пиксельные изображения, которые стиль должен предоставить.

На изображении выше панели заголовка в стиле Java мы показываем ограничивающие прямоугольники подэлементов, поддерживаемые стилем Java (все из которых отрисовываются с помощью стандартных пиксельных изображений). Обычно фоны кнопок рисуются с использованием PE_PanelButtonTool, но это необязательно.

Опция стиля для панелей заголовков — QStyleOptionTitleBar. Ее члены:

Член Содержание
icon Иконка панели заголовка.
text Текст для метки панели заголовка.
windowFlags Флаги перечисления Qt::WindowFlag. Флаги окна, используемые QMdiArea для управления окнами.
titleBarState Это QWidget::windowState() окна, содержащего панель заголовка.

Комбо-бокс

QComboBox использует стиль для отрисовки кнопки и метки нередактируемых полей с помощью CC_ComboBox и CE_ComboBoxLabel.

Список, который всплывает, когда пользователь щелкает по комбо-боксу, отрисовывается делегатом делегат, который мы не рассматриваем в этом обзоре. Однако вы можете использовать стиль для управления размером и позицией списка с помощью подэлемента SC_ComboBoxListBoxPopup. Стиль также определяет, где должно быть поле редактирования для редактируемых полей с помощью SC_ComboBoxEditField; само поле — это QLineEdit, которое является дочерним элементом комбо-бокса.

Комбо-боксы Java не используют прямоугольник фокуса; он меняет свой цвет фона при получении фокуса. Поле SC_ComboBoxEdit используется как QComboBox для расчёта размера поля редактирования, так и стилем для расчёта размера метки комбо-бокса.

Опция стиля для комбо-боксов — QStyleOptionComboBox. Она может устанавливать следующие состояния:

Состояние Установка, когда
State_Selected Поле не редактируется и имеет фокус.
State_Sunken SC_ComboBoxArrow активен.
State_on Контейнер (список) поля виден.

Остальные члены опций стиля:

Член Содержание
currentIcon Иконка текущего (выбранного) элемента комбо-бокса.
currentText Текст текущего элемента в поле.
editable Содержит информацию о том, редактируется ли комбо-бокс.
frame Содержит информацию о наличии рамки у комбо-бокса.
iconSize Размер значка текущего элемента.
popupRect Ограничивающий прямоугольник всплывающего списка комбо-бокса.

Групповые рамки

При расчёте размера подсказки QGroupBox получает три значения пикселей из стиля: PM_IndicatorWidth, PM_CheckBoxLabelSpacing, и PM_IndicatorHeight. Дерево элементов стиля для QGroupBox:

Qt не накладывает ограничений на способ отрисовки флажка; стиль Java рисует его с помощью CE_IndicatorCheckBox. См. Флажки и радиокнопки для полного дерева.

Мы также приводим изображение виджета с отрисованными подэлементами и ограничивающими прямоугольниками:

Опция стиля для групповых рамок — QStyleOptionGroupBox. На ней могут быть установлены следующие состояния:

Состояние Установка, когда
State_On Флажок установлен.
State_Sunken Флажок нажат.
State_Off Флажок снят (или флажка нет).

Остальные члены QStyleOptionGroupBox:

Член Содержание
features Флаги перечисления QStyleOptionFrame::FrameFeatures, описывающие рамку групповой рамки.
lineWidth Ширина линии для отрисовки панели. Всегда 1.
text Текст групповой рамки.
textAlignment Выравнивание заголовка групповой рамки.
textColor Цвет текста QColor.

Разделители

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

Для своей опции стиля разделитель использует базовый класс QStyleOption. Он может устанавливать следующие флаги состояния:

Состояние Установка, когда
State_Horizontal Устанавливается, если это горизонтальный разделитель.

QSplitter не использует initFrom() для настройки своей опции; он устанавливает флаги State_MouseOver и State_Disabled самостоятельно.

Полоса прогресса

Элемент CE_ProgressBar используется QProgressBar, и это единственный используемый элемент для этого виджета. Мы начинаем со структуры стиля:

Вот полоса прогресса в общем стиле (прямоугольники в стиле Java равны):

Опция стиля для QProgressBar — QStyleOptionProgressBar. Полоса не устанавливает никаких флагов состояния, но другие члены опции:

Элемент Описание
minimum Минимальное значение шкалы.
maximum Максимальное значение шкалы.
progress Текущее значение шкалы.
textAlignment Выравнивание текста в метке.
textVisible Отображение метки.
text Текст метки.
orientation Тип шкалы: вертикальная или горизонтальная.
invertedAppearance Инвертированный порядок прогресса (например, справа налево в горизонтальной шкале).
bottomToTop Логическое значение, которое, если true, поворачивает метку вертикальной шкалы на 90 градусов.

Кнопки инструментов

Кнопки инструментов могут существовать независимо или как часть панелей инструментов. Отрисовка в обоих случаях одинаковая. QToolButton отображает только один элемент стиля: CC_ToolButton.

Ниже приведена структура дерева виджета:

Обратите внимание, что PE_FrameButtonTool и PE_IndicatorArrowDown включены в дерево, так как Java-стиль их отображает, но их можно безопасно опустить, если вы этого предпочитаете. Структура также может отличаться. Например, QCommonStyle отображает как PE_IndicatorButtonDropDown, так и PE_IndicatorArrowDown в CE_ToolButton.

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

Вот таблица состояний для кнопок инструментов:

Состояние Условие
State_AutoRise Кнопка инструмента имеет свойство autoRise.
State_Raised Кнопка не углублена (например, не выбрана и не нажата мышью).
State_Sunken Кнопка нажата.
State_On Кнопка может быть выбрана и выбрана.

QStyleOptionToolButton также содержит следующие элементы:

Элемент Описание
arrowType Значение перечисления Qt::ArrowType, содержащее направление стрелки кнопки (если стрелка используется вместо значка).
features Флаги перечисления QStyleOptionToolButton::ButtonFeature, описывающие, имеет ли кнопка стрелку, меню и/или задержку всплывающего окна.
font Шрифт QFont метки кнопки.
icon Значок QIcon кнопки инструмента.
iconSize Размер значка кнопки.
pos Положение кнопки, заданное QWidget::pos()
text Текст кнопки.
toolButtonStyle Значение перечисления Qt::ToolButtonStyle, которое определяет, отображает ли кнопка значок, текст или оба.

Панели инструментов

Панели инструментов являются частью фреймворка главного окна и взаимодействуют с QMainWindow, к которому они принадлежат, во время построения его опции стиля. Главное окно имеет 4 области, в которых могут располагаться панели инструментов. Они размещаются рядом со всеми четырьмя сторонами окна (т. е. север, юг, восток и запад). В каждой области может быть более одной строки панелей инструментов; строка состоит из панелей инструментов с одинаковой ориентацией (вертикальной или горизонтальной), расположенных рядом друг с другом.

Панели инструментов в Qt состоят из трех элементов: CE_ToolBar, PE_IndicatorToolBarHandle, и PE_IndicatorToolBarSeparator. Именно QMainWindowLayout рассчитывает ограничительные прямоугольники (т. е. положение и размер панелей инструментов и их содержимого. Главное окно также использует sizeHint() элементов в панелях инструментов при расчете размера панелей.

Вот дерево элементов для QToolBar:

Пунктирные линии указывают, что QToolBar сохраняет экземпляр QToolBarLayout, а QToolBarSeparators сохраняются QToolBarLayout. Когда панель инструментов плавающая (т. е. имеет собственное окно), отображается элемент PE_FrameMenu, иначе QToolBar отображает CE_ToolBar.

Вот изображение панели инструментов в стиле Java:

QToolBarSaparator использует QStyleOption для своей опции стиля. Он устанавливает флаг State_Horizontal если панель инструментов горизонтальная. Помимо этого, они используют initFrom().

Опция стиля для QToolBar — QStyleOptionToolBar. Единственный флаг состояния, установленный (кроме общих флагов), — State_Horizontal если панель горизонтальная (т. е. в области панели инструментов на севере или юге). Переменные-члены опции стиля:

Элемент Описание
features Указывает, может ли панель инструментов перемещаться (значение ToolBarFeature: Movable или None).
lineWidth Ширина рамки панели инструментов.
midLineWidth В настоящее время эта переменная не используется и всегда равна 0.
positionOfLine Положение линии панели инструментов в области панели инструментов, к которой она принадлежит.
positionWithinLine Положение панели инструментов в строке панелей инструментов.
toolBarArea Область панели инструментов, в которой находится панель инструментов.

Меню

Меню в Qt реализованы в QMenu. QMenu сохраняет список действий, которые он отображает как пункты меню. При получении событий рисования QMenu рассчитывает размер каждого пункта меню и отображает их индивидуально с помощью CE_MenuItem. Пункты меню не имеют отдельного элемента для своей метки (содержимого), поэтому все рисование выполняется в CE_MenuItem. Меню также отображает рамку меню с помощью PE_FrameMenu. Он также отображает CE_MenuScroller если стиль поддерживает прокрутку. CE_MenuTearOff отображается, если меню слишком велико для его ограничительного прямоугольника.

В дереве структуры стиля также содержится QMenu, так как он также выполняет работу, связанную со стилем. Ограничительные прямоугольники пунктов меню рассчитываются для размера подсказки меню и при отображении или изменении размера меню.

Элементы CE_MenuScroller и CE_MenuTearOff обрабатываются QCommonStyle и не отображаются, если меню слишком велико для экрана. PE_FrameMenu отображается только для всплывающих меню.

QMenu рассчитывает прямоугольники на основе своих действий и вызывает CE_MenuItem и CE_MenuScroller, если стиль это поддерживает.

Также обычно используется PE_IndicatorCheckBox (вместо использования PE_IndicatorMenuCheckMark) и PE_IndicatorRadioButton для отображения выбираемых пунктов меню; мы не включали их в дерево стиля, так как это необязательно и варьируется от стиля к стилю.

Опция стиля для пунктов меню — QStyleOptionMenuItem. Следующие таблицы описывают флаги состояния и другие члены.

Состояние Условие
State_Selected Курсор над элементом действия, и это действие не разделитель.
State_Sunken Нажата кнопка мыши на пункте меню.
State_DownArrow Установлено, если пункт меню — прокрутка меню и оно прокручивается вниз.
Элемент Описание
checkType Значение перечисления CheckType: NotCheckable, Exclusive или NonExclusive.
checked Логическое значение, равное true если пункт меню выбран.
font Шрифт QFont, используемый для текста пункта меню.
icon Значок QIcon пункта меню.
maxIconWidth Максимальная разрешенная ширина значка.
menuHasChecableItem Логическое значение, равное true если хотя бы один пункт меню выбираем.
menuItemType Тип пункта меню. Значение перечисления MenuItemType.
menuRect Ограничительный прямоугольник QMenu, в котором находится пункт меню.
tabWidth Расстояние между текстом пункта меню и сочетанием клавиш.
text Текст пункта меню.

Настройка опции стиля для CE_MenuTearOff и CE_MenuScroller также использует QStyleOptionMenuItem; они устанавливают только переменную menuRect в дополнение к общим настройкам с QStyleOption's initFrom().

Панель меню

QMenuBar использует стиль для отрисовки каждого пункта панели меню и пустого места панели меню. Сами выпадающие меню являются QMenu (см. Меню). Дерево элементов стиля для панели меню:

END_OF_DOCUMENT_MARKER

Панель и пустая область рисуются после пунктов меню. 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_Sibling Этот индикатор ветви имеет элемент.
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_DockWidgetTitleMargin, 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.13/style-reference.html

Spec-Zone.ru

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