Рад снова всех приветствовать! Как известно, спрос рождает предложение, поэтому я решил опубликовать цикл статей, посвященных различным аспектам QML 👍 Пройдемся более менее масштабно, начиная с основных интерактивных элементов для взаимодействия с пользователем, таких как Button
, CheckBox
, ComboBox
и т. д... И не оставим в стороне также более узконаправленные нюансы. При этом среди вопросов по этой тематике добрую половину представляют из себя вопросы по кастомизации стиля и внешнего вида элементов. Соответственно, этому уделим отдельное внимание, конечно же, все будет сопровождаться конкретными практическими примерами.
Не знал, с чего именно начать, но по факту последовательность тут особой роли не играет, так что без веской на то причины первым героем данного цикла будет тип Button
. И в качестве отправной точки стабильно будем использовать пустой Qt Quick проект:
В итоге имеем проект с двумя файлами с дефолтным содержимым, main.cpp:
#include <QGuiApplication> #include <QQmlApplicationEngine> int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); QQmlApplicationEngine engine; const QUrl url(QStringLiteral("qrc:/main.qml")); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.load(url); return app.exec(); }
И main.qml:
import QtQuick 2.15 import QtQuick.Window 2.15 Window { width: 640 height: 480 visible: true title: qsTr("Hello World") }
Для использования Button
сразу импортируем:
import QtQuick.Controls 2.15
QML Button. Создание.
Итак, пустое приложение дает нам в результате запуска пустое же окно, и это прекрасно, ничего лишнего. Сегодня разбираем исключительно кнопку, поэтому создадим ее прямо в самом центре окна, сделав последнее чуть поменьше 👍:
import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.15 Window { width: 320 height: 240 visible: true title: qsTr("QML Button Review") Button { id: testButton anchors.centerIn: parent text: qsTr("testButton") } }
Как и для практически любого компонента QML непосредственно добавление осуществляется элементарно, в чем мы и убедились. Вид на данный момент Button
имеет дефолтный, ничем не примечательный, кастомизацией займемся чуть позже. Но прежде рассмотрим кратко функционал...
QML Button. События.
Итак, кнопка создана для того, чтобы ее нажимали. Поэтому разберемся, как обработать данное действие пользователя. И все также максимально просто, при помощи обработчика - onClicked
:
Window { width: 320 height: 240 visible: true title: qsTr("QML Button Review") Button { id: testButton anchors.centerIn: parent text: qsTr("testButton") onClicked: { console.log("testButton clicked"); } } }
Здесь мы по нажатию выводим строку в консоль. Пример настолько прост, что просто вгоняет в краску, поэтому минимально, но усложним. Добавим под кнопку текст, в который при нажатии будем выводить количество кликов по кнопке:
Window { width: 320 height: 240 visible: true title: qsTr("QML Button Review") Button { id: testButton anchors.centerIn: parent text: qsTr("testButton") property var clickCounter: 0 onClicked: { clickCounter++; testButtonClicks.text = qsTr("testButton clicked: ") + clickCounter; } } Text { id: testButtonClicks anchors.horizontalCenter: parent.horizontalCenter anchors.top: testButton.bottom anchors.topMargin: 10 text: qsTr("No clicks") } }
Button
наследует от AbstractButton, поэтому именно здесь определена и описана подавляющая часть свойств и методов. В частности, сигналы, для каждого из которых существует свой обработчик вида onИмяСигнала
:
- onCanceled
- onClicked
- onDoubleClicked
- onPressAndHold
- onPressed
- onReleased
- onToggled
Добавим еще одну текстовую строку для вывода последней произошедшей с кнопкой активности, а также пару обработчиков для наглядности:
Window { width: 320 height: 240 visible: true title: qsTr("QML Button Review") Button { id: testButton anchors.centerIn: parent text: qsTr("testButton") property var clickCounter: 0 onClicked: { clickCounter++; testButtonClicks.text = qsTr("testButton clicked: ") + clickCounter; testButtonLastEvent.text = qsTr("testButton: clicked"); } onDoubleClicked: { testButtonLastEvent.text = qsTr("testButton: doubleCilcked"); } onPressAndHold: { testButtonLastEvent.text = qsTr("testButton: pressAndHold"); } onPressed: { testButtonLastEvent.text = qsTr("testButton: pressed"); } } Text { id: testButtonClicks anchors.horizontalCenter: parent.horizontalCenter anchors.top: testButton.bottom anchors.topMargin: 10 text: qsTr("No clicks") } Text { id: testButtonLastEvent anchors.horizontalCenter: parent.horizontalCenter anchors.top: testButtonClicks.bottom anchors.topMargin: 10 text: qsTr("No event") } }
Таким вот образом можно спокойно поэкспериментировать и на практике ощутить, какой смысл в себе несет тот или иной сигнал, сопутствующий пользовательским действиям.
Так, кроме всего прочего, интерфейс часто требует наличия кнопки, которая выступает в роли переключателя. То есть не просто отрабатывает события нажатия и т. д., но может находиться в двух состояниях - нажатом и не нажатом. Естественно, обойти это стороной нельзя. Реализация также очень проста:
- изменяем свойство
checkable
кнопки, ставимtrue
- добавим обработчик
onToggled
- текущее состояние кнопки доступно через свойство
checked
, в контексте нашего примера -testButton.checked
Убираем "лишние" обработчики и получаем следующий базовый пример использования:
Window { width: 320 height: 240 visible: true title: qsTr("QML Button Review") Button { id: testButton anchors.centerIn: parent text: qsTr("testButton") checkable: true onToggled: { testButtonLastEvent.text = qsTr("testButton: onToggled ") + testButton.checked; } } Text { id: testButtonClicks anchors.horizontalCenter: parent.horizontalCenter anchors.top: testButton.bottom anchors.topMargin: 10 text: qsTr("No clicks") } Text { id: testButtonLastEvent anchors.horizontalCenter: parent.horizontalCenter anchors.top: testButtonClicks.bottom anchors.topMargin: 10 text: qsTr("No event") } }
При нажатии меняется состояние кнопки, а также ее цвет. Стиль мы вообще не трогали пока, тем не менее стиль по умолчанию предусматривает изменение цвета при разных действиях. В общем-то, самое время как раз и перейти к кастомизации внешнего вида.
QML Button. Стили. Кастомизация.
Давайте изначально систематизируем - у кнопки есть два объекта для настройки стиля - непосредственно сама прямоугольная область и текст, заключенный внутри нее. Поэтому QML предоставляет нам два соответствующих свойства:
- background
- contentItem
Лучше всего разобраться можно на конкретных примерах, к ним и переходим. Для начала сделаем минимально необходимые вещи, а затем будем постепенно добавлять:
Window { width: 320 height: 240 visible: true title: qsTr("QML Button Review") Button { id: testButton anchors.centerIn: parent text: qsTr("testButton") contentItem: Text { text: testButton.text color: "#ffffff" } background: Rectangle { color: "#005291" } } }
Полезные ссылки - Rectangle, Text - и результат:
Увеличим размер кнопки:
background: Rectangle { implicitWidth: 100 implicitHeight: 50 color: "#005291" }
Проблема налицо, разместим текст по центру и сразу же немного увеличим шрифт:
contentItem: Text { text: testButton.text color: "#ffffff" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter font.pointSize: 10 font.bold: true }
Уважающая себя кнопка должна иметь отклик в виде изменения оформления при действиях пользователя - наведении курсора, нажатии кнопки и т. д. Поэтому добавим цвета:
- normalColor - #005291 - текущий цвет
- hoveredColor - #4587ba - цвет при наведении
- pressedColor - #002948 - цвет при нажатии
Window { width: 320 height: 240 visible: true title: qsTr("QML Button Review") Button { id: testButton anchors.centerIn: parent text: qsTr("testButton") contentItem: Text { text: testButton.text color: "#ffffff" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter font.pointSize: 10 font.bold: true } background: Rectangle { property var normalColor: "#005291" property var hoveredColor: "#4587ba" property var pressedColor: "#002948" implicitWidth: 100 implicitHeight: 50 color: testButton.pressed ? pressedColor : testButton.hovered ? hoveredColor : normalColor } } }
С этим разобрались, теперь мы имеем весь необходимый функционал, предлагаю рассмотреть еще один пример, с градиентами. Про градиенты будет отдельная статья (может быть), так что здесь просто, отбросив сомнения, используем. Добавим еще скругление углов кнопки и рамку по периметру, чтобы охватить бОльшее количество доступных свойств:
Window { width: 320 height: 240 visible: true title: qsTr("QML Button Review") Button { id: testButton anchors.centerIn: parent text: qsTr("testButton") contentItem: Text { text: testButton.text color: "#ffffff" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter font.pointSize: 10 } background: Rectangle { Gradient { id: normalGradient GradientStop { position: 0.0; color: "#252525" } GradientStop { position: 0.5; color: "#e30000" } GradientStop { position: 1.0; color: "#252525" } } Gradient { id: hoveredGradient GradientStop { position: 0.0; color: "#252525" } GradientStop { position: 0.5; color: "#ff5e5e" } GradientStop { position: 1.0; color: "#252525" } } Gradient { id: pressedGradient GradientStop { position: 0.0; color: "#252525" } GradientStop { position: 0.5; color: "#b00000" } GradientStop { position: 1.0; color: "#252525" } } implicitWidth: 100 implicitHeight: 50 gradient: testButton.pressed ? pressedGradient : testButton.hovered ? hoveredGradient : normalGradient radius: 10 border.width: 2.0 border.color: "#000000" } } }
Ну и, пожалуй, на этом сегодня и остановимся, до встречи🤝 Если возникли какие-либо вопросы или проблемы, буду рад помочь с решением, пишите в комментарии, либо на форум.