В статье описывается создание приложения QML через Qt Quick 2.4 с выполнением кода на С++.
Более новая статья тут.
В качестве среды для разработки используется Qt 5.5.0 for Windows 32-bit.
В статье http://blog.harrix.org/?p=3175 приведена сокращенная версия данной статьи.
В статье http://blog.harrix.org/?p=3183 рассказывается о подобном приложении, но с использованием компонентов QtQuick Controls.
Статья из цикла «Сложение двух чисел». Для меня минимальное освоение любой системы программирования начинается с возможности создания такой программы. Если можно написать приложение, в которой пользователь может ввести два числа, считать их, провести с ними какие-то действия, а потом вывести результат, то, значит, базовое владение имеется. И много задач именно из области программирования, алгоритмики можно будет решать, зная, как в конкретной системе программирования запрограммировать такую программу.
Содержание
Подготовка
Создадим Qt Quick приложение.
Выбираем приложение на чистом qml без виджетов.
Построение интерфейса
Если раньше при создании приложения по умолчанию создавался один qml файл, а именно main.qml, то теперь в main.qml предполагается наличие элемента Window и обработку событий, а внешний вид прописывается в файле MainForm.ui.qml (что-то типа фрагмента для тех кто знаком с Android программированием).
Почистим наши файлы main.qml и MainForm.ui.qml. В данный момент main.qml выглядит вот так:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import QtQuick 2.4 import QtQuick.Window 2.2 Window { visible: true MainForm { anchors.fill: parent mouseArea.onClicked: { Qt.quit(); } } } |
Удалим обработчик mouseArea.onClicked. Он тут демонстрационный и закрывает приложение при клике в любой области окна приложения. Получим вот такой код файла:
1 2 3 4 5 6 7 8 9 10 |
import QtQuick 2.4 import QtQuick.Window 2.2 Window { visible: true MainForm { anchors.fill: parent } } |
Займемся файлом MainForm.ui.qml. Он пока содержит подобный код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import QtQuick 2.4 Rectangle { property alias mouseArea: mouseArea width: 360 height: 360 MouseArea { id: mouseArea anchors.fill: parent } Text { anchors.centerIn: parent text: "Hello World" } } |
Удалим свойство mouseArea, компоненты MouseArea и Text.
1 2 3 4 5 6 7 8 |
import QtQuick 2.4 Rectangle { width: 360 height: 360 } |
Добавим в основной Rectangle следующую конструкцию:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
Column { spacing: 5 anchors.centerIn: parent; //Кнопка Rectangle { id: button //Имя кнопки //Размеры кнопки width: 100 height: 30 //Цвет кнопки color: "#0066ff" //Текст кнопки Text { id: buttonLabel text: "Сложить" color: "#ffffff"; anchors.centerIn: parent; } //Действие мыши MouseArea { id: mouseArea1 anchors.fill: parent hoverEnabled: true; } } //Строка ввода первого числа Rectangle { id: textinputRect1 //Имя строки ввода //Размеры строки ввода width: 100 height: 18 //цвет строки ввода color: "#ffffff" TextInput { id: textinput1 objectName: "textinput1" color: "#0066ff"; selectionColor: "blue" font.pixelSize: 12; width: parent.width-4 anchors.centerIn: parent focus: true text:"1" } } //Строка ввода второго числа Rectangle { id: textinputRect2 //Имя строки ввода //Размеры строки ввода width: 100 height: 18 //цвет строки ввода color: "#ffffff" TextInput { id: textinput2 objectName: "textinput2" color: "#0066ff"; selectionColor: "blue" font.pixelSize: 12; width: parent.width-4 anchors.centerIn: parent focus: true text:"1" } } //Поле вывода Rectangle { id: memoRect //Имя поля вывода //Размеры поле вывода width: 100 height: 35 //Цвет поля вывода color: "#ffffff" TextEdit{ id: memo color: "#0066ff" objectName: "memo" wrapMode: TextEdit.Wrap width:parent.width; readOnly:true } } } |
Получим такой код файла MainForm.ui.qml (я в корневой Rectangle добавил еще фоновый цвет):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
import QtQuick 2.4 Rectangle { width: 360 height: 360 color: "#ececec"; Column { spacing: 5 anchors.centerIn: parent; //Кнопка Rectangle { id: button //Имя кнопки //Размеры кнопки width: 100 height: 30 //Цвет кнопки color: "#0066ff" //Текст кнопки Text { id: buttonLabel text: "Сложить" color: "#ffffff"; anchors.centerIn: parent; } //Действие мыши MouseArea { id: mouseArea1 anchors.fill: parent hoverEnabled: true; } } //Строка ввода первого числа Rectangle { id: textinputRect1 //Имя строки ввода //Размеры строки ввода width: 100 height: 18 //цвет строки ввода color: "#ffffff" TextInput { id: textinput1 objectName: "textinput1" color: "#0066ff"; selectionColor: "blue" font.pixelSize: 12; width: parent.width-4 anchors.centerIn: parent focus: true text:"1" } } //Строка ввода второго числа Rectangle { id: textinputRect2 //Имя строки ввода //Размеры строки ввода width: 100 height: 18 //цвет строки ввода color: "#ffffff" TextInput { id: textinput2 objectName: "textinput2" color: "#0066ff"; selectionColor: "blue" font.pixelSize: 12; width: parent.width-4 anchors.centerIn: parent focus: true text:"1" } } //Поле вывода Rectangle { id: memoRect //Имя поля вывода //Размеры поле вывода width: 100 height: 35 //Цвет поля вывода color: "#ffffff" TextEdit{ id: memo color: "#0066ff" objectName: "memo" wrapMode: TextEdit.Wrap width:parent.width; readOnly:true } } } } |
Обратите внимание, что для всех элементов, к которым вы потом захотите обращаться в С++, пропишите не только свойство id, но и свойство objectName со строковым значением совпадающим с id.
Кратко пробежимся по коду, который у нас получился. Все компоненты мы расположили в главном корневом Rectangle. В нем находится компонент Column, который располагает находящиеся в нем компоненты в столбик (вертикальная разметка компонентов).
1 2 3 4 5 |
Column { spacing: 5 anchors.centerIn: parent; ... } |
Внутри него находится четыре компонента Rectangle.
Первый имеет id равное button, и это обычная кнопка, а точнее закрашенный прямоугольник с текстом внутри и областью для воздействия мышью с id равным mouseArea1.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
//Кнопка Rectangle { id: button //Имя кнопки //Размеры кнопки width: 100 height: 30 //Цвет кнопки color: "#0066ff" //Текст кнопки Text { id: buttonLabel text: "Сложить" color: "#ffffff"; anchors.centerIn: parent; } //Действие мыши MouseArea { id: mouseArea1 anchors.fill: parent hoverEnabled: true; } } |
Второй и третий Rectangle содержит компонент TextInput, то есть строку для ввода информации. В них будем записывать два наших числа.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
//Строка ввода первого числа Rectangle { id: textinputRect1 //Имя строки ввода //Размеры строки ввода width: 100 height: 18 //цвет строки ввода color: "#ffffff" TextInput { id: textinput1 objectName: "textinput1" color: "#0066ff"; selectionColor: "blue" font.pixelSize: 12; width: parent.width-4 anchors.centerIn: parent focus: true text:"1" } } //Строка ввода второго числа Rectangle { id: textinputRect2 //Имя строки ввода //Размеры строки ввода width: 100 height: 18 //цвет строки ввода color: "#ffffff" TextInput { id: textinput2 objectName: "textinput2" color: "#0066ff"; selectionColor: "blue" font.pixelSize: 12; width: parent.width-4 anchors.centerIn: parent focus: true text:"1" } } |
В последнем четвертом Rectangle содержится поле для вывода текста: TextEdit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
//Поле вывода Rectangle { id: memoRect //Имя поля вывода //Размеры поле вывода width: 100 height: 35 //Цвет поля вывода color: "#ffffff" TextEdit{ id: memo color: "#0066ff" objectName: "memo" wrapMode: TextEdit.Wrap width:parent.width; readOnly:true } } |
Если мы запустим приложение, то получим следующее:
Итак, мы описали интерфейс нашей программы.
C++ часть
При нажатии на кнопку пока ничего не происходит. Исправим это. Для начала установим взаимосвязь между QML моделью и C++ кодом. Для этого создадим класс, через которое будем осуществлять взаимодействие.
Правой кнопкой щелкнем по проекту и выбираем пункт Add New....
Там выбираем C++ Class.
Там вводим название нашего нового класса, например, myClass, также добавив подключение инклуда QObject.
В итоге получаем наш класс:
Начнем его редактирование. Перейдем вначале в заголовочный файл myclass.h. Сейчас он имеет такой вид:
1 2 3 4 5 6 7 8 9 10 11 12 |
#ifndef MYCLASS_H #define MYCLASS_H #include <QObject> class myClass { public: myClass(); }; #endif // MYCLASS_H |
Поменяем содержимое на такое:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#ifndef MYCLASS_H #define MYCLASS_H #include <QObject> #include <QDebug> class MyClass : public QObject { Q_OBJECT public: MyClass(QObject *QMLObject) : viewer(QMLObject) {} signals: public slots: void buttonClicked(const QString& in); protected: QObject *viewer; }; #endif // MYCLASS_H |
Что мы сделали?
- Подключили модули QDebug – это лишь для отображения дебажной информации.
- Сделали класс наследником класса QObject.
- Добавили мета-информацию Q_OBJECT.
- Прописали пустой конструктор класса с начальным присвоением объекта viewer.
- Добавили объект viewer. Это корневой объект все нашей сцены, в которой будут располагаться все наши объекты из qml формы.
- И прописали метод, обрабатывающий клик кнопки, который принимает в качестве параметра некоторую строку: void buttonClicked(const QString& in).
Перейдем в файл myclass.cpp. На данный момент он выглядит так:
1 2 3 4 5 6 |
#include "myclass.h" myClass::myClass() { } |
Конструктор, реализованный тут, нам больше не нужен, так как конструктор прописали в заголовочном файле. А вот метод buttonClicked реализовать нужно. Меняем содержимое файла на такое:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#include "myclass.h" void MyClass::buttonClicked(const QString& in) { qDebug() << in;//Просто выведем информацию строки in в консоль Debug. Просто так. //Найдем строки ввода QObject* textinput1 = viewer->findChild<QObject*>("textinput1"); QObject* textinput2 = viewer->findChild<QObject*>("textinput2"); //Найдем поле вывода QObject* memo = viewer->findChild<QObject*>("memo"); //Считаем информацию со строк ввода через свойство text QString str1=(textinput1->property("text")).toString(); QString str2=(textinput2->property("text")).toString(); int a = str1.toInt();//Переведем строку в число int b = str2.toInt();//Переведем строку в число int c = a + b; //Вычисления наши QString strResult=QString::number(c);//Переведем результат в строку //Ну и наконец выведем в поле вывода нашу информацию memo->setProperty("text", str1+" + "+str2+" = "+strResult); } |
Что тут у нас?
- Элементы в QML документы мы находим через функцию findChild.
- А изменяем через функцию setProperty.
- viewer – это тот QObject, который использовался при создании класса.
Приложение сейчас должно запуститься, но при нажатию на кнопку ничего не произойдет.
Это объясняется тем, что мы не связали наш метод с QML моделью. К тому же метод клика на кнопку реализован в классе, экземпляр которого мы не создали.
Вначале пропишем вызов в QML коде. Для этого откроем файл MainForm.ui.qml. И там в объекте MouseArea пропишем строчку:
1 |
onClicked: _myClass.buttonClicked("Worked?") |
При этом скорее всего у вас строчка будет поддчеркиваться красным, так как Qt Creator считает, что нельзя функции прописывать в файлах qml форм. Но это можно. Пример того как сделать, чтобы не поддчеркивалось, рассмотрено в примере с контролами (нужно вынести в main.qml обработчик, но пока пойдем по простому пути).
В качестве параметра мы передали строку “Worked?”, которую мы в методе buttonClicked первой же строчкой просто так выведем в Debug консоль.
Теперь, перейдем в файл main.cpp, чтобы прописать создание экземпляра класса и связать его с нужными объектами.
Пока данный файл имеет вид:
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <QGuiApplication> #include <QQmlApplicationEngine> int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); } |
Добавим два заголовочных файла:
1 2 |
#include <QQmlContext> #include "myclass.h" |
А в функции main после загрузки qml файла пропишем строчки:
1 2 3 |
QObject* root = engine.rootObjects()[0]; MyClass myClass(root); engine.rootContext()->setContextProperty("_myClass", &myClass); |
В первой строчке мы находим корневой объект в QML модели. Во второй строчке создаем экземпляр нашего класса. И в третьей строчке связываем наш класс и qml модель.
Теперь main.cpp выглядит вот так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include "myclass.h" int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); QObject* root = engine.rootObjects()[0]; MyClass myClass(root); engine.rootContext()->setContextProperty("_myClass", &myClass); return app.exec(); } |
Если всё сделали правильно, то при запуск приложения и нажатия на кнопку получим следующее:
Вот и всё)