В статье описывается создание приложения QML через Qt Quick 2.6 с выполнением кода на С++.
В качестве среды для разработки используется Qt 5.6.0 для Windows 64 bit под компилятор Visual Studio.
У меня уже была статья на эту тему. Но эта более актуальная. В данной статье рассматривается другой способ соединения C++ и QML.
В статье http://blog.harrix.org/?p=5079 приведена сокращенная версия данной статьи.
В статье http://blog.harrix.org/?p=5082 рассказывается о подобном приложении, но с использованием компонентов QtQuick Controls.
Статья из цикла «Сложение двух чисел». Для меня минимальное освоение любой системы программирования начинается с возможности создания такой программы. Если можно написать приложение, в которой пользователь может ввести два числа, считать их, провести с ними какие-то действия, а потом вывести результат, то, значит, базовое владение имеется. И много задач именно из области программирования, алгоритмики можно будет решать, зная, как в конкретной системе программирования запрограммировать такую программу.
Содержание
Подготовка
Создадим Qt Quick приложение.
Не рекомендую выбирать With ui.qml. По крайней мере не все функции QML с таким файлом работали. Либо я что-то делал не то.
И переведите разработку приложения в режим Release.
Построение интерфейса
Откроем файл main.qml:
Удалим элемент MouseArea и элемент Text. Они тут демонстрационные.
Добавим в основной элемент Window следующую конструкцию:
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 } } } |
Получим такой код файла main.qml:
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 |
import QtQuick 2.6 import QtQuick.Window 2.2 Window { visible: true 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.
Кратко пробежимся по коду, который у нас получился. Все компоненты мы расположили в главном корневом Window. В нем находится компонент 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.
Там вводим название нашего нового класса, например, HandlerSignals, также добавив подключение инклуда QObject и базовым классом объявляем QObject.
В итоге получаем наш класс:
Начнем его редактирование. Перейдем вначале в заголовочный файл handlersignals.h.
Мы будем создавать экземпляр нашего объекта и передавать в качестве родителя сцену QML объектов.
Подключите файл #include <QVariant>.
handlersignals.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#ifndef HANDLERSIGNALS_H #define HANDLERSIGNALS_H #include <QObject> #include <QVariant> class HandlerSignals : public QObject { Q_OBJECT public: explicit HandlerSignals(QObject *parent = 0); signals: public slots: }; #endif // HANDLERSIGNALS_H |
handlersignals.cpp (без изменений)
1 2 3 4 5 6 |
#include "handlersignals.h" HandlerSignals::HandlerSignals(QObject *parent) : QObject(parent) { } |
Теперь можем добавить наш слот по обработке сигнала. Для примера мы передадим из сигнала текстовой фрагмент, который добавим в итоговое сообщение.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
void HandlerSignals::cppSlot(const QString &msg) { //Найдем строки ввода QObject* textinput1 = this->parent()->findChild<QObject*>("textinput1"); QObject* textinput2 = this->parent()->findChild<QObject*>("textinput2"); //Найдем поле вывода QObject* memo = this->parent()->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+" "+msg); } |
Получим такие файлы:
handlersignals.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#ifndef HANDLERSIGNALS_H #define HANDLERSIGNALS_H #include <QObject> #include <QVariant> class HandlerSignals : public QObject { Q_OBJECT public: explicit HandlerSignals(QObject *parent = 0); signals: public slots: void cppSlot(const QString &msg); }; #endif // HANDLERSIGNALS_H |
handlersignals.cpp
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 |
#include "handlersignals.h" HandlerSignals::HandlerSignals(QObject *parent) : QObject(parent) { } void HandlerSignals::cppSlot(const QString &msg) { //Найдем строки ввода QObject* textinput1 = this->parent()->findChild<QObject*>("textinput1"); QObject* textinput2 = this->parent()->findChild<QObject*>("textinput2"); //Найдем поле вывода QObject* memo = this->parent()->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+" "+msg); } |
В прошлом варианте данного материала (когда писал, например, для Qt 5.5.0) я регистрировал в QML экземпляр класса и вызывал методы-слоты класса. Но при этом QML становился привязан к С++ коду, что не есть хорошо. Поэтому сейчас поступим по другому. Мы в QML документе просто отправим сигнал, что нужно посчитать сумму двух чисел, а уже в С++ поймаем данный сигнал и отправим на обработку в класс.
Вначале отправим сигнал. В main.qml в головном элементе Window добавим сигнал:
1 |
signal qmlSignal(string msg) |
Обратите внимание, что объявление сигнала надо добавлять именно в корневой элемент, иначе мы сигнал не найдем.
А в кнопке в области действия мыши пропишем уже отправку сигнала:
1 2 3 4 5 6 7 |
//Действие мыши MouseArea { id: mouseArea1 anchors.fill: parent hoverEnabled: true; onClicked: {qmlSignal("яблок")} } |
В итоге получим файл main.qml:
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.6 import QtQuick.Window 2.2 Window { visible: true signal qmlSignal(string msg) 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; onClicked: {qmlSignal("яблок")} } } //Строка ввода первого числа 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 } } } } |
Запустите приложение. Конечно, у вас кнопка не сработает пока, но вы сможете проверить написали всё верно или нет.
Теперь, перейдем в файл main.cpp, чтобы прописать создание экземпляра класса и связать его с нужными объектами.
Добавим заголовочный файл:
1 |
#include "handlersignals.h" |
А в функции main после загрузки qml файла пропишем строчки:
1 2 3 4 5 6 |
QObject* root = engine.rootObjects()[0]; HandlerSignals *handlerSignals= new HandlerSignals(root); QObject::connect(root, SIGNAL(qmlSignal(QString)), handlerSignals, SLOT(cppSlot(QString))); |
В первой строчке мы находим корневой объект в QML модели. Во второй строчке создаем экземпляр нашего класса. И в третьей строчке связываем наш слот из класса и сигнал из qml файла.
Теперь main.cpp выглядит вот так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include <QGuiApplication> #include <QQmlApplicationEngine> #include "handlersignals.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]; HandlerSignals *handlerSignals= new HandlerSignals(root); QObject::connect(root, SIGNAL(qmlSignal(QString)), handlerSignals, SLOT(cppSlot(QString))); return app.exec(); } |
Если всё сделали правильно, то при запуск приложения и нажатия на кнопку получим следующее:
Кстати, если у вас в QML используется компонент со своими сигналами, то связь вы устанавливаете с ним. Например:
1 2 3 |
QObject* ob = root->findChild<QObject*>("flatButton"); QObject::connect(ob, SIGNAL(qmlSignalFlatButton(QString)), handlerSignals, SLOT(cppSlot(QString))); |
Вот и всё)