Spec-Zone.ru › Qt 5.11

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

Стили (классы, наследующие 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() возвращает 0. Например:

const QStyleOptionFocusRect *focusRectOption =
        qstyleoption_cast<const QStyleOptionFocusRect *>(option);
if (focusRectOption) {
    ...
}

Следующий фрагмент кода демонстрирует, как использовать QStyle для рисования прямоугольника фокуса из пользовательского виджета в событии paintEvent():

void MyWidget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    ...

    QStyleOptionFocusRect option(1);
    option.init(this);
    option.backgroundColor = palette().color(QPalette::Window);

    style().drawPrimitive(QStyle::PE_FrameFocusRect, &option, &painter,
                          this);
}

Следующий пример показывает, как вывести из существующего стиля, чтобы настроить внешний вид графического элемента:

class CustomStyle : public QProxyStyle
{
    Q_OBJECT

public:
    CustomStyle();
    ~CustomStyle() {}

    void drawPrimitive(PrimitiveElement element, const QStyleOption *option,
                       QPainter *painter, const QWidget *widget) const 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 также центрирует содержимое кнопок, когда у кнопки есть значок. Вы можете изучить эту реализацию, если вам нужен пример повторной реализации рисования меток.

Теперь мы рассмотрим реализацию 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).

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

Вот структура стиля для 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 Кнопка инструмента имеет свойство автовозврата.
State_Raised Кнопка не утоплена (т.е. не отмечена и не нажата мышкой).
State_Sunken Кнопка нажата.
State_On Кнопка переключаемая и отмечена.

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

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

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

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

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

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

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

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

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

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

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

Меню

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

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

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

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

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

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

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

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

Панель меню

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

Панель и пустая область рисуются после пунктов меню. QPainter, который QMenuBar отправляет стилю, имеет ограничивающие прямоугольники пунктов, вырезанные (т.е., область обрезки), поэтому вам не нужно беспокоиться о рисовании поверх пунктов. Метрики пикселей в QMenuBar используются при вычислении ограничивающих прямоугольников пунктов меню.

QStyleOptionMenuItem используется для пунктов меню. Члены, используемые QMenuBar, описаны в следующей таблице:

Член Содержание
menuRect Ограничивающий прямоугольник всего меню, к которому принадлежит пункт.
text Текст пункта.
icon Иконка пункта меню (нечасто стили рисуют эту иконку).

QStyleOptionMenuItem также используется для рисования CE_EmptyMenuBarArea.

QStyleOptionFrame используется для рисования рамки панели. lineWidth задается значением PM_MenuBarPanelWidth. midLineWidth в настоящее время всегда равен 0.

Заголовки представлений элементов

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

Вот QTableWidget, показывающий ограничивающие прямоугольники заголовка Java:

QHeaderView использует CT_HeaderSection, PM_HeaderMargin и PM_HeaderGripMargin для вычисления размера и проверки попадания. PM_HeaderMarkSize в настоящее время не используется Qt. QTableView рисует кнопку в верхнем левом углу (т.е., область пересечения вертикального и горизонтального заголовков) как CE_Header.

Стилевой параметр для представлений заголовков — QStyleOptionHeader. Представление рисует одну секцию заголовка за раз, поэтому данные относятся к рисуемой секции. Его содержимое:

Член Содержание
icon Иконка заголовка (для рисуемой секции).
iconAlignment Выравнивание (Qt::Alignment) иконки в заголовке.
orientation Значение Qt::Orientation, определяющее, является ли заголовок горизонтальным заголовком над представлением или вертикальным заголовком слева.
position Значение QStyleOptionHeader::SectionPosition, указывающее положение секции заголовка относительно других секций.
section Содержит рисуемую секцию.
selectedPosition Значение QStyleOptionHeader::SelectedPosition, указывающее положение выделенной секции относительно рисуемой секции.
sortIndicator Значение QStyleOptionHeader::SortIndicator, описывающее направление, в котором должен быть нарисован указатель сортировки секции.
text Текст текущей рисуемой секции.
textAlignment Qt::Alignment текста внутри секции заголовка.

Указатели ветвей дерева

Указатели ветвей в представлении дерева рисуются стилем с PE_IndicatorBranch. Мы рассматриваем здесь указатели, описывающие отношения узлов в дереве. Для рисования этих элементов используется общий QStyleOption. Различные типы ветвей описываются состояниями. Поскольку нет специфического стилевого параметра, мы просто представляем таблицу состояний:

Состояние Устанавливается когда
State_Sibling Узел в дереве имеет брата (т.е., существует другой узел в том же столбце).
State_Item Этот указатель ветви имеет элемент.
State_Children Ветвь имеет потомков (т.е., новая ветвь дерева может быть открыта в ветви).
State_Open В указателе ветви открыта подветвь.

Представление дерева (и виджет дерева) использует стиль для рисования ветвей (узлов) дерева.

QStyleOption используется в качестве стиля для PE_IndicatorBranch с флагами состояния, установленными в зависимости от типа ветви.

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

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

PM_SmallIconSize для sizeHints.

QToolBox — контейнер, хранящий набор виджетов. Он имеет вкладку для каждого виджета и отображает один из них за раз. Панель инструментов выкладывает отображаемые компоненты (кнопки панели инструментов и выбранный виджет) в QVBoxLayout.

Мы показываем изображение панели инструментов в стиле Plastique:

Все элементы имеют одинаковые ограничивающие прямоугольники в стиле Plastique, а также в других встроенных стилях Qt.

Стилевой параметр для панелей инструментов — QStyleOptionToolBox. Он содержит текст и иконку содержимого панели инструментов. Единственное состояние, устанавливаемое QToolBox, это State_Sunken, которое устанавливается, когда пользователь нажимает вкладку вниз мышкой. Остальные члены QStyleOptionToolBox:

Член Содержание
icon Иконка на вкладке панели инструментов.
text Текст на вкладке панели инструментов.

Маркер размера

Маркер размера рассчитывает свой размер с CT_SizeGrip. Метрика пикселей PM_SizeGripSize в настоящее время не используется Qt. Дерево элементов для изображения в стиле Plastique QSizeGrip выглядит так:

Мы показываем маркер размера в правом нижнем углу QMainWindow.

Стилевой параметр маркера размера, QStyleOptionSizeGrip, имеет один член помимо общих членов из QStyleOption:

Член Содержание
corner Значение Qt::Corner, указывающее, в каком углу окна (или эквиваленте) находится маркер.

Резиновая полоска

Дерево стиля QRubberBand состоит из двух узлов.

Мы показываем изображение перемещения окна в стиле Java в QMdiArea с резиновой полоской:

Стилевой параметр для резиновых полосок — QStyleOptionRubberBand. Его члены:

Член Содержание
opaque Булево значение, которое true если резиновая полоска должна быть нарисована непрозрачно (т.е., цветом).
shape Значение перечисления QRubberBand::Shape, которое содержит форму полоски (прямоугольник или линия).

Виджеты причаливания

При выкладке содержимого виджета причаливания он запрашивает у стиля эти метрики пикселей: PM_DockWidgetSeparatorExtent, PM_DockWidgetTitleBarButtonMargin, PM_DockWidgetFrameWidth, и PM_DockWidgetTitleMargin. Он также вычисляет ограничивающие прямоугольники кнопок плавания и закрытия с SE_DockWidgetFloatButton и SE_DockWidgetCloseButton.

Пунктирные линии указывают, что отправитель сохраняет экземпляры получателя стрелки (т.е., это не элемент стиля для рисования). Виджет причаливания рисует PE_frameDockWidget только когда он отделен от основного окна (т.е., это окно верхнего уровня). Если он прикреплен, он рисует индикатор ручки изменения размера виджета причаливания. Мы показываем виджет причаливания в прикрепленном и плавающем состоянии в стиле plastique:

Стилевой параметр — QStyleOptionDockWidget:

Член Содержание
closeable Булево значение, указывающее, можно ли закрыть окно виджета причаливания.
floatable Булево значение, указывающее, может ли окно виджета причаливания плавать (т.е., отделяться от основного окна, в котором оно находится).
movable Булево значение, указывающее, можно ли перемещать окно (т.е., перемещать его в другие области виджетов причаливания).
title Текст заголовка окна виджета причаливания.

Для кнопок используется QStyleOptionButton (см. Кнопки инструментов для описания содержимого). Ручка изменения размера виджета причаливания имеет простой QStyleOption.

© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/archives/qt-5.11/style-reference.html

Spec-Zone.ru

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