Настройка Qt Quick Controls
Qt Quick Controls состоят из иерархии (дерева) элементов. Для предоставления пользовательского внешнего вида и ощущения можно заменить стандартную реализацию QML каждого элемента пользовательской.
Настройка элемента управления
Иногда вам нужно создать уникальный вид для определенной части вашего пользовательского интерфейса и использовать универсальный стиль для всех остальных элементов. Возможно, вам нравится используемый стиль, но есть определенная кнопка, которая имеет особое значение.
Первый способ создания такой кнопки — просто определить ее на месте, где она необходима. Например, возможно, вы не удовлетворены квадратными углами кнопки в стиле «Basic». Чтобы сделать их закругленными, можно переопределить элемент background и установить свойство радиуса Rectangle:
import QtQuick
import QtQuick.Controls
ApplicationWindow {
width: 400
height: 400
visible: true
Button {
id: button
text: "A Special Button"
background: Rectangle {
implicitWidth: 100
implicitHeight: 40
color: button.down ? "#d6d6d6" : "#f6f6f6"
border.color: "#26282a"
border.width: 1
radius: 4
}
}
} Второй способ создания кнопки хорош, если вы планируете использовать вашу закругленную кнопку в нескольких местах. Он заключается в перемещении кода в свой собственный файл QML в вашем проекте.
Для этого подхода скопируем код фона из стиля «Basic» Button.qml. Этот файл можно найти в следующем пути в вашей установке Qt:
$QTDIR/qml/QtQuick/Controls/Basic/Button.qml
После этого просто добавим следующую строку:
radius: 4
Чтобы избежать путаницы с элементами управления в самом модуле, назовем файл MyButton.qml. Чтобы использовать элемент управления в вашем приложении, ссылайтесь на него по имени файла:
import QtQuick.Controls
ApplicationWindow {
MyButton {
text: qsTr("A Special Button")
}
} Третий способ создания кнопки немного более структурирован, как в отношении расположения файла в файловой системе, так и в отношении его использования в QML. Сначала скопируйте существующий файл, как описано выше, но на этот раз поместите его в подпапку вашего проекта, например, controls. Чтобы использовать элемент управления, сначала импортируйте папку в пространство имен:
import QtQuick.Controls
import "controls" as MyControls
ApplicationWindow {
MyControls.Button {
text: qsTr("A Special Button")
}
} Теперь у вас есть пространство имен MyControls, вы можете называть элементы управления по их фактическим аналогам в модуле Qt Quick Controls. Вы можете повторить этот процесс для любого элемента управления, который вы хотите добавить.
Дополнительным преимуществом этих трех методов является то, что нет необходимости реализовывать шаблон с нуля.
Создание пользовательского стиля
Существует несколько способов создания собственных стилей. Ниже мы объясним различные подходы.
Определение стиля
В Qt Quick Controls стиль — это в сущности набор файлов QML в одном каталоге. Для того, чтобы стиль был использоваем, необходимо выполнить четыре требования:
- Должен существовать по крайней мере один файл QML, имя которого соответствует элементу управления (например,
Button.qml) - Каждый файл QML должен содержать соответствующий тип из импорта QtQuick.Templates в качестве корневого элемента. Например, Button.qml должен содержать шаблон Button в качестве своего корневого элемента.
Если вместо этого мы использовали бы соответствующий тип из импорта QtQuick.Controls, как мы делали в предыдущем разделе, это не сработало бы: элемент управления, который мы определяли, попытался бы вывести себя из самого себя.
- Должен существовать файл qmldir наряду с файлами QML. Ниже приведен пример простого файла
qmldirдля стиля, который предоставляет кнопку:module MyStyle Button 2.15 Button.qml
Если вы используете выбор стиля во время компиляции, qmldir также должен импортировать стиль по умолчанию:
# ... import QtQuick.Controls.Basic auto
Это также можно сделать для выбора стиля во время выполнения вместо использования, например, QQuickStyle::setFallbackStyle().
Структура каталога для такого стиля выглядит следующим образом:
MyStyle ├─── Button.qml └─── qmldir
- Файлы должны находиться в каталоге, который доступен через путь импорта QML.
Например, если путь к каталогу MyStyle, упомянутому выше, был
/home/user/MyApp/MyStyle, тогда/home/user/MyAppдолжен быть добавлен в путь импорта QML.Чтобы использовать MyStyle в MyApp, ссылайтесь на него по имени:
./MyApp -style MyStyle
Имя стиля должно соответствовать регистру каталога стиля; использование mystyle или MYSTYLE не поддерживается.
По умолчанию система стилизации использует стиль «Basic» в качестве стиля по умолчанию для элементов управления, которые не реализованы. Для настройки или расширения любого другого встроенного стиля можно указать другой стиль по умолчанию с помощью QQuickStyle.
Это означает, что вы можете реализовать любое количество элементов управления для вашего пользовательского стиля и разместить их практически где угодно. Это также позволяет пользователям создавать свои собственные стили для вашего приложения.
Предварительный просмотр пользовательских стилей в Qt Quick Designer
Используя приведенный выше подход, можно предварительно просмотреть пользовательский стиль в Qt Quick Designer. Для этого убедитесь, что в проекте есть файл qtquickcontrols2.conf и что в нем есть следующая запись:
[Controls] Style=MyStyle
Дополнительную информацию можно найти в примере плоского стиля.
Стилеспецифические расширения C++
Иногда для расширения пользовательского стиля может потребоваться использование C++. Существует два способа экспонировать такие типы в QML:
- Если стиль, использующий тип, является единственным стилем, используемым приложением, достаточно зарегистрировать его в движке QML с помощью qmlRegisterType():
qmlRegisterType<ACoolCppItem>("MyApp", 1, 0, "ACoolItem");Дополнительную информацию см. в разделе Использование данных C++ из QML.
- Если стиль, использующий тип, является одним из многих стилей, используемых приложением, может быть лучше зарегистрировать его только при необходимости. Это тот момент, когда имеет смысл реализовать собственный QML плагин.
Использование плагина как части вашего стиля не сильно отличается от использования набора файлов QML. Единственное различие заключается в том, что плагин и его файл
qmldirдолжны присутствовать в той же директории, что и файлы QML.
Учет при создании пользовательских стилей
При реализации собственного стиля и настройке элементов управления необходимо учитывать некоторые моменты, чтобы обеспечить максимальную производительность вашего приложения.
Избегайте присвоения идентификатора реализациям элементов управления в стилях
Как описано в Определении стиля, при реализации собственного стиля для элемента управления вы начинаете с соответствующего шаблона для этого элемента управления. Например, стиль Button.qml будет иметь структуру, аналогичную этой:
T.Button {
// ...
background: Rectangle {
// ...
}
contentItem: Text {
// ...
}
// ...
} Когда вы используете кнопку в своем приложении, элементы background и contentItem будут созданы и помещены в качестве дочерних элементов корневому элементу Button:
// Creates the Button root item, the Rectangle background,
// and the Text contentItem.
Button {
text: qsTr("Confirm")
} Предположим, что вам необходимо выполнить однократную настройку кнопки (как описано в Настройка элемента управления):
import QtQuick
import QtQuick.Controls
ApplicationWindow {
width: 400
height: 400
visible: true
Button {
id: button
text: "A Special Button"
background: Rectangle {
implicitWidth: 100
implicitHeight: 40
color: button.down ? "#d6d6d6" : "#f6f6f6"
border.color: "#26282a"
border.width: 1
radius: 4
}
}
} В QML это обычно приведет к созданию как стандартной реализации background элемента, так и одноразового, пользовательского элемента background. Qt Quick Controls использует технику, которая позволяет избежать создания обоих элементов, а вместо этого создает только пользовательский элемент background, значительно улучшая производительность создания элементов управления.
Эта техника опирается на отсутствие id в реализации элемента в стиле. Если идентификатор присвоен, техника не может работать, и оба элемента будут созданы. Например, может возникнуть желание присвоить id элементам background или contentItem, чтобы другие объекты в файле могли ссылаться на эти элементы:
T.Button {
// ...
background: Rectangle {
id: backgroundRect
// ...
}
contentItem: Text {
// Use backgroundRect in some way...
}
// ...
} С этим кодом каждый раз, когда создается экземпляр кнопки с настроенным фоном, оба фона будут создаваться, что приведет к не оптимальной производительности создания.
До Qt 5.15 устаревший, неиспользуемый фон удалялся, чтобы освободить ресурсы, связанные с ним. Однако, поскольку элемент управления не владеет элементами, он не должен их удалять. Начиная с Qt 5.15, старые элементы больше не удаляются, и поэтому элемент backgroundRect будет существовать дольше, чем необходимо — обычно до выхода приложения. Хотя старый элемент будет скрыт, визуально не связан с элементом управления и удален из дерева доступности, важно помнить о времени создания и использовании памяти этих неиспользуемых элементов при присвоении идентификатора в этом контексте.
Избегайте императивного назначения пользовательских элементов
Упомянутая выше техника работает только тогда, когда элемент декларативно назначается в первый раз, поэтому императивные назначения приведут к появлению оторванных элементов. Всякий раз, когда это возможно, используйте декларативные связи для назначения пользовательских элементов.
Присоединённые свойства
Для стиля часто существуют определенные свойства или атрибуты, которые применяются ко всем элементам управления. Присоединённые свойства — отличный способ расширения элемента в QML, не изменяя существующий C++, относящийся к этому элементу. Например, у стилей Material и Universal есть присоединённое свойство темы, которое управляет тем, будут ли элемент и его дочерние элементы отображаться в светлой или тёмной теме.
В качестве примера добавим присоединённое свойство, которое управляет подъёмом. Наш стиль проиллюстрирует подъём с помощью тени; чем выше подъём, тем больше тень.
Первый шаг — создать новое приложение Qt Quick Controls в Qt Creator. После этого мы добавим тип C++, который хранит подъём. Поскольку тип будет использоваться для каждого элемента управления, поддерживаемого нашим стилем, и поскольку мы можем добавить другие присоединённые свойства позже, мы назовём его MyStyle. Вот MyStyle.h:
#ifndef MYSTYLE_H
#define MYSTYLE_H
#include <QObject>
#include <QtQml>
class MyStyle : public QObject
{
Q_OBJECT
Q_PROPERTY(int elevation READ elevation WRITE setElevation NOTIFY elevationChanged)
public:
explicit MyStyle(QObject *parent = nullptr);
static MyStyle *qmlAttachedProperties(QObject *object);
int elevation() const;
void setElevation(int elevation);
signals:
void elevationChanged();
private:
int m_elevation;
};
QML_DECLARE_TYPEINFO(MyStyle, QML_HAS_ATTACHED_PROPERTIES)
#endif // MYSTYLE_H MyStyle.cpp:
#include "mystyle.h"
MyStyle::MyStyle(QObject *parent) :
QObject(parent),
m_elevation(0)
{
}
MyStyle *MyStyle::qmlAttachedProperties(QObject *object)
{
return new MyStyle(object);
}
int MyStyle::elevation() const
{
return m_elevation;
}
void MyStyle::setElevation(int elevation)
{
if (elevation == m_elevation)
return;
m_elevation = elevation;
emit elevationChanged();
} Тип MyStyle является особым в том смысле, что его не следует создавать, а использовать для его присоединённых свойств. По этой причине мы регистрируем его следующим образом в main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "mystyle.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterUncreatableType<MyStyle>("MyStyle", 1, 0, "MyStyle", "MyStyle is an attached property");
QQmlApplicationEngine engine;
// Make the directory containing our style known to the QML engine.
engine.addImportPath(":/");
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
return app.exec();
} Затем мы копируем Button.qml из стиля «Basic» в $QTDIR/qml/QtQuick/Controls/Basic/ в новую папку myproject в нашей директории проекта. Добавьте скопированный Button.qml в qml.qrc, который является файлом ресурсов, содержащим наши файлы QML.
Далее, мы добавляем тень с эффектом падающей тени к делегату фона кнопки:
// ...
import QtGraphicalEffects
import MyStyle
// ...
background: Rectangle {
// ...
layer.enabled: control.enabled && control.MyStyle.elevation > 0
layer.effect: DropShadow {
verticalOffset: 1
color: control.visualFocus ? "#330066ff" : "#aaaaaa"
samples: control.MyStyle.elevation
spread: 0.5
}
} Обратите внимание, что мы:
- Не используем тень с эффектом падающей тени, когда значение параметра возвышения равно
0 - Изменяем цвет тени в зависимости от того, имеет ли кнопка фокус
- Зависим размер тени от значения параметра возвышения
Чтобы протестировать присоединённое свойство, мы создаём ряд с двумя кнопками в main.qml:
import QtQuick
import QtQuick.Controls
import MyStyle 1.0
ApplicationWindow {
id: window
width: 400
height: 400
visible: true
Row {
spacing: 20
anchors.centerIn: parent
Button {
text: "Button 1"
}
Button {
text: "Button 2"
MyStyle.elevation: 10
}
}
} Одна кнопка не имеет возвышения, а другая имеет возвышение 10.
После этого мы можем запустить наш пример. Чтобы указать приложению использовать наш новый стиль, мы передаём -style MyStyle в качестве аргумента приложения, но существует много способов указать используемый стиль.
Конечный результат:
Обратите внимание, что import MyStyle 1.0 утверждение необходимо только потому, что мы используем присоединённое свойство, принадлежащее MyStyle. Обе кнопки будут использовать наш пользовательский стиль, даже если мы удалим импорт.
Справочник по настройке
В следующих фрагментах приведены примеры, где элементы управления стилем Basic были настраиваемы с использованием того же подхода, что и в разделе Настройка элемента управления. Данный код можно использовать в качестве отправной точки для реализации пользовательского внешнего вида.
Настройка ApplicationWindow
ApplicationWindow состоит из одного визуального элемента: фона.
import
import QtQuick.Controls
ApplicationWindow {
visible: true
background: Rectangle {
gradient: Gradient {
GradientStop { position: 0; color: "#ffffff" }
GradientStop { position: 1; color: "#c1bbf9" }
}
}
} Настройка BusyIndicator
BusyIndicator состоит из двух визуальных элементов: фона и элемента содержимого.
import QtQuick
import QtQuick.Controls
BusyIndicator {
id: control
contentItem: Item {
implicitWidth: 64
implicitHeight: 64
Item {
id: item
x: parent.width / 2 - 32
y: parent.height / 2 - 32
width: 64
height: 64
opacity: control.running ? 1 : 0
Behavior on opacity {
OpacityAnimator {
duration: 250
}
}
RotationAnimator {
target: item
running: control.visible && control.running
from: 0
to: 360
loops: Animation.Infinite
duration: 1250
}
Repeater {
id: repeater
model: 6
Rectangle {
x: item.width / 2 - width / 2
y: item.height / 2 - height / 2
implicitWidth: 10
implicitHeight: 10
radius: 5
color: "#21be2b"
transform: [
Translate {
y: -Math.min(item.width, item.height) * 0.5 + 5
},
Rotation {
angle: index / repeater.count * 360
origin.x: 5
origin.y: 5
}
]
}
}
}
}
} Настройка Button
Кнопка состоит из двух визуальных элементов: фона и элемента содержимого.
import QtQuick
import QtQuick.Controls
Button {
id: control
text: qsTr("Button")
contentItem: Text {
text: control.text
font: control.font
opacity: enabled ? 1.0 : 0.3
color: control.down ? "#17a81a" : "#21be2b"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
background: Rectangle {
implicitWidth: 100
implicitHeight: 40
opacity: enabled ? 1 : 0.3
border.color: control.down ? "#17a81a" : "#21be2b"
border.width: 1
radius: 2
}
} Настройка CheckBox
CheckBox состоит из трёх визуальных элементов: фона, элемента содержимого и индикатора.
import QtQuick
import QtQuick.Controls
CheckBox {
id: control
text: qsTr("CheckBox")
checked: true
indicator: Rectangle {
implicitWidth: 26
implicitHeight: 26
x: control.leftPadding
y: parent.height / 2 - height / 2
radius: 3
border.color: control.down ? "#17a81a" : "#21be2b"
Rectangle {
width: 14
height: 14
x: 6
y: 6
radius: 2
color: control.down ? "#17a81a" : "#21be2b"
visible: control.checked
}
}
contentItem: Text {
text: control.text
font: control.font
opacity: enabled ? 1.0 : 0.3
color: control.down ? "#17a81a" : "#21be2b"
verticalAlignment: Text.AlignVCenter
leftPadding: control.indicator.width + control.spacing
}
} Настройка CheckDelegate
CheckDelegate состоит из трёх визуальных элементов: фона, элемента содержимого и индикатора.
import QtQuick
import QtQuick.Controls
CheckDelegate {
id: control
text: qsTr("CheckDelegate")
checked: true
contentItem: Text {
rightPadding: control.indicator.width + control.spacing
text: control.text
font: control.font
opacity: enabled ? 1.0 : 0.3
color: control.down ? "#17a81a" : "#21be2b"
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
indicator: Rectangle {
implicitWidth: 26
implicitHeight: 26
x: control.width - width - control.rightPadding
y: control.topPadding + control.availableHeight / 2 - height / 2
radius: 3
color: "transparent"
border.color: control.down ? "#17a81a" : "#21be2b"
Rectangle {
width: 14
height: 14
x: 6
y: 6
radius: 2
color: control.down ? "#17a81a" : "#21be2b"
visible: control.checked
}
}
background: Rectangle {
implicitWidth: 100
implicitHeight: 40
visible: control.down || control.highlighted
color: control.down ? "#bdbebf" : "#eeeeee"
}
} Настройка ComboBox
ComboBox состоит из фона, элемента содержимого, выпадающего списка, индикатора и делегата.
import QtQuick
import QtQuick.Controls
ComboBox {
id: control
model: ["First", "Second", "Third"]
delegate: ItemDelegate {
width: control.width
contentItem: Text {
text: modelData
color: "#21be2b"
font: control.font
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
highlighted: control.highlightedIndex === index
}
indicator: Canvas {
id: canvas
x: control.width - width - control.rightPadding
y: control.topPadding + (control.availableHeight - height) / 2
width: 12
height: 8
contextType: "2d"
Connections {
target: control
function onPressedChanged() { canvas.requestPaint(); }
}
onPaint: {
context.reset();
context.moveTo(0, 0);
context.lineTo(width, 0);
context.lineTo(width / 2, height);
context.closePath();
context.fillStyle = control.pressed ? "#17a81a" : "#21be2b";
context.fill();
}
}
contentItem: Text {
leftPadding: 0
rightPadding: control.indicator.width + control.spacing
text: control.displayText
font: control.font
color: control.pressed ? "#17a81a" : "#21be2b"
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
background: Rectangle {
implicitWidth: 120
implicitHeight: 40
border.color: control.pressed ? "#17a81a" : "#21be2b"
border.width: control.visualFocus ? 2 : 1
radius: 2
}
popup: Popup {
y: control.height - 1
width: control.width
implicitHeight: contentItem.implicitHeight
padding: 1
contentItem: ListView {
clip: true
implicitHeight: contentHeight
model: control.popup.visible ? control.delegateModel : null
currentIndex: control.highlightedIndex
ScrollIndicator.vertical: ScrollIndicator { }
}
background: Rectangle {
border.color: "#21be2b"
radius: 2
}
}
} Настройка DelayButton
DelayButton состоит из двух визуальных элементов: фона и элемента содержимого.
import QtQuick
import QtQuick.Controls
DelayButton {
id: control
checked: true
text: qsTr("Delay\nButton")
contentItem: Text {
text: control.text
font: control.font
opacity: enabled ? 1.0 : 0.3
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
background: Rectangle {
implicitWidth: 100
implicitHeight: 100
opacity: enabled ? 1 : 0.3
color: control.down ? "#17a81a" : "#21be2b"
radius: size / 2
readonly property real size: Math.min(control.width, control.height)
width: size
height: size
anchors.centerIn: parent
Canvas {
id: canvas
anchors.fill: parent
Connections {
target: control
function onProgressChanged() { canvas.requestPaint(); }
}
onPaint: {
var ctx = getContext("2d")
ctx.clearRect(0, 0, width, height)
ctx.strokeStyle = "white"
ctx.lineWidth = parent.size / 20
ctx.beginPath()
var startAngle = Math.PI / 5 * 3
var endAngle = startAngle + control.progress * Math.PI / 5 * 9
ctx.arc(width / 2, height / 2, width / 2 - ctx.lineWidth / 2 - 2, startAngle, endAngle)
ctx.stroke()
}
}
}
} Настройка Dial
Dial состоит из двух визуальных элементов: фона и ручки.
import QtQuick
import QtQuick.Controls
Dial {
id: control
background: Rectangle {
x: control.width / 2 - width / 2
y: control.height / 2 - height / 2
width: Math.max(64, Math.min(control.width, control.height))
height: width
color: "transparent"
radius: width / 2
border.color: control.pressed ? "#17a81a" : "#21be2b"
opacity: control.enabled ? 1 : 0.3
}
handle: Rectangle {
id: handleItem
x: control.background.x + control.background.width / 2 - width / 2
y: control.background.y + control.background.height / 2 - height / 2
width: 16
height: 16
color: control.pressed ? "#17a81a" : "#21be2b"
radius: 8
antialiasing: true
opacity: control.enabled ? 1 : 0.3
transform: [
Translate {
y: -Math.min(control.background.width, control.background.height) * 0.4 + handleItem.height / 2
},
Rotation {
angle: control.angle
origin.x: handleItem.width / 2
origin.y: handleItem.height / 2
}
]
}
} Настройка Drawer
Drawer может иметь визуальный фон элемент.
background: Rectangle {
Rectangle {
x: parent.width - 1
width: 1
height: parent.height
color: "#21be2b"
}
} Настройка Frame
Frame состоит из одного визуального элемента: фона.
import QtQuick
import QtQuick.Controls
Frame {
background: Rectangle {
color: "transparent"
border.color: "#21be2b"
radius: 2
}
Label {
text: qsTr("Content goes here!")
}
} Настройка GroupBox
GroupBox состоит из двух визуальных элементов: фона и метки.
import QtQuick
import QtQuick.Controls
GroupBox {
id: control
title: qsTr("GroupBox")
background: Rectangle {
y: control.topPadding - control.bottomPadding
width: parent.width
height: parent.height - control.topPadding + control.bottomPadding
color: "transparent"
border.color: "#21be2b"
radius: 2
}
label: Label {
x: control.leftPadding
width: control.availableWidth
text: control.title
color: "#21be2b"
elide: Text.ElideRight
}
Label {
text: qsTr("Content goes here!")
}
} Настройка ItemDelegate
ItemDelegate состоит из двух визуальных элементов: фона и элемента содержимого.
import QtQuick
import QtQuick.Controls
ItemDelegate {
id: control
text: qsTr("ItemDelegate")
contentItem: Text {
rightPadding: control.spacing
text: control.text
font: control.font
color: control.enabled ? (control.down ? "#17a81a" : "#21be2b") : "#bdbebf"
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
implicitWidth: 100
implicitHeight: 40
opacity: enabled ? 1 : 0.3
color: control.down ? "#dddedf" : "#eeeeee"
Rectangle {
width: parent.width
height: 1
color: control.down ? "#17a81a" : "#21be2b"
anchors.bottom: parent.bottom
}
}
} Настройка Label
Метка может иметь визуальный фон элемент.
import QtQuick
import QtQuick.Controls
Label {
text: qsTr("Label")
color: "#21be2b"
} Настройка Menu
- Меню состоит из визуального фонового элемента.
- Элемент меню состоит из четырёх визуальных элементов: фона, элемента содержимого, индикатора и стрелки.
- Разделитель меню состоит из визуального фона и элемента содержимого.
import QtQuick
import QtQuick.Controls
Menu {
id: menu
Action { text: qsTr("Tool Bar"); checkable: true }
Action { text: qsTr("Side Bar"); checkable: true; checked: true }
Action { text: qsTr("Status Bar"); checkable: true; checked: true }
MenuSeparator {
contentItem: Rectangle {
implicitWidth: 200
implicitHeight: 1
color: "#21be2b"
}
}
Menu {
title: qsTr("Advanced")
// ...
}
topPadding: 2
bottomPadding: 2
delegate: MenuItem {
id: menuItem
implicitWidth: 200
implicitHeight: 40
arrow: Canvas {
x: parent.width - width
implicitWidth: 40
implicitHeight: 40
visible: menuItem.subMenu
onPaint: {
var ctx = getContext("2d")
ctx.fillStyle = menuItem.highlighted ? "#ffffff" : "#21be2b"
ctx.moveTo(15, 15)
ctx.lineTo(width - 15, height / 2)
ctx.lineTo(15, height - 15)
ctx.closePath()
ctx.fill()
}
}
indicator: Item {
implicitWidth: 40
implicitHeight: 40
Rectangle {
width: 26
height: 26
anchors.centerIn: parent
visible: menuItem.checkable
border.color: "#21be2b"
radius: 3
Rectangle {
width: 14
height: 14
anchors.centerIn: parent
visible: menuItem.checked
color: "#21be2b"
radius: 2
}
}
}
contentItem: Text {
leftPadding: menuItem.indicator.width
rightPadding: menuItem.arrow.width
text: menuItem.text
font: menuItem.font
opacity: enabled ? 1.0 : 0.3
color: menuItem.highlighted ? "#ffffff" : "#21be2b"
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
background: Rectangle {
implicitWidth: 200
implicitHeight: 40
opacity: enabled ? 1 : 0.3
color: menuItem.highlighted ? "#21be2b" : "transparent"
}
}
background: Rectangle {
implicitWidth: 200
implicitHeight: 40
color: "#ffffff"
border.color: "#21be2b"
radius: 2
}
} Настройка MenuBar
Строка меню может иметь визуальный фон элемент, а Элемент строки меню состоит из двух визуальных элементов: фона и элемента содержимого.
import QtQuick
import QtQuick.Controls
MenuBar {
id: menuBar
Menu { title: qsTr("File") }
Menu { title: qsTr("Edit") }
Menu { title: qsTr("View") }
Menu { title: qsTr("Help") }
delegate: MenuBarItem {
id: menuBarItem
contentItem: Text {
text: menuBarItem.text
font: menuBarItem.font
opacity: enabled ? 1.0 : 0.3
color: menuBarItem.highlighted ? "#ffffff" : "#21be2b"
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
background: Rectangle {
implicitWidth: 40
implicitHeight: 40
opacity: enabled ? 1 : 0.3
color: menuBarItem.highlighted ? "#21be2b" : "transparent"
}
}
background: Rectangle {
implicitWidth: 40
implicitHeight: 40
color: "#ffffff"
Rectangle {
color: "#21be2b"
width: parent.width
height: 1
anchors.bottom: parent.bottom
}
}
} Настройка PageIndicator
PageIndicator состоит из фона, элемента содержимого и делегата.
import QtQuick
import QtQuick.Controls
PageIndicator {
id: control
count: 5
currentIndex: 2
delegate: Rectangle {
implicitWidth: 8
implicitHeight: 8
radius: width / 2
color: "#21be2b"
opacity: index === control.currentIndex ? 0.95 : pressed ? 0.7 : 0.45
Behavior on opacity {
OpacityAnimator {
duration: 100
}
}
}
} Настройка Pane
Pane состоит из фона.
import QtQuick
import QtQuick.Controls
Pane {
background: Rectangle {
color: "#eeeeee"
}
Label {
text: qsTr("Content goes here!")
}
} Настройка Popup
Popup состоит из фона и элемента содержимого.
import QtQuick
import QtQuick.Controls
Popup {
id: popup
background: Rectangle {
implicitWidth: 200
implicitHeight: 200
border.color: "#444"
}
contentItem: Column {}
} Настройка ProgressBar
ProgressBar состоит из двух визуальных элементов: фона и элемента содержимого.
import QtQuick
import QtQuick.Controls
ProgressBar {
id: control
value: 0.5
padding: 2
background: Rectangle {
implicitWidth: 200
implicitHeight: 6
color: "#e6e6e6"
radius: 3
}
contentItem: Item {
implicitWidth: 200
implicitHeight: 4
Rectangle {
width: control.visualPosition * parent.width
height: parent.height
radius: 2
color: "#17a81a"
}
}
} Настройка RadioButton
RadioButton состоит из трёх визуальных элементов: фона, элемента содержимого и индикатора.
import QtQuick
import QtQuick.Controls
RadioButton {
id: control
text: qsTr("RadioButton")
checked: true
indicator: Rectangle {
implicitWidth: 26
implicitHeight: 26
x: control.leftPadding
y: parent.height / 2 - height / 2
radius: 13
border.color: control.down ? "#17a81a" : "#21be2b"
Rectangle {
width: 14
height: 14
x: 6
y: 6
radius: 7
color: control.down ? "#17a81a" : "#21be2b"
visible: control.checked
}
}
contentItem: Text {
text: control.text
font: control.font
opacity: enabled ? 1.0 : 0.3
color: control.down ? "#17a81a" : "#21be2b"
verticalAlignment: Text.AlignVCenter
leftPadding: control.indicator.width + control.spacing
}
} Настройка RadioDelegate
RadioDelegate состоит из трёх визуальных элементов: фона, элемента содержимого и индикатора.
import QtQuick
import QtQuick.Controls
RadioDelegate {
id: control
text: qsTr("RadioDelegate")
checked: true
contentItem: Text {
rightPadding: control.indicator.width + control.spacing
text: control.text
font: control.font
opacity: enabled ? 1.0 : 0.3
color: control.down ? "#17a81a" : "#21be2b"
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
indicator: Rectangle {
implicitWidth: 26
implicitHeight: 26
x: control.width - width - control.rightPadding
y: parent.height / 2 - height / 2
radius: 13
color: "transparent"
border.color: control.down ? "#17a81a" : "#21be2b"
Rectangle {
width: 14
height: 14
x: 6
y: 6
radius: 7
color: control.down ? "#17a81a" : "#21be2b"
visible: control.checked
}
}
background: Rectangle {
implicitWidth: 100
implicitHeight: 40
visible: control.down || control.highlighted
color: control.down ? "#bdbebf" : "#eeeeee"
}
} Настройка RangeSlider
RangeSlider состоит из трёх визуальных элементов: фона, первой ручки и второй ручки.
import QtQuick
import QtQuick.Controls
RangeSlider {
id: control
first.value: 0.25
second.value: 0.75
background: Rectangle {
x: control.leftPadding
y: control.topPadding + control.availableHeight / 2 - height / 2
implicitWidth: 200
implicitHeight: 4
width: control.availableWidth
height: implicitHeight
radius: 2
color: "#bdbebf"
Rectangle {
x: control.first.visualPosition * parent.width
width: control.second.visualPosition * parent.width - x
height: parent.height
color: "#21be2b"
radius: 2
}
}
first.handle: Rectangle {
x: control.leftPadding + control.first.visualPosition * (control.availableWidth - width)
y: control.topPadding + control.availableHeight / 2 - height / 2
implicitWidth: 26
implicitHeight: 26
radius: 13
color: control.first.pressed ? "#f0f0f0" : "#f6f6f6"
border.color: "#bdbebf"
}
second.handle: Rectangle {
x: control.leftPadding + control.second.visualPosition * (control.availableWidth - width)
y: control.topPadding + control.availableHeight / 2 - height / 2
implicitWidth: 26
implicitHeight: 26
radius: 13
color: control.second.pressed ? "#f0f0f0" : "#f6f6f6"
border.color: "#bdbebf"
}
} Настройка RoundButton
RoundButton можно настраивать аналогично кнопке.
Настройка ScrollBar
ScrollBar состоит из двух визуальных элементов: фона и элемента содержимого.
import QtQuick
import QtQuick.Controls
ScrollBar {
id: control
size: 0.3
position: 0.2
active: true
orientation: Qt.Vertical
contentItem: Rectangle {
implicitWidth: 6
implicitHeight: 100
radius: width / 2
color: control.pressed ? "#81e889" : "#c2f4c6"
}
} Настройка ScrollIndicator
ScrollIndicator состоит из двух визуальных элементов: фона и элемента содержимого.
import QtQuick
import QtQuick.Controls
ScrollIndicator {
id: control
size: 0.3
position: 0.2
active: true
orientation: Qt.Vertical
contentItem: Rectangle {
implicitWidth: 2
implicitHeight: 100
color: "#c2f4c6"
}
} Настройка ScrollView
ScrollView состоит из элемента фона и горизонтальной и вертикальной полосы прокрутки.
ScrollView {
id: control
width: 200
height: 200
focus: true
Label {
text: "ABC"
font.pixelSize: 224
}
ScrollBar.vertical: ScrollBar {
parent: control
x: control.mirrored ? 0 : control.width - width
y: control.topPadding
height: control.availableHeight
active: control.ScrollBar.horizontal.active
}
ScrollBar.horizontal: ScrollBar {
parent: control
x: control.leftPadding
y: control.height - height
width: control.availableWidth
active: control.ScrollBar.vertical.active
}
background: Rectangle {
border.color: control.activeFocus ? "#21be2b" : "#bdbebf"
}
} Настройка Slider
Ползунок состоит из двух визуальных элементов: фоновый и ручка.
import QtQuick
import QtQuick.Controls
Slider {
id: control
value: 0.5
background: Rectangle {
x: control.leftPadding
y: control.topPadding + control.availableHeight / 2 - height / 2
implicitWidth: 200
implicitHeight: 4
width: control.availableWidth
height: implicitHeight
radius: 2
color: "#bdbebf"
Rectangle {
width: control.visualPosition * parent.width
height: parent.height
color: "#21be2b"
radius: 2
}
}
handle: Rectangle {
x: control.leftPadding + control.visualPosition * (control.availableWidth - width)
y: control.topPadding + control.availableHeight / 2 - height / 2
implicitWidth: 26
implicitHeight: 26
radius: 13
color: control.pressed ? "#f0f0f0" : "#f6f6f6"
border.color: "#bdbebf"
}
} Настройка SpinBox
SpinBox состоит из четырёх визуальных элементов: фоновый, элемент содержимого, индикатор вверх и индикатор вниз.
import QtQuick
import QtQuick.Controls
SpinBox {
id: control
value: 50
editable: true
contentItem: TextInput {
z: 2
text: control.textFromValue(control.value, control.locale)
font: control.font
color: "#21be2b"
selectionColor: "#21be2b"
selectedTextColor: "#ffffff"
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
readOnly: !control.editable
validator: control.validator
inputMethodHints: Qt.ImhFormattedNumbersOnly
}
up.indicator: Rectangle {
x: control.mirrored ? 0 : parent.width - width
height: parent.height
implicitWidth: 40
implicitHeight: 40
color: control.up.pressed ? "#e4e4e4" : "#f6f6f6"
border.color: enabled ? "#21be2b" : "#bdbebf"
Text {
text: "+"
font.pixelSize: control.font.pixelSize * 2
color: "#21be2b"
anchors.fill: parent
fontSizeMode: Text.Fit
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
down.indicator: Rectangle {
x: control.mirrored ? parent.width - width : 0
height: parent.height
implicitWidth: 40
implicitHeight: 40
color: control.down.pressed ? "#e4e4e4" : "#f6f6f6"
border.color: enabled ? "#21be2b" : "#bdbebf"
Text {
text: "-"
font.pixelSize: control.font.pixelSize * 2
color: "#21be2b"
anchors.fill: parent
fontSizeMode: Text.Fit
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
background: Rectangle {
implicitWidth: 140
border.color: "#bdbebf"
}
} Настройка SplitView
SplitView состоит из визуального элемента управления.
SplitView {
id: splitView
anchors.fill: parent
handle: Rectangle {
implicitWidth: 4
implicitHeight: 4
color: SplitHandle.pressed ? "#81e889"
: (SplitHandle.hovered ? Qt.lighter("#c2f4c6", 1.1) : "#c2f4c6")
}
Rectangle {
implicitWidth: 150
color: "#444"
}
Rectangle {
implicitWidth: 50
color: "#666"
}
} Настройка StackView
StackView может иметь визуальный элемент фоновый, и позволяет настраивать переходы, используемые для операций push, pop и replace.
import QtQuick
import QtQuick.Controls
StackView {
id: control
popEnter: Transition {
XAnimator {
from: (control.mirrored ? -1 : 1) * -control.width
to: 0
duration: 400
easing.type: Easing.OutCubic
}
}
popExit: Transition {
XAnimator {
from: 0
to: (control.mirrored ? -1 : 1) * control.width
duration: 400
easing.type: Easing.OutCubic
}
}
} Настройка SwipeDelegate
SwipeDelegate состоит из шести визуальных элементов: фоновый, элемент содержимого, индикатор, swipe.left, swipe.right, и swipe.behind.
import QtQuick
import QtQuick.Controls
SwipeDelegate {
id: control
text: qsTr("SwipeDelegate")
Component {
id: component
Rectangle {
color: SwipeDelegate.pressed ? "#333" : "#444"
width: parent.width
height: parent.height
clip: true
Label {
text: qsTr("Press me!")
color: "#21be2b"
anchors.centerIn: parent
}
}
}
swipe.left: component
swipe.right: component
contentItem: Text {
text: control.text
font: control.font
color: control.enabled ? (control.down ? "#17a81a" : "#21be2b") : "#bdbebf"
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
Behavior on x {
enabled: !control.down
NumberAnimation {
easing.type: Easing.InOutCubic
duration: 400
}
}
}
} Настройка SwipeView
SwipeView может иметь визуальный элемент фоновый. Навигация реализуется элементом содержимого.
import QtQuick
import QtQuick.Controls
SwipeView {
id: control
background: Rectangle {
color: "#eeeeee"
}
} Настройка Switch
Переключатель состоит из трёх визуальных элементов: фоновый, элемент содержимого и индикатор.
import QtQuick
import QtQuick.Controls
Switch {
id: control
text: qsTr("Switch")
indicator: Rectangle {
implicitWidth: 48
implicitHeight: 26
x: control.leftPadding
y: parent.height / 2 - height / 2
radius: 13
color: control.checked ? "#17a81a" : "#ffffff"
border.color: control.checked ? "#17a81a" : "#cccccc"
Rectangle {
x: control.checked ? parent.width - width : 0
width: 26
height: 26
radius: 13
color: control.down ? "#cccccc" : "#ffffff"
border.color: control.checked ? (control.down ? "#17a81a" : "#21be2b") : "#999999"
}
}
contentItem: Text {
text: control.text
font: control.font
opacity: enabled ? 1.0 : 0.3
color: control.down ? "#17a81a" : "#21be2b"
verticalAlignment: Text.AlignVCenter
leftPadding: control.indicator.width + control.spacing
}
} Настройка SwitchDelegate
SwitchDelegate состоит из трёх визуальных элементов: фоновый, элемент содержимого и индикатор.
import QtQuick
import QtQuick.Controls
SwitchDelegate {
id: control
text: qsTr("SwitchDelegate")
checked: true
contentItem: Text {
rightPadding: control.indicator.width + control.spacing
text: control.text
font: control.font
opacity: enabled ? 1.0 : 0.3
color: control.down ? "#17a81a" : "#21be2b"
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
indicator: Rectangle {
implicitWidth: 48
implicitHeight: 26
x: control.width - width - control.rightPadding
y: parent.height / 2 - height / 2
radius: 13
color: control.checked ? "#17a81a" : "transparent"
border.color: control.checked ? "#17a81a" : "#cccccc"
Rectangle {
x: control.checked ? parent.width - width : 0
width: 26
height: 26
radius: 13
color: control.down ? "#cccccc" : "#ffffff"
border.color: control.checked ? (control.down ? "#17a81a" : "#21be2b") : "#999999"
}
}
background: Rectangle {
implicitWidth: 100
implicitHeight: 40
visible: control.down || control.highlighted
color: control.down ? "#bdbebf" : "#eeeeee"
}
} Настройка TabBar
TabBar состоит из двух визуальных элементов: фоновый и элемент содержимого.
import QtQuick
import QtQuick.Controls
TabBar {
id: control
background: Rectangle {
color: "#eeeeee"
}
TabButton {
text: qsTr("Home")
}
TabButton {
text: qsTr("Discover")
}
TabButton {
text: qsTr("Activity")
}
} Настройка TabButton
TabButton можно настраивать аналогично кнопке.
Настройка TextArea
TextArea состоит из элемента фоновый.
import QtQuick
import QtQuick.Controls
TextArea {
id: control
placeholderText: qsTr("Enter description")
background: Rectangle {
implicitWidth: 200
implicitHeight: 40
border.color: control.enabled ? "#21be2b" : "transparent"
}
} Настройка TextField
TextField состоит из элемента фоновый.
import QtQuick
import QtQuick.Controls
TextField {
id: control
placeholderText: qsTr("Enter description")
background: Rectangle {
implicitWidth: 200
implicitHeight: 40
color: control.enabled ? "transparent" : "#353637"
border.color: control.enabled ? "#21be2b" : "transparent"
}
} Настройка ToolBar
ToolBar состоит из одного визуального элемента: фоновый.
ToolBar {
id: control
background: Rectangle {
implicitHeight: 40
color: "#eeeeee"
Rectangle {
width: parent.width
height: 1
anchors.bottom: parent.bottom
color: "transparent"
border.color: "#21be2b"
}
}
RowLayout {
anchors.fill: parent
ToolButton {
text: qsTr("Undo")
}
ToolButton {
text: qsTr("Redo")
}
}
} Настройка ToolButton
ToolButton состоит из двух визуальных элементов: фоновый и элемент содержимого.
import QtQuick
import QtQuick.Controls
ToolButton {
id: control
text: qsTr("ToolButton")
width: 120
contentItem: Text {
text: control.text
font: control.font
opacity: enabled ? 1.0 : 0.3
color: control.down ? "#17a81a" : "#21be2b"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
background: Rectangle {
implicitWidth: 40
implicitHeight: 40
color: Qt.darker("#33333333", control.enabled && (control.checked || control.highlighted) ? 1.5 : 1.0)
opacity: enabled ? 1 : 0.3
visible: control.down || (control.enabled && (control.checked || control.highlighted))
}
} Настройка ToolSeparator
ToolSeparator состоит из двух визуальных элементов: фоновый и элемент содержимого.
ToolBar {
RowLayout {
anchors.fill: parent
ToolButton {
text: qsTr("Action 1")
}
ToolButton {
text: qsTr("Action 2")
}
ToolSeparator {
padding: vertical ? 10 : 2
topPadding: vertical ? 2 : 10
bottomPadding: vertical ? 2 : 10
contentItem: Rectangle {
implicitWidth: parent.vertical ? 1 : 24
implicitHeight: parent.vertical ? 24 : 1
color: "#c3c3c3"
}
}
ToolButton {
text: qsTr("Action 3")
}
ToolButton {
text: qsTr("Action 4")
}
Item {
Layout.fillWidth: true
}
}
} Настройка ToolTip
ToolTip состоит из двух визуальных элементов: фоновый и элемент содержимого.
import QtQuick
import QtQuick.Controls
ToolTip {
id: control
text: qsTr("A descriptive tool tip of what the button does")
contentItem: Text {
text: control.text
font: control.font
color: "#21be2b"
}
background: Rectangle {
border.color: "#21be2b"
}
} Настройка Tumbler
Tumbler состоит из трёх визуальных элементов: фоновый, элемент содержимого и делегат.
import QtQuick
import QtQuick.Controls
Tumbler {
id: control
model: 15
background: Item {
Rectangle {
opacity: control.enabled ? 0.2 : 0.1
border.color: "#000000"
width: parent.width
height: 1
anchors.top: parent.top
}
Rectangle {
opacity: control.enabled ? 0.2 : 0.1
border.color: "#000000"
width: parent.width
height: 1
anchors.bottom: parent.bottom
}
}
delegate: Text {
text: qsTr("Item %1").arg(modelData + 1)
font: control.font
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
opacity: 1.0 - Math.abs(Tumbler.displacement) / (control.visibleItemCount / 2)
}
Rectangle {
anchors.horizontalCenter: control.horizontalCenter
y: control.height * 0.4
width: 40
height: 1
color: "#21be2b"
}
Rectangle {
anchors.horizontalCenter: control.horizontalCenter
y: control.height * 0.6
width: 40
height: 1
color: "#21be2b"
}
} Если вы хотите определить свой собственный элемент содержимого, используйте ListView или PathView в качестве корневого элемента. Для обертывающего Tumbler используйте PathView:
Tumbler {
id: tumbler
contentItem: PathView {
id: pathView
model: tumbler.model
delegate: tumbler.delegate
clip: true
pathItemCount: tumbler.visibleItemCount + 1
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
dragMargin: width / 2
path: Path {
startX: pathView.width / 2
startY: -pathView.delegateHeight / 2
PathLine {
x: pathView.width / 2
y: pathView.pathItemCount * pathView.delegateHeight - pathView.delegateHeight / 2
}
}
property real delegateHeight: tumbler.availableHeight / tumbler.visibleItemCount
}
} Для необорачивающего Tumbler используйте ListView:
Tumbler {
id: tumbler
contentItem: ListView {
model: tumbler.model
delegate: tumbler.delegate
snapMode: ListView.SnapToItem
highlightRangeMode: ListView.StrictlyEnforceRange
preferredHighlightBegin: height / 2 - (height / tumbler.visibleItemCount / 2)
preferredHighlightEnd: height / 2 + (height / tumbler.visibleItemCount / 2)
clip: true
}
}
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.1/qtquickcontrols2-customize.html