- 1. Project Structure
- 2. appcore.h
- 3. appcore.cpp
- 4. main.cpp
- 5. main.qml
- 6. Result
- 7. Video tutorial
And we got to transfer data between a layer of QML and C ++ layer. Frankly speaking, the principle is as simple as just using signals and slots in a single layer C ++. Especially in Qt 5.5.
An example will be shown on the basis of the code from the previous tutorial , where we have created a dialog box. But screenshots example works on Android will not be shown, but I assure you - Everything works like a Swiss watch.
Project Structure
Compared to the previous lesson, we had some changes. Namely, it adds a new class, which will be the core of the application.
- appcore.h - application core file header;
- appcore.cpp - application core source file.
And we continue to work and be with QQMLApplicationEngine. You will need to just take from the engine QML context and download it to a new class of object from which the signals in which data will be transmitted will be received.
appcore.h
Our class header file is as simple as a penny. In it there is a one meter (variable of type int), a slot that will increase the counter by one and run signal, which is also one in the class and which will transmit the counter value in QML-interface.
#ifndef APPCORE_H #define APPCORE_H #include <QObject> class AppCore : public QObject { Q_OBJECT public: explicit AppCore(QObject *parent = 0); signals: // Signal to transmit data to interface qml-interface void sendToQml(int count); public slots: // The slot for the receiving of data from the QML-interface void receiveFromQml(); private: int count; // Counter }; #endif // APPCORE_H
appcore.cpp
#include "appcore.h" AppCore::AppCore(QObject *parent) : QObject(parent) { count = 0; } void AppCore::receiveFromQml() { count++; emit sendToQml(count); }
main.cpp
And here we have connected to our class interface written in QML.
#include <QApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include "appcore.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); QQmlApplicationEngine engine; AppCore appCore; QQmlContext *context = engine.rootContext(); /* Load the object in context to establish a connection, * also define the name by which the connection will be * */ context->setContextProperty("appCore", &appCore); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); }
main.qml
Here is part of the code, so as not to distract you from the main. Namely, a compound layer in QML Connections by means of the object as a target is set your class is indicated in the context. Treatment is carried out on the text name that is loaded into the QML engine context, together with the object itself.
To receive signals from the C ++ layer is necessary in the Connections register a function that will be called in much the same way as the target signal, but will start on a signal and then the name with a capital letter. That is, the following logic
- signalToQml - on C++
- onSignalToQml - on QML
But the call Slot will be somewhat different. For example, we have a class object in C ++, to which we refer by name appCore (declared aim in Connections). And then we call the slot. That is as follows: appCore.slotSomething (count).
Call slot in this code is done by pressing the OK button in the dialog, and in Cancel the call does not occur. At the same time, appCore object in C ++ code, the counter increases by one and causes the signal to put counter value in a text label applications.
import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtQuick.Dialogs 1.2 ApplicationWindow { visible: true width: 640 height: 480 title: qsTr("Hello World") color: "white" /* With Connections object * Set up a connection to the core application class * */ Connections { target: appCore // Specify the target connection /* We declare and implement a function as an object parameter and * first name similar to the name of the signal. * The difference is that we add at the beginning on and * then write with a capital letter * */ onSendToQml: { labelCount.text = count // Set the counter to a text label } } MainForm { // Stretch the object of the main window over the parent element anchors.fill: parent // Create text label Text { id: labelCount anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top height: 300 verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter text: "Hello, World !!!" } button1.style: ButtonStyle { // The code from the previous lesson } // Стилизуем вторую кнопку button2.style: ButtonStyle { // The code from the previous lesson } // Start a dialogue by pressing any of the buttons in the main window button1.onClicked: dialogAndroid.open(); button2.onClicked: dialogAndroid.open(); Dialog { id: dialogAndroid width: 600 // set the width of the dialog works on the desktop, but it does not work on Android height: 500 // set the height of the dialog works on the desktop, but it does not work on Android contentItem: Rectangle { width: 600 // Set the width, necessary for Android-devices height: 500 // Set the width, necessary for Android-devices color: "#f7f7f7" // // The area for the dialog box message Rectangle { // The code from the previous lesson } Rectangle { id: dividerHorizontal // The code from the previous lesson } Row { id: row // The code from the previous lesson Button { id: dialogButtonCancel // The code from the previous lesson onClicked: dialogAndroid.close() } Rectangle { id: dividerVertical // The code from the previous lesson } Button { id: dialogButtonOk // The code from the previous lesson onClicked: { /* Before you close the dialog by clicking on OK, * Send the data in the slot application core * */ appCore.receiveFromQml() // And then close the dialogue dialogAndroid.close() } } } } } } }
Result
The result is a simple interaction between the C ++ and QML based on all of the same signals and slots. But the result of the application you can see in the video tutorial.
Не понял, как будет передаваться значение count в QML, если нигде он не описан через
Q_PROPERTY
Так и будет передаваться. Это аргумент сигнала.
А описывать в Q_PROPERTY его не нужно. Не обязательно всё описывать через Q_PROPERTY . Зачастую достаточно объявлять метод сигналом, слотом или Q_INVOKABLE.
Статья интересная, как и реализация, но в упор ругается на
А почему у вас название класса и имя переменной одинаковые?
Проект ваш смотреть нужно, что ещё не доделали, либо просто пересобрать билд для начала, возможно, что при создании файлов класса не обновилась информация для qmake. То есть как минимум перезапустить qmake требуется.
Огромное спасибо за статью.
Спасибо за отзыв!
Здраствуйте сново обращаюсь к вам.
Ваш клас appcore.h и исходник appcore.cpp
main.cpp
вот main.qml
Вот этот момент
frame Y:
не получается перенести значения из С++
Добрый день
И что вы хотели сделать в этом коде?
и это
Думал что через:
frameY: Number (count)
буду контролировать чередование анимаций, картинка 600 на 600 с кадром 100.
если сразу указать:
frameCount: 36
то вся анимация пролистается, а мне необходимо конкретная строка анимации
( ну например пролистать 6 кадров с Y= 200, а потом с Y= 400)
пока на сайтах искал наткнулся на Namber, но не получилось.
Ну ок, такой тип в документации есть, но откуда-то это взяли?
я и намёка не вижу на сам вот этот Count.
Впрочем, после пояснения уже немного яснее стало, думаю, что можете так попробовать сделать
Нет. Не читает.
Попробывал сразу задать значение в appcore
не работает, хотя без ошибок.
Чтобы работало, нужно сигнал высылать, то есть
Спасибо огромное! Заработало!
Доброй ночи. А почему вы не используете MainForm.ui.qml.? В нем я так понимаю надо верстать а в main.qml реализовывать логику?
Добрый день. Формы в QML на мой взгляд не так удобно реализованы, как в классических виджетах. Да, их удобно набрасывать в дизайнере, но при этом много функционала недоступно полноценно через дизайнер. Поэтому мне проще верстать и напрямую писать логику без дизайнера и форм QML.
Добрый день. У меня при реализации проекта по идеалогии описаной выше возникла проблема. Немогу достучатся до элемента ListView.
Пишет : Invalid alias reference. Unable to find id "spinBoxPlusMinus"
Не поможите?) Все таки хочется разделить логику от дизайна)
Ну вы не сможете так прокинуть
На самый верх из делегата. Делегат отвечает за внешнее представление элемента в ListView, а таких элементов могут быть сотни и тысячи. Поэтому в третьей строке вашего кода такой alias является бессмысленным. Поскольку QML не знает к какому именно элементу в списке ему нужно пробрасывать alias.
А вообще ваш вопрос тут немного не по теме. Здесь вопрос сигналов и слотов. А ващ вопрос о доступе к элементу через его парента. Лучше создайте на форуме отдельное обсуждение.
Спасибо. Так и сделаю.
А если мне нужно сделать конект из дочернего qml?
Сигнал работает только из main.qml
Придётся делать ещё сигнал в дочернем qml и пробрасывать через коннекты и обработчики. А вообще нужно смотреть конкретный код и что вы пытаетесь сделать.
Так что лучше будет, если вы зададите вопрос на форуме , чтобы можно было подробнее обсудить вашу проблему.
Извиняюсь, все работает(из-за невнимательности).
Хорошо, ну будут проблемы помимо того, что касается статей, то не стесняйтесь задавать вопросы на форуме.
Приветствую всех! Внедрил данный урок в свой проект, идеально никакой ругани на синтаксис, но...
Добрый день.
У вас сигнал sendToQml в вашем классе объявлен в секции singals? Просто вы говорите о том, что внедрили в свой проект, поэтому следует вопрос о том, что чего-то у вас не хватает.
Внедрен.
Единственное, что было измененно, это название класса.
Внедрен.
Единственное, что было измененно, это название класса.
Наверное, нужно смотреть ваш код, поскольку других мыслей у меня нет, что может быть не так. Можете создать тему на форуме и там показать код, касающийся внедрённой части?
И вообще, qml только ругается этой строчкой, но при этом работает, или тот слот вообще не срабатывает? (уверен, что не срабатывает, но мало ли)
Огромное спасибо вам! Очень понятно и наглядно
У связывания интерейса прогрммы с ядром через контекст (context->setContextProperty("appCore", &appCore);) есть один существенный недостаток, упоминание о котором я нигде не нашел, а выявился он мною империческим путём. А именно - при таком способе соединение сигнал-слот делается ИСКЛЮЧИТЕЛЬНО путем непосредственного доступа к функции (Qt::DirectConnection), задать соединение через событие (Qt::QueuedConnection) у меня не получилось, т.к. такой параметр просто некуда внести. Может это как-то в настройках компиляции можно задать не знаю. Отсюда серьёзная проблема - если интерфейс и тело программы крутятся в разных потоках (а это правильно), то получается, что интерфейсная часть обращается к телу не потокобезопасно. В итоге в моём случае пришлость вернуться к связыванию элементов интерфейса из .cpp фалов классическим connect().
Если знаете как это победить буду рад подсказке!
Здравствуйте!
Прекрасный сайт, отличные статьи.
Не хватает только готовых проектов для скачивания.
Многих комментариев типа appCore != AppCore просто бы не было )))