Spec-Zone.ru › Qt 6.0

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

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

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

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

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

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

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

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

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

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

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

QColor

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

QColorSpace

Абстракция цветового пространства

QColorTransform

Преобразование между цветовыми пространствами

QCommonStyle

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

QCursor

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

QFont

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

QFontDatabase

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

QFontInfo

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

QGraphicsAnchor

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

QGraphicsAnchorLayout

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

QPalette

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

QStyle

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

QStyleFactory

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

QStyleHintReturn

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

QStyleHintReturnMask

Стилистические подсказки, возвращающие QRegion

QStyleHintReturnVariant

Стилистические подсказки, возвращающие QVariant

QStyleOption

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

QStylePainter

Удобный класс для рисования элементов QStyle внутри виджета

Реализация QStyle

API QStyle содержит функции для рисования виджетов, статические вспомогательные функции для выполнения общих и сложных задач (например, расчёта положения ползунков ползунковых регуляторов) и функции для выполнения различных расчётов при отрисовке (например, для виджетов для расчёта их размеров).

Стиль также помогает некоторым виджетам с расположением их содержимого. Кроме того, он создаёт QPalette, который содержит QBrush для рисования.

QStyle рисует графические элементы; элемент — это виджет или часть виджета, например, выступ кнопки, рамка окна или полоса прокрутки. Большинство функций рисования теперь принимают четыре аргумента:

  • значение перечисления, указывающее, какой графический элемент нужно нарисовать
  • QStyleOption, определяющий, как и где нужно отрисовать этот элемент
  • QPainter, который должен использоваться для рисования элемента
  • виджет QWidget, на котором выполняется отрисовка (необязательно)

Когда виджет запрашивает у стиля отрисовку элемента, он предоставляет стилю QStyleOption, который является классом, содержащим информацию, необходимую для отрисовки. Благодаря QStyleOption возможно заставить QStyle отрисовывать виджеты без связывания кода для виджета. Это позволяет использовать функции рисования QStyle на любом устройстве рисования, т.е. вы можете нарисовать комбинацию на любом виджете, а не только на QComboBox.

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

В этом разделе мы рассмотрим элементы стиля, параметры стиля и функции QStyle. В конце мы опишем, как используется палитра.

Элементы в представлениях элементов отрисовываются делегатами в Qt. Заголовки представления элементов всё ещё рисуются стилем. Делегат по умолчанию Qt, QStyledItemDelegate, частично рисует свои элементы через текущий стиль; он рисует индикаторы флажков и вычисляет ограничивающие прямоугольники для элементов, из которых состоит элемент. В этом документе мы описываем только то, как реализовать подкласс QStyle. Если вы хотите добавить поддержку других типов данных, отличных от поддерживаемых QStyledItemDelegate, вам нужно реализовать пользовательский делегат. Обратите внимание, что делегаты должны настраиваться программно для каждого отдельного виджета (т.е. делегаты по умолчанию не могут предоставляться в виде плагинов).

Элементы стиля

Элемент стиля — это графическая часть графического интерфейса пользователя. Виджет состоит из иерархии (или дерева) элементов стиля. Например, когда стиль получает запрос на рисование кнопки (например, из QPushButton), он рисует метку (текст и значок), выступа кнопки и рамку фокуса. Выступ кнопки, в свою очередь, состоит из рамки вокруг выступа и двух других элементов, которые мы рассмотрим позже. Ниже представлена концептуальная иллюстрация дерева элементов кнопки.

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

Существует три типа элементов: примитивные элементы, элементы управления и сложные элементы управления. Элементы определяются перечислениями ComplexControl, ControlElement и PrimitiveElement. Значения каждого перечисления имеют префикс для идентификации их типа: CC_ для сложных элементов, CE_ для элементов управления и PE_ для примитивных элементов. В следующих трёх разделах мы рассмотрим, что определяет различные элементы, и увидим примеры виджетов, которые их используют.

Описание класса QStyle содержит список этих элементов и их роли в стилизации виджетов. Мы увидим, как они используются при стилизации отдельных виджетов.

Примитивные элементы

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

Элементы управления

Элемент управления выполняет действие или отображает информацию для пользователя. Примеры элементов управления — кнопки, флажки и заголовки секций в таблицах и древовидных представлениях. Элементы управления необязательно являются полными виджетами, такими как кнопки, но также могут быть частями виджетов, например, вкладки панели вкладок и ползунки полосы прокрутки. Они отличаются от примитивных элементов тем, что не являются пассивными, а выполняют функцию во взаимодействии с пользователем. Управляющие элементы, состоящие из нескольких элементов, часто используют стиль для вычисления границ элементов. Доступные дочерние элементы определяются перечислением SubElement. Это перечисление используется только для вычисления границ; дочерние элементы не являются графическими элементами, которые нужно рисовать, как примитивные, управляющие и сложные элементы.

Сложные элементы управления

Сложные элементы управления содержат дочерние элементы управления. Сложное поведение элементов управления зависит от того, где пользователь обрабатывает их мышью и какие клавиши нажаты на клавиатуре. Это зависит от того, какой дочерний элемент управления (если таковой имеется) находится под курсором мыши или нажата. Примеры сложных элементов управления — полосы прокрутки и комбинированные поля. С помощью полосы прокрутки можно перемещать ползунок мышью и нажимать кнопки «вверх» и «вниз». Доступные дочерние элементы управления определяются перечислением SubControl.

Помимо отрисовки, стиль должен предоставлять виджетам информацию о том, на какой дочерний элемент управления (если таковой имеется) было произведено нажатие мыши. Например, QScrollBar должен знать, нажали ли пользователь на ползунок, желобок ползунка или на одну из кнопок.

Обратите внимание, что дочерние элементы управления не являются тем же, что и элементы управления, описанные в предыдущем разделе. Вы не можете использовать стиль для рисования дочернего элемента управления; стиль будет только вычислять границу, в которой должен быть нарисован дочерний элемент управления. Однако часто сложные элементы используют элементы управления и примитивные элементы для рисования своих дочерних элементов управления, что является подходом, часто используемым встроенными стилями Qt и стилями Java. Например, стиль Java использует PE_IndicatorCheckBox для рисования флажка в группах (что является дочерним элементом CC_GroupBox). Некоторые дочерние элементы управления имеют эквивалентный элемент управления, например, ползунок полосы прокрутки (SC_SCrollBarSlider и CE_ScrollBarSlider).

Другие задачи QStyle

Элементы стиля и виджеты, как уже упоминалось, используют стиль для вычисления границ дочерних элементов и дочерних элементов управления. Для измерений при рисовании также используются пиксельные метрики, которые представляют собой зависящие от стиля размеры в пикселях экрана. Доступные прямоугольники и пиксельные метрики представлены тремя перечислениями в QStyle: SubElement, SubControl и PixelMetric. Значения перечислений легко идентифицируются, поскольку они начинаются с SE_, SC_ и PM_.

Стиль также содержит набор подсказок стиля, которые представлены значениями в перечислении StyleHint. Все виджеты не имеют одинаковых функциональности и внешнего вида в разных стилях. Например, когда пункты меню в меню не помещаются в один столбец на экране, некоторые стили поддерживают прокрутку, а другие рисуют больше одного столбца для размещения всех пунктов.

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

Стиль вычисляет интервалы между виджетами в макетах. Существует два способа обработки этих вычислений стилем. Вы можете установить PM_LayoutHorizontalSpacing и PM_LayoutVerticalSpacing, что является способом, используемым стилем Java (через QCommonStyle). В качестве альтернативы, вы можете реализовать QStyle::layoutSpacing() и QStyle::layoutSpacingImplementation(), если вам нужен больший контроль над этой частью макета. В этих функциях вы можете вычислить интервал на основе типов элементов управления (QSizePolicy::ControlType) для разных политик размеров (QSizePolicy::Policy), а также опцию стиля для рассматриваемого виджета.

Параметры стиля

Подклассы QStyleOption содержат всю необходимую информацию для форматирования отдельных элементов. Параметры стиля инициализируются — обычно в стеке — и заполняются вызывающей стороной функции QStyle. В зависимости от того, что рисуется, стиль ожидает другой класс параметров стиля. Например, элемент QStyle::PE_FrameFocusRect ожидает аргумент QStyleOptionFocusRect, и можно создать пользовательские подклассы, которые может использовать пользовательский стиль. Параметры стиля сохраняют общедоступные переменные для повышения производительности.

Виджеты могут находиться в нескольких различных состояниях, которые определяются перечислением State. Некоторые флаги состояний имеют разное значение в зависимости от виджета, но другие являются общими для всех виджетов, например, State_Disabled. Именно QStyleOption устанавливает общие состояния с помощью QStyleOption::initFrom(); остальные состояния устанавливаются отдельными виджетами.

В частности, параметры стиля содержат палитру и границы виджетов, которые необходимо нарисовать. У большинства виджетов есть специализированные параметры стиля. QPushButton и QCheckBox, например, используют QStyleOptionButton в качестве параметров стиля, которые содержат текст, значок и размер значка. Точное содержимое всех параметров описывается при рассмотрении отдельных виджетов.

При переопределении функций QStyle, которые принимают параметр QStyleOption, часто необходимо привести QStyleOption к подклассу (например, QStyleOptionFocusRect). Для обеспечения безопасности вы можете использовать qstyleoption_cast() для проверки правильности типа указателя. Если объект не имеет нужного типа, qstyleoption_cast() возвращает nullptr. Например:

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

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

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

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

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

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

class CustomStyle : public QProxyStyle
{
    Q_OBJECT

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

    void drawPrimitive(PrimitiveElement element, const QStyleOption *option,
                       QPainter *painter, const QWidget *widget) const override;
};

void CustomStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
                                QPainter *painter, const QWidget *widget) const
{
    if (element == PE_IndicatorSpinUp || element == PE_IndicatorSpinDown) {
        QPolygon points(3);
        int x = option->rect.x();
        int y = option->rect.y();
        int w = option->rect.width() / 2;
        int h = option->rect.height() / 2;
        x += (option->rect.width() - w) / 2;
        y += (option->rect.height() - h) / 2;

        if (element == PE_IndicatorSpinUp) {
            points[0] = QPoint(x, y + h);
            points[1] = QPoint(x + w, y + h);
            points[2] = QPoint(x + w / 2, y);
        } else { // PE_SpinBoxDown
            points[0] = QPoint(x, y);
            points[1] = QPoint(x + w, y);
            points[2] = QPoint(x + w / 2, y + h);
        }

        if (option->state & State_Enabled) {
            painter->setPen(option->palette.mid().color());
            painter->setBrush(option->palette.buttonText());
        } else {
            painter->setPen(option->palette.buttonText().color());
            painter->setBrush(option->palette.mid());
        }
        painter->drawPolygon(points);
    } else {
    QProxyStyle::drawPrimitive(element, option, painter, widget);
    }
}

Функции QStyle

Класс QStyle определяет три функции для рисования примитивных, управляющих и сложных элементов: drawPrimitive(), drawControl() и drawComplexControl(). Функции принимают следующие параметры:

  • значение перечисления элемента для рисования.
  • QStyleOption, содержащий информацию, необходимую для рисования элемента.
  • QPainter для рисования элемента.
  • указатель на QWidget, обычно виджет, на котором отрисовывается элемент.

Не все виджеты отправляют указатель на себя. Если параметр стиля, отправленный в функцию, не содержит необходимой информации, вы должны проверить реализацию виджета, чтобы узнать, отправляет ли он указатель на себя.

Класс QStyle также предоставляет вспомогательные функции, которые используются при рисовании элементов. Функция drawItemText() рисует текст в указанном прямоугольнике, принимая QPalette в качестве параметра. Функция drawItemPixmap() помогает выровнять пиксель внутри указанной области.

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

  • Функция subElementRect() принимает значение перечисления SubElement и вычисляет границу дочернего элемента. Стиль использует эту функцию для того, чтобы знать, где рисовать разные части элемента. Это в основном делается для повторного использования; если вы создаете новый стиль, вы можете использовать то же расположение дочерних элементов, что и у родительского класса.
  • Функция subControlRect() используется для вычисления границ дочерних элементов управления в сложных элементах управления. При реализации нового стиля вы переопределяете subControlRect() и вычисляете прямоугольники, которые отличаются от родительского класса.
  • Функция pixelMetric() возвращает пиксельную метрику, которая представляет собой зависящий от стиля размер в пикселях экрана. Она принимает значение перечисления PixelMetric и возвращает правильное измерение. Обратите внимание, что пиксельные метрики не обязательно должны быть статическими измерениями, но могут быть вычислены, например, с использованием параметра стиля.
  • Функция hitTestComplexControl() возвращает дочерний элемент управления, над которым находится указатель мыши в сложном элементе управления. Обычно это просто вопрос использования subControlRect() для получения границ дочерних элементов управления и определения того, какой прямоугольник содержит положение курсора.

QStyle также имеет функции polish() и unpolish(). Все виджеты отправляются в функцию polish() перед отображением и в unpolish() при их скрытии. Вы можете использовать эти функции для установки атрибутов на виджеты или для выполнения других операций, необходимых вашему стилю. Например, если вам нужно знать, когда курсор мыши находится над виджетом, вам необходимо установить атрибут виджета WA_Hover. Флаг состояния State_MouseOver затем будет установлен в параметрах стиля виджета.

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

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

Палитра

Каждый стиль предоставляет палитру цветов — то есть, QBrush — которая должна использоваться для рисования виджетов. Существует один набор цветов для различных состояний виджетов (QPalette::ColorGroup): активное (виджеты в окне, у которых есть фокус клавиатуры), неактивное (виджеты, используемые для других окон) и отключенное (виджеты, которые отключены). Состояния можно найти, запросив флаги состояния State_Active и State_Enabled. Каждый набор содержит определённые роли цветов, задаваемые перечислением QPalette::ColorRole. Роли описывают ситуации, в которых цвета должны использоваться (например, для рисования фонов виджетов, текста или кнопок).

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

QPalette, который предоставляет палитру, хранит цвета для различных состояний виджетов и ролей цветов. Палитра для стиля возвращается функцией standardPalette(). Стандартная палитра не устанавливается автоматически при установке нового стиля на приложение (QApplication::setStyle()) или виджет (QWidget::setStyle()), поэтому вы должны установить палитру самостоятельно с помощью (QApplication::setPalette()) или (QWidget::setPalette()).

Не рекомендуется жёстко кодировать цвета, так как приложения и отдельные виджеты могут устанавливать свою собственную палитру и также использовать палитру своего стиля для рисования. Обратите внимание, что ни один из виджетов Qt не устанавливает свою собственную палитру. Стиль Java жёстко кодирует некоторые цвета, но только по решению автора; это не рекомендуется. Конечно, не предполагается, что стиль будет выглядеть хорошо с любой палитрой.

Проблемы реализации

При реализации стилей необходимо учитывать несколько проблем. Здесь мы предоставим некоторые подсказки и рекомендации по реализации.

При реализации стилей необходимо просмотреть код виджетов и код базового класса и его предков. Это связано с тем, что виджеты используют стиль по-разному, потому что реализация в виртуальных функциях разных стилей может влиять на состояние рисования (например, путём изменения состояния QPainter без его восстановления и рисования некоторых элементов без использования соответствующих метрик пикселей и подобъектов).

Рекомендуется, чтобы стили не изменяли предлагаемый размер виджетов с помощью функции QStyle::sizeFromContents(), а позволили реализации QCommonStyle обрабатывать это вместо этого. Если изменения необходимы, следует стараться делать их небольшими; разработка приложений может быть затруднена, если макет виджетов выглядит существенно по-разному в различных стилях.

Стиль Java

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

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

Проектирование и реализация

Первым шагом в проектировании стиля было выбор базового класса. Мы выбрали подкласс QCommonStyle. Этот класс реализует большую часть необходимой функциональности, за исключением фактического рисования.

Стиль реализован в одном классе. Мы сделали это, потому что считаем удобным хранить весь код в одном файле. Кроме того, это выгодно с точки зрения оптимизации, так как мы создаём меньше объектов. Мы также стремимся минимизировать количество функций, используя переключатели для определения элемента, который нужно нарисовать в функциях. Это приводит к большим функциям, но поскольку мы делим код для каждого элемента в переключателях, код по-прежнему должен быть легко читаемым.

Ограничения и отличия от Java

Мы не полностью реализовали каждый элемент в стиле Java. Таким образом, мы сократили объём и сложность кода. В общем, стиль задумывался как практический пример для этого обзорного документа по стилям, а не для включения в сам Qt.

Не все виджеты реализуют все состояния. Это относится к общим состояниям, например, State_Disabled. Однако каждое состояние реализовано по крайней мере для одного виджета.

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

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

Мы жёстко закодировали цвета (мы не используем QPalette) для линейных градиентов, которые используются, например, для наклонов кнопок, панелей инструментов и флажков. Это связано с тем, что палитра Java не может генерировать эти цвета. Java также не изменяет эти цвета в зависимости от группы цветов или роли виджета (они не зависят от палитры), поэтому это не проблема в любом случае.

Стилизуются виджеты Qt. Некоторые виджеты вообще не существуют в Java, например, QToolBox. Другие содержат элементы, которых нет в виджетах Java. Деревовидный виджет является примером последнего, в котором в Java JTree нет заголовка.

Стиль не обрабатывает обратные макеты. Мы предполагаем, что направление макета слева направо. QCommonStyle обрабатывает обратные виджеты; если бы мы реализовали обратные макеты, виджеты, которые мы изменяем позицию дочерних элементов или сами обрабатываем выравнивание текста в метках, необходимо было бы обновить.

Стилизация флажков Java

В качестве примера мы рассмотрим стилизация флажков в стиле Java. Мы опишем весь процесс и напечатаем весь код как в стиле Java, так и в классах Qt, участвующих в этом. В остальной части документа мы не будем рассматривать исходный код отдельных виджетов. Надеемся, что это поможет вам понять, как искать в коде, если вам нужно проверить определённые детали реализации; большинство виджетов следуют той же структуре, что и флажки.

Мы начнём с просмотра того, как QCheckBox создаёт свой вариант стиля, который является QStyleOptionButton для флажков:

    opt.initFrom(q);
        if (down)
        opt.state |= QStyle::State_Sunken;
    if (tristate && noChange)
        opt.state |= QStyle::State_NoChange;
    else
        opt.state |= checked ? QStyle::State_On :
        QStyle::State_Off;
    if (q->testAttribute(Qt::WA_Hover) &&  q->underMouse()) {
        if (hovering)
        opt.state |= QStyle::State_MouseOver;
        else
        opt.state &= ~QStyle::State_MouseOver;
    }
    opt.text = text;
    opt.icon = icon;
    opt.iconSize = q->iconSize();

Сначала мы позволим QStyleOption настроить вариант с информацией, общей для всех виджетов с initFrom(). Мы рассмотрим это вскоре.

Переменная down QStyleOption равна true при нажатии пользователем на поле; это верно для флажка, независимо от того, отмечен он или нет. Состояние State_NoChange устанавливается, когда у нас есть трёхсостояниевый флажок и он частично отмечен. Он имеет значение State_On если поле отмечено и State_Off если оно не отмечено. State_MouseOver устанавливается, если курсор мыши находится над флажком и виджет имеет свойство Qt::WA_Hover установленное — вы устанавливаете это в QStyle::polish(). Кроме того, вариант стиля также содержит текст, значок и размер значка кнопки.

initFrom() устанавливает вариант стиля с атрибутами, общими для всех виджетов. Мы печатаем его реализацию здесь:

    state = QStyle::State_None;
    if (widget->isEnabled())
        state |= QStyle::State_Enabled;
    if (widget->hasFocus())
        state |= QStyle::State_HasFocus;
    if (widget->window()->testAttribute(Qt::WA_KeyboardFocusChange))
        state |= QStyle::State_KeyboardFocusChange;
    if (widget->underMouse())
        state |= QStyle::State_MouseOver;
    if (widget->window()->isActiveWindow())
        state |= QStyle::State_Active;
#ifdef 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 и затем рисует их. Если флажок имеет фокус, также рисуется рамка фокуса.

END_OF_DOCUMENT_MARKER

Стиль 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() рисует текст, учитывая выравнивание, направление макета и мнeмонический символ. Также он использует палитру для рисования текста в правильном цвете.

Рисование меток часто оказывается довольно сложным. К счастью, это обычно можно обработать в базовом классе. Стиль 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. Обе переменные представляют собой объединённые по оператору OR значения перечисления QStyle::SubControl. Они указывают, из каких подэлементов управления состоит сложное управление и какие из этих элементов управления в настоящее время активны.

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

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

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

Кнопки

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

The style structure for push buttons

Макет кнопок относительно границ элементов варьируется от стиля к стилю. Это затрудняет показ концептуальных изображений. Кроме того, элементы могут — даже должны — иметь одинаковые границы; PE_PushButtonBevel, например, используется в QCommonStyle для рисования элементов, которые он содержит: PE_FrameDefaultButton, PE_FrameButtonBevel, и PE_PanelButtonCommand, все из которых имеют общие границы в общем стиле. PE_PushButtonBevel также отвечает за рисование индикатора меню (QCommonStyle рисует PE_IndicatorArrowDown).

Ниже приведено изображение кнопки в стиле Java, отображающее прямоугольники границ элементов. Цвета используются для разделения прямоугольников границ на изображении; они не выполняют никакой другой функции. Это также верно для аналогичных изображений для других виджетов.

Стиль Java, а также все другие стили, реализованные в Qt, не используют PE_FrameButtonBevel. Обычно кнопка с PE_DefaultFrame корректирует прямоугольник PE_PanelButtonCommand на PM_ButtonDefaultIndicator. CE_PushButtonLabel находится путём корректировки rect на PM_DefaultFrameWidth.

Теперь мы рассмотрим опцию стиля для кнопок — QStyleOptionButton. Следующая таблица содержит состояния, которые QPushButton может установить в опции стиля:

Состояние Устанавливается при
State_Sunken Кнопка нажата или показано нажатие меню
State_On Кнопка отмечена
State_Raised Кнопка не плоская и не нажата

Другие члены QStyleOptionButton:

Элемент Содержание
features Флаги перечисления QStyleOptionButton::ButtonFeatures, описывающие различные свойства кнопки (см. перечисление)
icon Иконка кнопки QIcon (если есть)
iconSize Размер QSize иконки
text QString с текстом кнопки

Кнопки "галочки" и радиокнопки

Структуры для радио- и "галочных" кнопок идентичны. Мы демонстрируем структуру на примере элемента QCheckBox и названий пиксельных метрик:

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

Состояние Состояние устанавливается при
State_sunken Кнопка нажата
State_NoChange Кнопка частично отмечена (для кнопок с тремя состояниями.)
State_On Кнопка отмечена
State_Off Кнопка не отмечена

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

Вкладки

В Qt, QTabBar использует стиль для отрисовки вкладок. Вкладки существуют либо в QTabWidget, который содержит QTabBar, либо как отдельный бар. Если бар не является частью виджета вкладок, он рисует свою собственную основу.

QTabBar выстраивает вкладки, поэтому стиль не контролирует расположение вкладок. Однако, выстраивая свои вкладки, бар запрашивает у стиля PM_TabBarTabHSpace и PM_TabBarTabVSpace, что представляет собой дополнительную ширину и высоту по отношению к минимальному размеру метки вкладки (иконки и текста). Стиль также может влиять на размер вкладки до ее выстраивания, так как таб бар запрашивает CT_TabBarTab. Граница прямоугольника бара определяется виджетом вкладок, когда он является частью виджета (при этом учитывается CT_TabBarTab).

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

Вот структура стиля для QTabWidget и QTabBar:

Пунктирные линии указывают, что QTabWidget содержит вкладку, но не рисует её сам. QTabBar рисует только свою базовую линию, когда не является частью виджета вкладок, и сохраняет две кнопки инструментов для прокрутки бара, когда все вкладки не помещаются; см. Кнопки инструментов для их структуры элементов. Также обратите внимание, что, поскольку кнопки являются дочерними элементами бара вкладок, они отрисовываются после бара. Границы прямоугольников вкладок перекрывают базу на PM_TabBarBaseOverlap.

Вот виджет вкладок в стиле Java:

В стиле Java форма и метка вкладки имеют ту же область прямоугольника, что и CE_TabBarTab. Обратите внимание, что вкладки перекрывают рамку виджета вкладок. Основание бара вкладок (если отрисовано) — это область, где вкладки и рамка перекрываются.

Опция стиля для вкладок (QStyleOptionTab) содержит необходимую информацию для отрисовки вкладок. Опция содержит положение вкладки в баре вкладок, положение выделенной вкладки, форму вкладки, текст, иконку и размер иконки.

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

Ниже приводится таблица состояний, которые бар вкладок может устанавливать на своих вкладках:

Состояние Состояние устанавливается при
State_Sunken Вкладка нажата мышью.
State_Selected Если это текущая вкладка.
State_HasFocus У бара вкладок фокус и вкладка выделена.

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

Следующая таблица содержит члены QStyleOptionTab:

Элемент Содержание
cornerWidgets Флаги перечисления CornerWidget, указывающие, есть ли и какие виджеты углов у бара вкладок.
icon Иконка QIcon вкладки.
iconSize Размер QSize иконки.
position Значение перечисления TabPosition, указывающее положение вкладки на баре относительно других вкладок.
row Указывает строку, в которой находится вкладка.
selectedPosition Значение перечисления SelectedPosition, указывающее, расположена ли выделенная вкладка рядом с или является текущей вкладкой.
shape Значение перечисления QTabBar::Shape, указывающее, имеют ли вкладки закругленные или треугольные углы, и ориентацию вкладки.
text Текст вкладки.

Рамка для виджетов вкладок использует QStyleOptionTabWidgetFrame в качестве опции стиля. Мы перечисляем здесь её члены. Она не имеет установленных состояний помимо общих флагов.

Элемент Содержание
leftCornerWidgetSize Размер QSize виджета левого угла (если есть).
rightCornerWidgetSize Размер QSize виджета правого угла (если есть).
lineWidth Ширина линии для отрисовки панели.
midLineWith Это значение всегда равно 0.
shape Форма вкладок на баре вкладок.
tabBarSize Размер QSize бара вкладок.

Скроллбары

Вот структура стиля для скроллбаров:

QScrollBar просто создаёт свою опцию стиля, а затем рисует CC_ScrollBar. Некоторые стили рисуют фон страницы добавления и страницы удаления с помощью PE_PanelButtonBevel, а также используют индикаторные стрелки для отрисовки стрелок в индикаторах следующей и предыдущей строк; мы не включили их в дерево, так как их использование зависит от конкретного стиля. PM_MaximumDragDistance стиля — это максимальное расстояние в пикселях, на которое мышь может отойти от границ скроллбара и при этом перемещать ползунок.

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

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

Давайте рассмотрим различные состояния, которые скроллбар может установить в опции стиля:

Состояние Состояние устанавливается при
State_Horizontal Скроллбар горизонтальный.

Опция стиля для QScrollBar — QStyleOptionSlider. Её члены перечислены в следующей таблице. Опция используется всеми QAbstractSlider; мы описываем здесь только члены, относящиеся к скроллбарам.

Элемент Содержание
maximum Максимальное значение скроллбара.
minimum Минимальное значение скроллбара.
notchTarget Количество пикселей между рисками.
orientation Значение перечисления Qt::Orientation, указывающее, вертикальный или горизонтальный скроллбар.
pageStep Число, на которое увеличивается или уменьшается значение ползунка при шагах страницы (относительно размера ползунка и диапазона его значений).
singleStep Число, на которое увеличивается или уменьшается значение ползунка при отдельных (или линейных) шагах.
sliderValue Значение ползунка.
sliderPosition Положение ползунка. Это то же, что и sliderValue если скроллбар QAbstractSlider::tracking. Если нет, скроллбар не обновляет своё значение до отпускания мыши.
upsideDown Указывает направление, в котором скроллбар увеличивает своё значение. Используется вместо QStyleOption::direction для всех абстрактных ползунков.

Ползунки

При расчёте размера подсказки ползунка запрашиваются PM_SliderThickness и PM_SliderLength у стиля. Как и с скроллбарами, QSlider позволяет пользователю перемещать ползунок только если мышь находится в пределах PM_MaximumDragDistance от границ ползунка. При отрисовке он создаёт опцию стиля и вызывает drawComplexControl() с CC_Slider:

Мы также покажем изображение ползунка в стиле Java. Мы покажем области прямоугольников подэлементов, так как вся отрисовка выполняется в CC_Slider.

QSlider использует QStyleOptionSlider, как и все QAbstractSliderы. Мы представляем таблицу с членами, которые влияют на QSlider:

Член Содержимое
maximum Максимальное значение ползунка.
minimum Минимальное значение ползунка.
notchTarget Это количество пикселей между каждой риской.
orientation Значение перечисления Qt::Orientation, которое указывает, является ли ползунок вертикальным или горизонтальным.
pageStep Число, на которое увеличивается или уменьшается значение ползунка при шаге страницы.
singleStep Число, на которое увеличивается или уменьшается значение ползунка при одиночном (или линейном) шаге.
sliderValue Значение ползунка.
sliderPosition Положение ползунка, заданное как значение ползунка. Оно будет равно sliderValue если ползунок отслеживает; если нет, значение ползунка не изменится, пока рукоятка не будет отпущена мышкой.
upsideDown Этот член используется вместо QStyleOption::direction для всех абстрактных ползунков.

Обратите внимание, что ползунок не использует направление для обратной компоновки; он использует upsideDown.

Поле ввода с кнопками

Когда QSpinBox рисует себя, он создаёт QStyleOptionSpinBox и просит стиль нарисовать CC_SpinBox. Поле редактирования — это строковое поле, которое является дочерним элементом поля ввода. Размеры поля вычисляются стилем с помощью SC_SpinBoxEditField.

Ниже приведён древовидный вид структуры стилей для полей ввода с кнопками. Не требуется, чтобы стиль использовал примитив панели кнопок для отрисовки фонов индикаторов. Ниже дерева показано изображение с подэлементами в QSpinBox в стиле Java.

В QStyleOptionSpinBox, который является стилем для полей ввода с кнопками, можно задать следующие состояния для поля ввода:

Состояние Устанавливается, когда
State_Sunken Устанавливается, если одна из подконтролей CC_SpinUp или CC_SpinDown нажата мышкой.

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

Свойство Функция
frame Булево значение, которое true если поле ввода с кнопками должно рисовать рамку.
buttonSymbols Значение перечисления ButtonSymbols, которое определяет символ на кнопках вверх/вниз.
stepEnabled Значение перечисления StepEnabled, указывающее, какие кнопки поля ввода с кнопками нажаты.

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

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

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

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

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

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

Комбо-бокс

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

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

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

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

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

Другие члены стилизованных опций:

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

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

При вычислении размера подсказки QGroupBox извлекает три пиксельных метрики из стиля: PM_IndicatorWidth, PM_CheckBoxLabelSpacing, и PM_IndicatorHeight. QGroupBox имеет следующую древовидную структуру элементов стиля:

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

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

Стилевые опции для групповых рамок — QStyleOptionGroupBox. Можно задать следующие состояния:

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

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

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

Разделители

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

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

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

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

Индикатор процесса

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Меню

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

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

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

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

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

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

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

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

Панель меню

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

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

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

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

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

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

Заголовки элементов

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

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

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

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

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

Индикаторы ветвей дерева

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

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

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

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

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

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

PM_SmallIconSize для подсказок размера.

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

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

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

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

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

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

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

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

Опция стиля маркера размера, QStyleOptionSizeGrip, помимо общих членов из QStyleOption имеет один член:

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

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

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

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

Опция стиля для резиновых полос — QStyleOptionRubberBand. Ее члены:

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

Виджеты доков

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

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

Опция стиля — QStyleOptionDockWidget:

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

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

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

Spec-Zone.ru

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