В статье описывается создание приложения QML через Qt Quick Controls 2 (раньше назывались Qt Labs Controls) с выполнением кода на С++.
В качестве среды для разработки используется Qt 5.7.0 для Windows 64 bit под компилятор Visual Studio 2015.
Статья из цикла «Сложение двух чисел». Для меня минимальное освоение любой системы программирования начинается с возможности создания такой программы. Если можно написать приложение, в которой пользователь может ввести два числа, считать их, провести с ними какие-то действия, а потом вывести результат, то, значит, базовое владение имеется. И много задач именно из области программирования, алгоритмики можно будет решать, зная, как в конкретной системе программирования запрограммировать такую программу.
Содержание
Подготовка
Создадим Qt Quick приложение.
И переведите разработку приложения в режим Release.
По умолчанию создается в болванке три qml файла. Удалим два из них — они нам пока не нужны.
Построение интерфейса
Откроем файл 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 |
import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.3 import QtQuick.Controls.Material 2.0 ApplicationWindow { visible: true width: 640 height: 480 title: qsTr("Сложение двух чисел") Material.theme: Material.Light Material.primary: Material.BlueGray Material.accent: Material.Teal Column { spacing: 10 anchors.centerIn: parent TextField { id: field1 objectName: "field1" placeholderText: "Введите первое число" width: 250 } TextField { id: field2 objectName: "field2" placeholderText: "Введите второе число" width: 250 } Button { id: button text: qsTr("Сложить") width: 250 } Text { text: "Ответ:" font.pixelSize: textArea.font.pixelSize } TextArea { id: textArea objectName: "textArea" wrapMode: TextArea.Wrap readOnly: true width: 250 } } } |
Обратите внимание, что для всех элементов, к которым вы потом захотите обращаться в С++, пропишите не только свойство id, но и свойство objectName со строковым значением совпадающим с id.
Перейдите в файл .pro вашего проекта и добавьте там quickcontrols2 в строчке QT += qml quick:
Запустите проект:
Как видите, болванка приложения заработала, однако, Material дизайн в стилях отсутствует.
Перейдем в файл main.cpp. Подключите инклуд:
1 |
#include <QQuickStyle> |
И пропишите строчку:
1 |
QQuickStyle::setStyle("Material"); |
В итоге у меня файл принял вид:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQuickStyle> int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); QQuickStyle::setStyle("Material"); QQmlApplicationEngine engine; engine.load(QUrl(QLatin1String("qrc:/main.qml"))); return app.exec(); } |
Если мы запустим приложение, то получим следующее:
Итак, мы описали интерфейс нашей программы.
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* field1 = this->parent()->findChild<QObject*>("field1"); QObject* field2 = this->parent()->findChild<QObject*>("field2"); //Найдем поле вывода QObject* textArea = this->parent()->findChild<QObject*>("textArea"); //Считаем информацию со строк ввода через свойство text QString str1=(field1->property("text")).toString(); QString str2=(field2->property("text")).toString(); int a = str1.toInt();//Переведем строку в число int b = str2.toInt();//Переведем строку в число int c = a + b; //Вычисления наши QString strResult=QString::number(c);//Переведем результат в строку //Ну и наконец выведем в поле вывода нашу информацию textArea->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* field1 = this->parent()->findChild<QObject*>("field1"); QObject* field2 = this->parent()->findChild<QObject*>("field2"); //Найдем поле вывода QObject* textArea = this->parent()->findChild<QObject*>("textArea"); //Считаем информацию со строк ввода через свойство text QString str1=(field1->property("text")).toString(); QString str2=(field2->property("text")).toString(); int a = str1.toInt();//Переведем строку в число int b = str2.toInt();//Переведем строку в число int c = a + b; //Вычисления наши QString strResult=QString::number(c);//Переведем результат в строку //Ну и наконец выведем в поле вывода нашу информацию textArea->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 51 52 53 54 55 56 57 58 |
import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.3 import QtQuick.Controls.Material 2.0 ApplicationWindow { visible: true width: 640 height: 480 title: qsTr("Сложение двух чисел") Material.theme: Material.Light Material.primary: Material.BlueGray Material.accent: Material.Teal signal qmlSignal(string msg) Column { spacing: 10 anchors.centerIn: parent TextField { id: field1 objectName: "field1" placeholderText: "Введите первое число" width: 250 } TextField { id: field2 objectName: "field2" placeholderText: "Введите второе число" width: 250 } Button { id: button text: qsTr("Сложить") width: 250 onClicked: qmlSignal("яблок") } Text { text: "Ответ:" font.pixelSize: textArea.font.pixelSize } TextArea { id: textArea objectName: "textArea" wrapMode: TextArea.Wrap readOnly: true width: 250 } } } |
Запустите приложение. Конечно, у вас кнопка не сработает пока, но вы сможете проверить есть ли ошибки или нет при компиляции.
Теперь, перейдем в файл 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 22 23 24 25 |
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQuickStyle> #include "handlersignals.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); QQuickStyle::setStyle("Material"); QQmlApplicationEngine engine; engine.load(QUrl(QLatin1String("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))); |
Вот и всё)