В статье описывается создание приложения QML через Qt Quick Controls 1.5 с выполнением кода на С++.
В качестве среды для разработки используется Qt 5.6.0 для Windows 64 bit под компилятор Visual Studio 2015.
У меня уже была статья на эту тему. Но эта более актуальная. В данной статье рассматривается другой способ соединения C++ и QML.
В отличии от статьи в данной статьи рассматривается построение приложение не с использованием чистого QML, а с использованием QtQuick Controls, то есть компонентов, которые похожи на стандартные компоненты из Qt Widgets и вообще из стандартных приложений. Но получается всё равно QML приложение со всеми плюсами.
В статье http://blog.harrix.org/?p=5079 рассказывается краткий алгоритм построения подобных приложений.
Статья из цикла «Сложение двух чисел». Для меня минимальное освоение любой системы программирования начинается с возможности создания такой программы. Если можно написать приложение, в которой пользователь может ввести два числа, считать их, провести с ними какие-то действия, а потом вывести результат, то, значит, базовое владение имеется. И много задач именно из области программирования, алгоритмики можно будет решать, зная, как в конкретной системе программирования запрограммировать такую программу.
Содержание
Подготовка
Создадим Qt Quick приложение.
Не рекомендую выбирать With ui.qml. По крайней мере не все функции QML с таким файлом работали. Либо я что-то делал не то.
И переведите разработку приложения в режим Release.
Построение интерфейса
Откроем файл main.qml:
Удалим элемент menuBar и Label. Они тут демонстрационные.
Добавим в основной элемент ApplicationWindow следующую конструкцию:
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 |
property alias button1: button1 ColumnLayout { anchors.centerIn: parent Button { id: button1 text: qsTr("Сложить") } TextInput { id: textInput1 objectName: "textInput1" width: 80 height: 20 text: qsTr("1") font.family: "Times New Roman" font.pixelSize: 12 } TextInput { id: textInput2 objectName: "textInput2" width: 80 height: 20 text: qsTr("1") font.pixelSize: 12 } Text { id: memo objectName: "memo" text: qsTr("") font.pixelSize: 12 } } |
Получим такой код файла main.qml (я еще добавил один import):
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 |
import QtQuick 2.6 import QtQuick.Controls 1.5 import QtQuick.Layouts 1.1 ApplicationWindow { visible: true width: 640 height: 480 title: qsTr("Hello World") property alias button1: button1 ColumnLayout { anchors.centerIn: parent Button { id: button1 text: qsTr("Сложить") } TextInput { id: textInput1 objectName: "textInput1" width: 80 height: 20 text: qsTr("1") font.family: "Times New Roman" font.pixelSize: 12 } TextInput { id: textInput2 objectName: "textInput2" width: 80 height: 20 text: qsTr("1") font.pixelSize: 12 } Text { id: memo objectName: "memo" text: qsTr("") font.pixelSize: 12 } } } |
Обратите внимание, что для всех элементов, к которым вы потом захотите обращаться в С++, пропишите не только свойство id, но и свойство objectName со строковым значением совпадающим с id.
Если мы запустим приложение, то получим следующее:
Итак, мы описали интерфейс нашей программы.
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 в головном элементе ApplicationWindow добавим сигнал:
1 |
signal qmlSignal(string msg) |
Обратите внимание, что объявление сигнала надо добавлять именно в корневой элемент, иначе мы сигнал не найдем.
А в кнопке в области действия мыши пропишем уже отправку сигнала:
1 |
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 |
import QtQuick 2.6 import QtQuick.Controls 1.5 import QtQuick.Layouts 1.1 ApplicationWindow { visible: true width: 640 height: 480 title: qsTr("Hello World") property alias button1: button1 signal qmlSignal(string msg) ColumnLayout { anchors.centerIn: parent Button { id: button1 text: qsTr("Сложить") onClicked: qmlSignal("яблок") } TextInput { id: textInput1 objectName: "textInput1" width: 80 height: 20 text: qsTr("1") font.family: "Times New Roman" font.pixelSize: 12 } TextInput { id: textInput2 objectName: "textInput2" width: 80 height: 20 text: qsTr("1") font.pixelSize: 12 } Text { id: memo objectName: "memo" text: qsTr("") font.pixelSize: 12 } } } |
Запустите приложение. Конечно, у вас кнопка не сработает пока, но вы сможете проверить написали всё верно или нет.
Теперь, перейдем в файл 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 <QApplication> #include <QQmlApplicationEngine> #include "handlersignals.h" int main(int argc, char *argv[]) { QApplication 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(); } |
Если всё сделали правильно, то при запуск приложения и нажатия на кнопку получим следующее:
Вот и всё)