Оглавление
Стили и виджеты, поддерживающие стилиСтили (классы, унаследованные от QStyle) рисуют от имени виджетов и инкапсулируют внешний вид и поведение графического интерфейса пользователя. Класс QStyle - это абстрактный базовый класс, инкапсулирующий внешний вид и поведение элементов графического интерфейса пользователя. Встроенные виджеты Qt используют его для выполнения почти всех операций рисования, гарантируя сходство с аналогичными родными виджетами. В Qt встроено несколько стилей (например, стили windows и motif). Другие стили доступны только на определенных платформах (например, стиль windows XP). Пользовательские стили доступны как подключаемые модули (plugins) или через создание в приложении экземпляра класса стиля и его настройку с помощью QApplication::setStyle(). Чтобы реализовать новый стиль создайте наследника одного из имеющихся стилей Qt - наиболее похожего на создаваемый вами стиль - и переопределите несколько виртуальных функций. Поскольку этот процесс несколько запутан, мы предоставляем этот обзор. Мы дадим пошаговый разбор применения стиля к отдельным виджетам Qt. Мы рассмотрим виртуальные функции, переменные-члены и перечисления класса QStyle. Ту часть данного документа, что не имеет отношения к применению стиля к отдельным виджетам, следует читать последовательно, поскольку последующие разделы имеют тенденцию зависеть от предыдущих. Описание виджетов может быть использовано как руководство по реализации стиля. Однако в отдельных случаях вам может понадобиться обратиться за справкой к исходному коду Qt. Последовательность в процессе создания стиля (styling process) должна проясниться после прочтения этого документа, что поможет в локализации существенного (relevant) кода. Для разработки виджетов, поддерживающих стили (т.е. виджетов, которые согласовываются со стилем, в котором отрисовываются), вам необходимо отрисовывать их используя текущий стиль. Данный документ покажет как виджеты отрисовывают себя и какие возможности им предоставляет стиль. Классы для применения стилей к виджетамЭти классы используются для настройки внешнего вида и стиля приложения.
Реализация QStyleAPI QStyle содержит отрисовывающие виджеты функции, статические вспомогательные функции (static helper functions) для общих и сложных задач (например, вычисления позиций рукояток ползунка) и функций для разнообразных вычислений, необходимых во время отрисовки (например, для вычисления предпочитаемых размеров виджетов). Стиль также помогает некоторым виджетам размещать свое содержимое. В дополнение, он создает палитру QPalette, содержащую несколько объектов кисти QBrush для отрисовки. QStyle отрисовывает графические элементы; элемент - это виджет или часть виджета такая, как кромка кнопки, рамка окна или полоса прокрутки. Большинство функций рисования принимают четыре аргумента:
Когда виджет запрашивает у стиля отрисовку элемента, он предоставляет стиль с опцией стиля QStyleOption, который является классом, содержащим необходимую для отрисовки информацию. Благодаря QStyleOption, QStyle может выполнять отрисовку виджетов в любом месте кода без установления связи с виджетом. Это делает возможным использование функций отрисовки QStyle'а на любом устройстве рисования, т.е., вы можете нарисовать выпадающий список на любом виджете, а не только на QComboBox. Виджет передается в последнем аргументе в том случае, если стиль нуждается в нем для создания специальных эффектов (таких как анимированные кнопки по умолчанию в Mac OS X), но это не обязательно. В данном разделе мы будем рассматривать элементы стиля, опции стиля и функции QStyle. В заключение, мы опишем использование палитры. Элементы в представлениях элементов Qt отрисовываются делегатами. Заголовки представлений элементов тем не менее отрисовываются стилем. Делегат Qt по умолчанию, QStyledItemDelegate, отрисовывет свои элементы частично посредством текущего стиля; он отрисовывает индикаторы флажков и вычисляет ограничивающие прямоугольники для элементов, из которых состоит элемент виджета. В данном документе мы опишем только как реализовать подкласс QStyle. Если вы хотите добавить поддержку других типов данных, отличных от поддерживаемых QStyledItemDelegate, то вы должны реализовать пользовательский делегат. Обратите внимание на то, что делегаты должны быть установлены программно для каждого отдельного виджета (т.е., стандартные делегаты не могут быть предоставлены как подключаемые модули). Элементы стиляЭлемент стиля - графическая часть графического интерфейса пользователя. Виджет состоит из иерархии (или дерева) элементов стиля. Например, когда стиль получает запрос на отрисовку кнопки (например, из QPushButton), он отрисовывает метку (текст и пиктограмму), кромку кнопки и прямоугольник фокуса. Кромка кнопки, в свою очередь, состоит из рамки вокруг кромки и двух других элементов, которые будут отображаться позднее. Ниже приведена схематичная иллюстрация дерева элементов кнопки. Мы увидим реальное дерево для QPushButton когда перейдем к отдельным виджетам. Виджеты необязательно отрисовывать запрашивая стиль для отрисовки только одного элемента. Виджеты могут сделать несколько вызовов стиля для отрисовки разных элементов. Примером является QTabWidget, который отрисовывает свои вкладки и фреймы по отдельности. Имеется три типа элементов: простейшие элементы, управляющие элементы и сложные управляющие элементы. Элементы определены в перечислениях ComplexControl, ControlElement и PrimitiveElement. Значения каждого элемента перечисления имеет префикс для обозначения его типа: CC_ - для сложных элементов, CE_ - для элементов управления и PE_ - для простейших элементов. В следующих трех разделах мы увидим что определяют разные элементы и увидим примеры использующих их виджетов. Описание класса QStyle содержит список этих элементов и их роли в стилевом оформлении (styling) виджетов. Мы увидим как их использовать когда применим стиль к отдельным виджетам. Простейшие элементыПростейшие элементы - это общие и часто используемые несколькими виджетами элементы графического интерфейса пользователя. Примером таких элементов являются фреймы, кромки кнопок и стрелки для окошек счетчиков, полосы прокрутки и выпадающие списки. Простейшие элементы не могут существовать сами по себе: они всегда являются частью более крупных структурных компонентов. Они не принимают участия во взаимодействии с пользователем, но являются пассивными украшениями графического интерфейса пользователя. Элементы управленияЭлемент управления выполняет действие или отображает информацию пользователю. Примеры элементов управления - это кнопки, флажки и заголовки секций в табличных и древовидных представлениях. Элементы управления необязательно являются завершенными виджетами как, например, кнопки, но могут быть также частью виджетов, например, вкладки набора вкладок (tab bar) и ползунки полосы прокрутки. Они отличаются от простейших элементов тем, что не являются пассивными, но выполняют функцию по взаимодействию с пользователем. Элементы управления, состоящие из нескольких элементов, часто используют стиль для вычисления ограничивающих прямоугольников элементов. Доступные субэлементы определены в перечислении SubElement. Это перечисление используется только для вычисления ограничивающих прямоугольников, а субэлементы как таковые не являются графическими элементами, способными быть отрисованными, как простейшие, управляющие и сложные элементы. Сложные элементы управленияСложные элементы управления содержат субэлементы управления (sub controls). Поведение сложных элементов управления по-разному зависит от того, где пользователь воздействовал на них с помощью мыши и какую клавишу нажал. Это зависит от субэлемента управления (или нескольких), над которыми находится мышь или получено нажатие кнопки мыши. Примеры сложных элементов управления - полосы прокрутки и выпадающие списки. С полосой прокрутки вы можете использовать мышь для перемещения ползунка и нажимать на кнопки сдвига на одну строку вверх и вниз. Доступные субэлементы управления определены в перечислении SubControl. В дополнение к отрисовке, стиль должен предоставить виджетам информацию о том, в каком субэлементе (или нескольких) было нажата кнопка мыши. Например, QScrollBar нуждается в знании того, нажал ли пользователь на ползунок, бороздку (groove) ползунка или одну из кнопок. Обратите внимание на то, что субэлементы управления - это не то же самое, что управляющие элементы, описываемые в предыдущем разделе. Вы не можете использовать стиль для отрисовки субэлементов управления; стиль будет только вычислять ограничивающий прямоугольник, в котором субэлементы управления должны быть отрисованы. Тем не менее, обычно сложные элементы управления используют элементы управления и простейшие элементы для отрисовки своих субэлементов управления, что часто используется встроенными стилями Qt, а также стилем Java. Например, стиль Java использует PE_IndicatorCheckBox для отрисовки флажка в групповых рамках (которые являются субэлементами управления CC_GroupBox). Некоторые субэлементы управления имеют эквивалентный элемент управления, например, ползунок полосы прокрутки (SC_SCrollBarSlider и CE_ScrollBarSlider). Другие задачи QStyleКак уже упоминалось, элементы стиля и виджеты используют стили для вычисления ограничивающих прямоугольников субэлементов и субэлементов управления, и пиксельные метрики (pixel metrics), которые являются зависящими от стиля размерами экрана в пикселях, для масштабирования во время отрисовки. Доступные прямоугольники и пиксельные метрики в QStyle представляются тремя перечислениями: SubElement, SubControl и PixelMetric. Значения перечислений можно легко идентифицировать, т.к. они начинаются на SE_, SC_ и PM_. Стиль содержит также набор подсказок для стиля (style hints), которые представляют собой значения из перечисления 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 flags) имеют различное значение в зависимости от виджета, но остальные - общие для всех виджетов, подобно 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 QWindowsStyle { Q_OBJECT public: CustomStyle() ~CustomStyle() {} void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const; }; void CustomStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { if (element == PE_IndicatorSpinUp || element == PE_IndicatorSpinDown) { QPolygon points(3); int x = option->rect.x(); int y = option->rect.y(); int w = option->rect.width() / 2; int h = option->rect.height() / 2; x += (option->rect.width() - w) / 2; y += (option->rect.height() - h) / 2; if (element == PE_IndicatorSpinUp) { points[0] = QPoint(x, y + h); points[1] = QPoint(x + w, y + h); points[2] = QPoint(x + w / 2, y); } else { // PE_SpinBoxDown points[0] = QPoint(x, y); points[1] = QPoint(x + w, y); points[2] = QPoint(x + w / 2, y + h); } if (option->state & State_Enabled) { painter->setPen(option->palette.mid().color()); painter->setBrush(option->palette.buttonText()); } else { painter->setPen(option->palette.buttonText().color()); painter->setBrush(option->palette.mid()); } painter->drawPolygon(points); } else { QWindowsStyle::drawPrimitive(element, option, painter, widget); } } Функции QStyleКласс QStyle определяет три функции для отрисовки простейших элементов, элементов управления и сложных элементов управления: drawPrimitive(), drawControl() и drawComplexControl(). Функции принимают следующие параметры:
Не все виджеты отправляют указатель на себя. Если опция стиля, посылаемая в функцию, не содержит необходимую вам информацию, проверьте реализацию виджета чтобы посмотреть, отправляет ли он указатель на самого себя. Класс QStyle также предоставляет вспомогательные функции, которые используются при отрисовке элементов. Функция drawItemText() отрисовывает текст внутри заданного прямоугольника и принимает палитру QPalette в качестве параметра. Функция drawItemPixmap() помогает выровнять растровое изображение (pixmap) внутри заданного ограничивающего прямоугольника. Другие функции QStyle выполняют различные вычисления для функций отрисовки. Виджеты также используют эти функции для вычисления подсказок размеров (size hints), а также для вычислений ограничивающих прямоугольников, если они отрисовывают несколько элементов стиля самостоятельно. Также как и функции отрисовки элементов вспомогательные функции принимают несколько аргументов.
QStyle также имеет функции polish() и unpolish(). Перед тем, как будут отображены все виджеты отправляются в функцию polish() и в функцию unpolish() - когда они невидимы. Вы также можете использовать эти функции для установки атрибутов виджетов или выполнения другой работы, которая требуется вашему стилю. Например, если необходимо узнать когда мышь нависнет над виджетом, вам необходимо установить атрибут виджета WA_Hover. Тогда флаг состояния State_MouseOver будет установлен в опциях стиля виджета. QStyle обладает также несколькими статическими вспомогательными функциями, которые выполняют некоторые общие и трудные задачи. Они могут вычислить позицию ползунка из значения ползунка и трансформировать прямоугольники и отрисовать текст с учетом обратных компоновок (reverse layouts); за подробностями обращайтесь к документации класса QStyle. Обычный подход, когда переопределяются виртуальные функции QStyle, работает на элементах, которые отличаются от суперкласса; для остальных элементов вы можете просто использовать реализацию суперкласса. ПалитраКаждый стиль предоставляет цвет - то есть, QBrush - палитру, которая будет использована для отрисовки виджетов. Для разных состояний виджета имеется свой набор цветов (QPalette::ColorGroup): активный (виджеты в окне, которое находится в фокусе клавиатуры), неактивный (виджеты используются для других окон) и отключенный (виджеты, которые отключены). Состояния можно узнать опросив флаги состояния State_Active и State_Enabled. Каждый набор содержит цвет для определенных ролей, задаваемых перечислением QPalette::ColorRole. Роли описывают в каких ситуациях цвета будут использованы (например, для отрисовки фона виджета, текста или кнопок). Как роли цвета (color roles) используются определяется стилем. Например, если стиль использует градиенты, то для создания градиента можно использовать цвет из палитры и сделать его темнее или светлее с помощью QColor::darker() и QColor::lighter(). В общем, если вам нужна кисть, которой нет в палитре, попробуйте получить ее из имеющейся. QPalette, которая предоставляет палитру, хранит цвета для различных состояний виджетов и ролей цвета. standardPalette() возвращает палитру для стиля. Стандартная палитра не устанавливается автоматически когда новый стиль устанавливается на приложение (QApplication::setStyle()) или виджет (QWidget::setStyle()), так что вы должны установить палитру самостоятельно с помощью (QApplication::setPalette()) или (QWidget::setPalette()). Не рекомендуется жестко кодировать цвета, поскольку приложения и отдельные виджеты могут устанавливать собственные палитры и использовать палитры стилей для отрисовки. Обратите внимание на то, что ни один виджет Qt не устанавливает свою собственную палитру. Стиль java жестко кодирует некоторые цвета, но автор его внешнего вида оставил это в забвении. Конечно, это не значит что стиль будет выглядеть хорошо с любой палитрой. Проблемы реализацииПри реализации стилей имеется несколько проблем, требующих рассмотрения. Здесь мы дадим несколько намеков и совет по реализации. При реализации стилей необходимо просмотреть код виджетов и код базового класса и его предков. Это связано с тем, что виджеты используют стиль по-разному, поскольку реализация виртуальных функций в разных стилях может оказывать влияние на состояние отрисовки (например, изменение состояния QPainter без восстановления исходного состояния и отрисовка некоторых элементов без использования подходящих пиксельных метрик и субэлементов). Рекомендуется, чтобы стили не меняли предлагаемый с помощью функции QStyle::sizeFromContents() размер виджетов, но позволяли реализации QCommonStyle обрабатывать его. Если необходимо внести изменения, попытайтесь сделать их минимальными; разработка приложения усложнится, если для разных стилей компоновка виджетов значительно различается. Мы рекомендуем использовать непосредственно QPainter для отрисовки, т.е. не использовать пиксельные карты или изображения. Это облегчит согласование стиля с палитрой (хотя вы можете установить собственную таблицу цветов в QImage с помощью setColorTable()). Разумеется, можно отрисовывать элементы и без использования стиля для отрисовки субэлементов как спроектировано Qt. Это не одобряется, поскольку пользовательские виджеты могут зависеть от корректной реализации этих субэлементов. Критический разбор виджетов покажет как Qt использует субэлементы. Стиль JavaМы реализовали стиль, имеющий сходство с внешним видом и поведением по умолчанию стиля Java (известный до этого как Metal). Мы сделали это, так как это было сравнительно просто реализовать и мы хотели создать стиль для данного обзорного документа. Для сохранения его простым и негромоздким, мы кое-что упростили в стиле, но с Qt безусловно отлично можно сделать точную копию стиля. Однако, конкретных планов по реализации стиля как части Qt нет. В данном разделе мы рассмотрим некоторые проблемы реализации. В заключение, мы рассмотрим завершенный пример применения стиля Java к виджету. Мы продолжим использовать стиль java на всем протяжении документа в примерах и изображениях виджетов. Сама по себе реализация немного запутана и не предназначена для прочтения вами. Проектирование и реализацияПервым шагом при разработке стиля является выбор базового класса. Мы решили создать подкласс от QWindowsStyle. Данный класс реализует большую часть функциональности, необходимой для выполнения действительного рисования. К тому же, стили windows и java разделяют компоновку субэлементов управления для нескольких сложных элементов управления (что значительно уменьшает объем кода). Стиль реализован в одном классе. Мы сделали так поскольку нашли удобным хранить весь код в одном файле. Кроме того, это выгодно в точки зрения оптимизации, так как мы создаем меньше объектов. Мы также оставим количество функций минимальным, используя переключатели (switches) для идентификации элемента, который отрисовывается функциями. Результатом этого являются большие функции, но так как мы разделили код для каждого элемента по переключателям, читать код будет легко. Ограничения и отличия от JavaМы не реализовали полностью каждый элемент из стиля Java. Таким образом, мы уменьшили объем и сложность кода. Вообще, стиль планировался как практический пример для данного обзорного документа по стилям, а не как часть Qt. Не для всех виджетов реализованы все состояния. Это сделано для часто используемых состояний, например, State_Disabled. Каждое состояние, однако, реализовано как минимум для одного виджета. Мы реализовали отметки (ticks) только ниже ползунка. Плоские кнопки (flat push) также пропущены. Мы не обрабатываем случай, когда строки заголовков и заголовки присоединяемых окон становятся меньше содержимого, а просто отрисовываем субэлементы управления один за другим. Мы не пытались эмулировать шрифты Java. Java и Qt используют очень разные механизмы шрифтов (font engines), поэтому мы не рассматривали его ценность, поскольку используем стиль лишь в качестве примера в этом обзоре. Мы жестко запрограммировали цвета (QPalette не использовалась) для линейных градиентов, используемых, например, для кромок кнопок, панелей инструментов и флажков. Так сделано потому, что палитра Java не может синтезировать такие цвета. В любом случае Java не изменяет эти цвета, основанные на цветовой группе виджета или роли (они не зависят от палитры), так что при любых обстоятельствах это не является проблемой. Это виджеты Qt, к которым применен стиль. Некоторых виджетов нет в Java, например, QToolBox. Другие содержат элементы, которые отсутствуют у виджетов Java. Примером последнего является виджет-дерево JTree из Java, у которого нет заголовка. Стиль не обрабатывает обратные компоновки (reverse layouts). Мы предполагаем, что направление компоновки (layout direction) - слева на право. QWindowsStyle обрабатывает обратные виджеты (reverse widgets); если мы реализовали обратные компоновки, то необходимо обновить виджеты, у которых мы изменяли положение субэлементов или изменяли выравнивание текста в метках. Конструирование флажков в стиле JavaВ качестве примера, мы рассмотрим конструирование флажков в стиле java. Мы опишем весь процесс и приведем весь код как стиля java, так и связанных с ним классов Qt. В оставшейся части документа мы не будем рассматривать исходный код отдельных виджетов. Надеемся, это даст вам понимание того как нужно разыскивать в коде конкретные детали реализации; большинство виджетов придерживаются структуры, схожей с флажками. Мы отредактировали код QCommonStyle, удалив из него кое-что не относящееся напрямую к конструированию флажков. Начнем с рассмотрения того, как 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 равно true, если пользователь нажал на квадратик; для флажка это будет верно вне зависимости от того, отмечен квадратик или нет. Состояние State_NoChange устанавливается, когда имеется флажок с тремя состояниями и он частично отмечен. State_On устанавливается, если квадратик отмечен, а State_Off - если не отмечен. State_MouseOver устанавливается, если мышь наведена на флажок и атрибут виджета Qt::WA_Hover установлен - вы устанавливаете это в QStyle::polish(). В дополнение, опция стиля также содержит текст, пиктограмму и размер пиктограммы кнопки. initFrom() устанавливает опцию стиля с атрибутами, общими для всех виджетов. Приведем ее реализацию: state = QStyle::State_None; if (widget->isEnabled()) state |= QStyle::State_Enabled; if (widget->hasFocus()) state |= QStyle::State_HasFocus; if (widget->window()->testAttribute(Qt::WA_KeyboardFocusChange)) state |= QStyle::State_KeyboardFocusChange; if (widget->underMouse()) state |= QStyle::State_MouseOver; if (widget->window()->isActiveWindow()) state |= QStyle::State_Active; #ifdef Q_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 - метрики шрифта, используемые виджетом. Мы даем изображение флажка и опции стиля соответствующую ему. Флажок выше имеет следующие флаги состояния в опции стиля:
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() рисует текст, принимая выравнивание, направление компоновки и мнемонику в учетной записи (account). Для отрисовки текста правильным цветом также используется палитра. При отрисовке меток часто получается что-то запутанное. К счастью, обычно эта ситуация обрабатывается базовым классом. Стиль Java реализует собственные надписи в кнопках, так как Java - в отличие от Windows - также центрирует содержимое кнопки при наличии в ней пиктограммы. Вы можете изучить эту реализацию, если вам необходим пример переопределения отрисовки метки. Взглянем на реализацию в стиле 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; Сначала мы сохраняем состояние рисовальщика (painter). Это не всегда необходимо, но в данном случае QWindowsStyle нужен рисовальщик в том же состояние, в каком он был когда был вызван 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, так как они могут понадобиться пользовательским виджетам (хотя это не исключает предоставление реализаций QWindowsStyle). Нижеперечисленное предоставлено для каждого виджета:
Дерево элементов содержит простейшие элементы, элементы управления и сложные элементы управления стиля. Сделав нисходящий обход дерева элементов, вы получите последовательность, в которой элементы должны быть отрисованы. В узлах мы написали прямоугольники субэлементов, субэлементов управления и пиксельные метрики, которые учитываются во время отрисовки элемента узла. Наш подход к процессу применения стиля сосредоточен на отрисовке виджетов. Вычисления используемых во время отрисовки прямоугольников субэлементов, субэлементов управления и пиксельных метрик лишь перечислены как содержимое деревьев элементов. Обратите внимание на то, что имеются прямоугольники и пиксельные метрики, которые используются только виджетами. Это оставляет такие вычисления за пределами критического разбора. Например, функции subControlRect() и sizeFromContents() часто вызывают subElementRect() для вычисления своих ограничивающих прямоугольников. Мы также можем нарисовать деревья для них. Однако то, как выполняются эти вычисления полностью возложено на отдельные стили, а они не следуют определенной структуре (Qt не навязывает определенную структуру). Тем не менее, вы должны убедиться, что используете подходящие пиксельные метрики. Для ограничения размера документа мы решили не включать деревья или описания вычислений, выполняемых стилем Java (или любым другим). При рассмотрении деревьев вас может озадачить использование различных пиксельных метрик, прямоугольников субэлементов и прямоугольников субэлементов управления. Если вы испытываете сомнения после прочтения описаний перечислений QStyle, мы советуем вам изучить реализации QCommonStyle и QWindowsStyle. Некоторые из выделенных на изображениях ограничивающих прямоугольников виджетов одинаковы. Причины в том, что некоторые элементы отрисовывают фон в то время, как другие отрисовывают рамки и метки. Если сомневаетесь, проверьте описание каждого элемента в QStyle. Также, некоторые элементы относятся к компоновке, т.е., решают где отрисовывать другие элементы. Общие свойства виджетовНекоторые состояния и переменные являются общими для всех виджетов. Они устанавливаются с помощью QStyleOption::initFrom(). Не все элементы используют эту функцию; это виджеты, создающие опции стиля, а для некоторых элементов информация из initFrom() не является необходимой. Таблица общих состояний:
Другими общими членами для виджетов являются:
Сложные опции стиля (классы, наследуемые от QStyleOptionComplex) используются для сложных элементов стиля разделяя две переменных: subControls и activeSubControls. Обе переменных являются объединением по ИЛИ переменных из QStyle::SubControl. Они отображают из каких субэлементов управления состоит сложный элемент управления и какие из этих элементов управления сейчас активны. Как упоминалось, стиль вычисляет размеры содержимого виджета, из подсказки (size hints) которых виджеты вычисляют свои размеры. Кроме того, сложные элементы управления также используют стиль для проверки над каким субэлементом управления находится мышь. Руководство по виджетамБез дальнейших задержек мы представляем критический обзор виджетов; каждому виджету отведен свой подраздел. КнопкиНиже показана структура стиля для кнопок. Сделав нисходящий обход дерева, вы получите последовательность в которой элементы должны быть отрисованы. Компоновка кнопок, с относительными границами элемента, изменяется от стиля к стилю. Из-за чего показывать ее концептуальные изображения становится сложно. Также элементы могут - даже запланированы - иметь одинаковые границы; PE_PushButtonBevel, например, используется в QCommonStyle для отрисовки элементов, которые его содержат: PE_FrameDefaultButton, PE_FrameButtonBevel и PE_PanelButtonCommand, которые все имеют одни и те же границы в обычном и windows стилях. PE_PushButtonBevel также отвечает за отрисовку индикатора меню (QCommonStyle отрисовывает PE_IndicatorArrowDown). Ниже приведено изображение кнопки в стиле java, на котором показаны ограничивающие прямоугольники элементов. Для ограничивающих прямоугольников на изображении используются разные цвета; другого назначения они не имеют. Это справедливо также для похожих изображений для других виджетов. Стиль java, так же как и другие реализованные в Qt стили, не использует PE_FrameButtonBevel. Обычно кнопка с PE_DefaultFrame корректирует прямоугольник PE_PanelButtonCommand с помощью PM_ButtonDefaultIndicator. Корректируя прямоугольник rect с помощью PM_DefaultFrameWidth находят CE_PushButtonLabel. Теперь рассмотрим опцию стиля для кнопок - QStyleOptionButton. Таблица состояний, которые QPushButton может установить на опцию стиля, следующая:
Остальные члены QStyleOptionButton:
Флажки и радиокнопкиСтруктура радиокнопок и флажков одинакова. Покажем структуру, используя элемент QCheckBox и имена пиксельных метрик: QStyleOptionButton используется как опция стиля и для радиокнопок, и для флажков. Сначала представим таблицу состояний, которые можно установить в опции:
Таблицу с остальными членами QStyleOptionButtonClass смотрите в Push Buttons. ВкладкиВ Qt QTabBar использует стиль для отрисовки своих вкладок. Вкладки существуют либо в QTabWidget, в котором содержится QTabBar, или как изолированная вкладка. Если панель не является частью виджета со вкладками, она отрисовывается своей основой (base). QTabBar расположена вне вкладок, поэтому стиль не контролирует размещение панели. Тем не менее, во время размещения своих вкладок панель запрашивает у стиля PM_TabBarTabHSpace и PM_TabBarTabVSpace, которые являются добавочной шириной и высотой к минимальным размерам метки вкладки (пиктограмма и текст) панели вкладок. Стиль может также продолжать воздействовать на размеры вкладки до того как она будет размещена, пока панель вкладок не запросит CT_TabBarTab. Ограничивающий прямоугольник панели выбирается виджетом со вкладками когда она является частью виджета (принимая во внимание CT_TabBarTab). Панель вкладок несет ответственность за отрисовку кнопок, которые появляются на ней когда все вкладки не помещаются. Их расположение не управляется стилем, но кнопки являются объектами QToolButton и, следовательно, отрисовываются стилем. Вот структура стиля для QTabWidget и QTabBar: Пунктирная линия означает, что панель вкладок содержится в QTabWidget, но виджет ее не отрисовывает самостоятельно, что когда QTabBar не является частью виджета со вкладками он отрисовывает только свою базовую линию (base line), и что панель вкладок содержит две инструментальные кнопки, которые прокручивают панель когда все вкладки на ней не помещаются; смотрите их дерево элементов в Инструментальных кнопках. Также обратите внимание на то, что так как кнопки являются дочерними по отношению к панели вкладок, они отрисовываются после отрисовки панели. Ограничивающие прямоугольники вкладок перекрывают основу с помощью PM_TabBarBaseOverlap. Вот виджет со вкладками в стиле java: В стиле java (а также в стиле windows), форма и метка панели вкладок имеют те же ограничивающие прямоугольники, что и CE_TabBarTab. Уведомляем, что вкладки перекрываются рамкой виджета со вкладками. Основа панели вкладок (если она отрисовывается) - это область, где вкладки и рамка перекрываются. Опция стиля для вкладок (QStyleOptionTab) содержит необходимую для отрисовки вкладок информацию. Опция содержит позицию вкладки в панели вкладок, позицию выделенной вкладки, форму вкладки, текст и пиктограмму. Начиная с Qt 4.1 опция будет приводиться к QStyleOptionTabV2, которая также содержит размеры пиктограмм. Поскольку вкладки в стиле java не перекрываются, мы также представляем картинку виджета со вкладками в стиле windows. Обратите внимание на то, что если вы хотите перекрытия вкладок по горизонтали, то вы можете это получить отрисовывая вкладки в CE_TabBarTabShape; ограничивающие прямоугольники вкладок не будут изменяться панелью вкладок. Вкладки отрисовываются слева направо в северной (north) форме панели вкладок, сверху вниз в восточной (east) форме панели вкладок, и т.д. Выделенная вкладка отрисовывается последней, поэтому ее легче рисовать над другими вкладками (если они больше). Таблица состояний, в которые можно установить панель вкладок, следующая:
Обращаем внимание, что отдельные вкладки могут быть отключены даже если панель вкладок включена. Вкладка включена, если включена панель вкладок. Вот таблица членов QStyleOptionTabV2:
Рамка виджетов со вкладками использует в качестве опции стиля QStyleOptionTabWidgetFrame. Здесь мы перечислим ее члены. Она не имеет других состояний, кроме тех что устанавливаются общими флагами.
Полосы прокруткиВот структура стиля для полос прокрутки: QScrollBar просто создает свою опцию стиля и затем отрисовывает CC_ScrollBar. Некоторые стили рисуют фон добавленной страницы и субстраницы с помощью PE_PanelButtonBevel, а также используют индикаторные стрелки (indicator arrows) для отрисовки стрелок во вложенной и предыдущей строке индикаторов; мы не включили их в дерево поскольку их использование зависят от индивидуального стиля. PM_MaximumDragDistance стиля - максимальное расстояние в пикселях, на которое мышь может переместиться от границ полосы прокрутки и по-прежнему передвигать регулятор. Вот изображение полосы прокрутки в стиле java: Вы можете обратить внимание на то, что наша полоса прокрутки немного отличается от такой же в Java, поскольку имеет два индикатора прокрутки на строку вверх (line up indicators). Мы сделали это чтобы показать как вы можете иметь два различных ограничивающих прямоугольника для одного субэлемента управления. Полоса прокрутки - это пример виджета, который полностью реализован в стиле java - ни QWindowsStyle, ни QCommonStyle не использовались при рисовании. Посмотрим на разные состояния полосы прокрутки, которые можно установить на опцию стиля:
Опцией стиля QScrollBar является QStyleOptionSlider. Его члены перечислены в следующей таблице. Опция используется всеми объектами QAbstractSlider; здесь мы опишем только члены, относящиеся к полосам прокрутки.
ПолзункиПри вычислении подсказки размера ползунков, из стиля запрашиваются PM_SliderTickness и PM_SliderLength. Как и с полосами прокрутки QSlider позволяет пользователю передвигать только регулятор, если мышь находится в пределах PM_MaximumDragDistance от границ ползунка. При отрисовывании себя ползунок создает опцию стиля и вызывает drawComplexControl() с CC_Slider: Мы также показываем изображение ползунка в стиле java. Мы покажем ограничивающие прямоугольники субэлементов управления, так как все рисование выполняется в CC_Slider. QSlider использует QStyleOptionSlider, как поступают все объекты QAbstractSlider. Представляем таблицу членов, оказывающих действие на QSlider:
Вы должны обратить внимание на то, что ползунки не используют направление для обратных компоновок (reverse layouts); они используют upsideDown. Окошки счетчикаКогда QSpinBox отрисовывает себя, он создает QStyleOptionSpinBox и запрашивает стиль для отрисовки CC_SpinBox. Поле редактирования - однострочный редактор, который является дочерним для окошка счетчика. Размеры поля вычисляются стилем с помощью SC_SpinBoxEditField. Далее представлено дерево стиля для окошка счетчика. Использование стилем примитива панели кнопки не является необходимым для рисования фона индикатора. На рисунке ниже показано дерево с субэлементами QSpinBox'а в стиле java. QStyleOptionSpinBox - опция стиля для окошек счетчика. Она может установить окошко счетчика в следующие состояния:
Остальные члены опции стиля окошка счетчика:
Строка заголовкаСложный элемент управления строки заголовка, CC_TitleBar, используется для отрисовки строки заголовка внутренних окон в QMdiArea. Обычно он состоит из заголовка окна и кнопок закрытия, системного меню, сворачивания и разворачивания окна. Некоторые стили также предоставляют кнопки затенения окна и кнопку контекстно чувствительной помощи. Строка заголовка отрисовывается в CC_TitleBar без использования субэлементов. Как отрисовывать свои кнопки каждый стиль решает самостоятельно, но имеются стандартные пиксельные карты для кнопок, которые стиль должен предоставить. На картинке выше строки заголовка в стиле java мы показали ограничивающие прямоугольники субэлементов управления, поддерживаемых стилем java (все они отрисовываются со стандартными пиксельными картами). Обычно для отрисовки фона кнопок используется PE_PanelButtonTool, но это неправильно. Опцией стиля для строк заголовка является QStyleOptionTitleBar. Ее элементы:
Выпадающий списокQComboBox использует стиль для отрисовки кнопки и метки нередактируемых комбинированных списков выбора с помощью CC_ComboBox и CE_ComboBoxLabel. Список, который всплывает, когда пользователь щелкает мышью на выпадающем списке, отрисовывается делегатом, который мы не рассматриваем в данном обзоре. Тем не менее вы можете использовать стиль чтобы управлять размерами и положением выпадающего списка с помощью субэлемента SC_ComboBoxListBoxPopup. Стиль также решает, где для редактируемых комбинированных списков будет поле редактирования с помощью SC_ComboBoxEditField; поле редактирование - QLineEdit, дочерний по отношению к выпадающему списку. На картинке выше показан выпадающий список в стиле java, мы выделили его субэлементы и прямоугольники субэлементов: Выпадающие списки в Java не используют прямоугольник фокуса; они изменяют цвет фона, когда получают фокус. SC_ComboBoxEdit field используется QComboBox как для вычисления размеров поля редактирования, так и стилем для вычисления размеров метки выпадающего списка. Опцией стиля выпадающих списков является QStyleOptionComboBox. Он может быть установлен в следующие состояния:
Остальные члены опции стиля:
Групповые рамкиКогда вычисляются подсказки размеров, QGroupBox извлекает из стиля три пиксельных метрики: PM_IndicatorWidth, PM_CheckBoxLabelSpacing и PM_IndicatorHeight. QGroupBox имеет следующее дерево элементов стиля: Qt не налагает ограничений на то, как флажок будет отрисовываться; стиль java отрисовывает его с помощью CE_IndicatorCheckBox. Полное дерево смотрите в статье Флажки и радиокнопки. Мы также предоставим изображение виджета с субэлементами управления и нарисованными прямоугольниками субэлементов управления: Опцией стиля для групповой рамки является QStyleOptionGroupBox. Могут быть установлены следующие состояния:
Остальные члены QStyleOptionGroupBox:
РазделителиПоскольку структура разделителей проста и не содержит никаких субэлементов, мы не стали включать изображение разделителей. CE_Splitter не использует никаких других элементов или метрик. Для своей опции стиля разделители используют базовый класс QStyleOption. Она может установить следующие флаги состояния:
QSplitter не использует initFrom() для установки своей опции; она самостоятельно устанавливает флаги State_MouseOver и State_Disabled. Индикатор выполненияЭлемент CE_ProgressBar используется классом QProgressBar и он является лишь элементом, используемым данным виджетом. Начнем наш обзор структуры стиля: Здесь представлен индикатор выполнения в стиле windows (ограничивающие прямоугольники для стиля java такие же): Опцией стиля для QProgressBar является QStyleOptionProgressBarV2. Индикатор выполнения не устанавливает никаких флагов состояния, но это делают другие члены опции:
Инструментальные кнопкиИнструментальные кнопки существуют либо автономно либо как часть панелей инструментов. Отрисовываются они одинаково. QToolButton отрисовывает только один элемент стиля: CC_ToolButton. Поскольку вам может это понадобиться (во всяком случае, если вы читали данный документ последовательно), мы приведем дерево структуры стиля виджета: Обратите внимание на то, что PE_FrameButtonTool и PE_IndicatorArrowDown включены в состав дерева, поскольку стиль java отрисовывает их, но если вам нужно, их можно спокойно пропустить. Структура может иметь отличия. QWindowsStyle, например, отрисовывает в CE_ToolButton и PE_IndicatorButtonDropDown, и PE_IndicatorArrowDown. Мы также приводим изображение инструментальной кнопки, где выделены ограничивающие прямоугольники субэлементов и субэлементы управления. Вот таблица состояния для инструментальных кнопок:
QStyleOptionToolButton также содержит следующие члены:
Панели инструментовПанели инструментов являются частью каркаса главного окна и взаимодействует с QMainWindow, которому они принадлежат пока строят свою опцию стиля. В главном окне имеется 4 области где могут быть расположены панели инструментов. Они располагаются по четырем сторонам окна (т.е., северной, южной, западной и восточной). Внутри каждой области может быть более чем одна линейка панелей инструментов; линейка состоит из расположенных рядом панелей инструментов с одинаковой ориентацией (вертикальной или горизонтальной). Объекты QToolbar в Qt состоят из трех элементов - CE_ToolBar, PE_IndicatorToolBarHandle и PE_IndicatorToolBarSeparator. QMainWindowLayout вычисляет ограничивающие прямоугольники (т.е., расположение и размеры панелей инструментов и их содержимого). Главное окно также использует sizeHint() элементов в панелях инструментов при вычислении размеров панелей. Вот дерево элементов для QToolBar: Пунктирные линии указывают, что QToolBar сохраняет экземпляр QToolBarLayout и что объекты QToolBarSeparator сохраняется QToolBarLayout'ом. Когда панель инструментов плавающая (т.е., имеет собственное окно) элемент PE_FrameMenu отрисовывается, в остальных случаях QToolbar отрисовывает CE_ToolBar. Вот изображение панели инструментов в стиле java: В качестве опции стиля QToolBarSaparator использует QStyleOption. Она устанавливает флаг State_horizontal если панель инструментов расположена горизонтально. В других случаях используется initFrom(). Опцией стиля для QToolBar является QStyleOptionToolBar. Устанавливается только флаг состояния (помимо общих флагов) State_Horizontal если панель горизонтальная (т.е., в северной или южной области панели инструментов). Переменные-члены опции стиля:
МенюМеню в Qt реализованы в классе QMenu. QMenu сохраняет список действий, которые оно отрисовывает как пункты меню. Когда QMenu получает события рисования, оно вычисляет размеры каждого пункта меню и отрисовывает их по одному с помощью CE_MenuItem. (Пункты меню не имеют разделительного элемента для своих меток (содержимого), поэтому все рисование выполняется в CE_MenuItem.) Меню также рисует рамку с помощью PE_FrameMenu. Оно также рисует CE_MenuScroller если стиль поддерживает прокрутку. CE_MenuTearOff отрисовывается если меню слишком велико для своего ограничивающего прямоугольника. В дерево структуры стиля мы также включили QMenu, поскольку оно также выполняет работу, связанную с созданием стиля. Ограничивающие прямоугольники пунктов меню вычисляются для подсказок размеров меню, а также когда меню отображается или изменяет свои размеры. Элементы CE_MenuScroller и CE_MenuTearOff обрабатываются QCommonStyle и не отображаются до тех пор, пока меню не станет слишком велико чтобы поместиться на экране. PE_FrameMenu отрисовывается только для всплывающих (pop-up) меню. QMenu вычисляет прямоугольники, основываясь на их действиях и вызывая CE_MenuItem и CE_MenuScroller, если стиль поддерживает это. Также часто используется PE_IndicatorCheckBox (вместо использования PE_IndicatorMenuCheckMark) и PE_IndicatorRadioButton для отрисовки пунктов меню, которые можно отмечать; мы не включили их в дерево стиля, поскольку это является необязательным и изменяется от стиля к стилю. Опцией стиля для элементов меню является QStyleOptionMenuItem. Следующие таблицы описывают ее флаги состояния и другие члены.
Установка опции стиля для CE_MenuTearOff и CE_MenuScroller также использует QStyleOptionMenuItem; они только устанавливают переменную menuRect в добавок к общим настройкам опции стиля QStyleOption с помощью initFrom(). Горизонтальная панель меню (Menu Bar)QMenuBar использует стиль для отрисовки каждого пункта меню и пустой области панели. Выпадающие (pull-down) меню сами являются объектами QMenu (смотрите Меню). Дерево элементов стиля для панели меню следующее: Панель и пустая область отрисовываются после элементов меню. Рисовальщик QPainter, который QMenuBar отправляет стилю, располагает ограничивающими прямоугольниками вырезанных (clipped out) пунктов меню (т.е., области отсечения), таким образом вам не нужно беспокоиться об отрисовке над пунктами меню. Пиксельные метрики в QMenuBar используются при вычислении ограничивающих прямоугольников пунктов меню. Для пунктов меню используется QStyleOptionMenuItem. Члены, используемые QMenuBar, описаны в следующей таблице:
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. Представление рисует один заголовок секции в один момент времени, поэтому данные относятся к отрисовываемой секции. Она содержит:
Индикаторы ветвей дереваИндикаторы ветвей в древовидном представлении отрисовываются стилем с помощью PE_IndicatorBranch. Мы понимаем индикаторы здесь как индикаторы, которые описывают отношения узлов в дереве. Общую опцию стиля QStyleOption отправляют в стиль для рисования этих элементов. Различные виды ветвей описываются состояниями. Так как нет отдельной опции стиля мы просто представим таблицу состояний:
Древовидное представление (и виджет-дерево) использует стиль для рисования ветвей (или узлов, если хотите) дерева. QStyleOption используемая стилем для PE_IndicatorBranch, имеет флаги состояния устанавливаемые в зависимости от типа ветви. Так как нет структуры дерева для индикаторов ветвей, мы представим только рисунок дерева в стиле java. Каждое состояние отмечено на рисунке с помощью прямоугольника определенного цвета (т.е., эти прямоугольники не являются ограничивающими прямоугольниками). Все сочетания состояний вы можете узнать из этого рисунка. Панели инструментов (Tool Boxes)PM_SmallIconSize для sizeHint'ов. QToolBox - это контейнер, в котором хранится коллекция виджетов. Для каждого виджета имеется одна вкладка и в каждый момент времени видна только одна из них. Панель инструментов располагает отображаемые компоненты (кнопки панели инструментов и выбранный виджет) в QVBoxLayout. Дерево стиля для панелей инструментов выглядит примерно так: Изображение панели инструментов в стиле Plastique: Все элементы обладают одними и теми же ограничивающими прямоугольниками в стиле так же как и в остальных встроенных стилях Qt. Опцией стиля для панелей инструментов является QStyleOptionToolBox. Она включает в себя текст и пиктограмму, содержащиеся в панели инструментов. Единственным устанавливаемым QToolBox состоянием является State_Sunken, которое устанавливается когда пользователь щелкает по вкладке (tab down) с помощью мыши. Остальные члены QStyleOptionToolBox:
Элемент изменения размеров (Size Grip)Элемент изменения размеров вычисляет свою подсказку размеров с помощью CT_SizeGrip. В настоящее время пиксельная метрика PM_SizeGripSize не используется Qt. Дерево элементов и изображение QSizeGrip в стиле Plastique следующие: Мы показали элемент изменения размеров в нижнем правом углу QMainWindow. Опция стиля элемента изменения размера, QStyleOptionSizeGrip, имеет один член за исключением общих членов из QStyleOption:
Резиновые линии (Rubber Band)Дерево стиля QRubberBand состоит из двух узлов. Представляем изображение окна в стиле Java, перемещаемое в QMdiArea с помощью резиновых линий: Опция стиля для резиновых линий - QStyleOptionRubberBand. Ее члены:
Присоединяемые виджетыПри размещении содержимого присоединяемые виджеты запрашивают у стиля следующие пиксельные метрики: PM_DockWidgetSeparatorExtent, PM_DockWidgetTitleBarButtonMargin, PM_DockWidgetFrameWidth и PM_DockWidgetTitleMargin. Они также вычисляют ограничивающие прямоугольники кнопок отделения (float) и закрытия (close) с помощью SE_DockWidgetCloseButton и SE_DockWidgetFloatButton. Пунктирные линии указывают, что отправитель сохраняет экземпляры класса приемника, указываемого стрелкой (т.е., это элемент стиля не для отрисовки). Присоединяемый виджет рисует PE_frameDockWidget только тогда, когда он отсоединен от своего главного окна (т.е., является окном верхнего уровня). Если он присоединен, он рисует индикатор метки-манипулятора изменения размера присоединяемого окна. Покажем для присоединяемого виджета в стиле plastique оба случая - когда он присоединен и отсоединен: Опцией стиля является QStyleOptionDockWidget:
Для кнопок используется QStyleOptionButton (смотрите описание содержимого Инструментальных кнопок). Метка-манипулятор изменения размера присоединяемого виджета имеет простую опцию стиля QStyleOption. |
Попытка перевода Qt документации. Если есть желание присоединиться, или если есть замечания или пожелания, то заходите на форум: Перевод Qt документации на русский язык... Люди внесшие вклад в перевод: Команда переводчиков |