- 1. Projektstruktur mit QML
- 2. appcore.h
- 3. appcore.cpp
- 4. main.cpp
- 5. main.qml
- 6. Ergebnis
- 7. Videoanleitung
Und hier sind wir beim Datentransfer zwischen der QML-Schicht und der C++-Schicht. Fairerweise ist das Prinzip so einfach wie die Verwendung von Signalen und Slots in derselben C++-Schicht. Vor allem in Qt 5.5.
Ein Beispiel wird basierend auf dem Code aus der vorherigen Lektion gezeigt, in dem wir ein Dialogfeld erstellt haben. Screenshots eines Arbeitsbeispiels auf Android werden jedoch nicht angezeigt, aber ich versichere Ihnen - Alles funktioniert wie eine Schweizer Uhr.
Projektstruktur mit QML
Projektstruktur mit qml Im Vergleich zur vorherigen Lektion haben wir einige Änderungen vorgenommen. Es wurde nämlich eine neue Klasse hinzugefügt, die den Kern der Anwendung bilden wird.
- appcore.h - Header-Datei des Anwendungskerns;
- appcore.cpp - Quellcodedatei des Anwendungskerns.
Und wir werden auch weiterhin mit QQMLApplicationEngine arbeiten. Sie brauchen nur einen Kontext aus der QML-Engine zu nehmen und ein Objekt einer neuen Klasse hineinzuladen, von dem Signale kommen und an welche Daten übertragen werden.
appcore.h
Die Header-Datei unserer Klasse ist so einfach wie drei Kopeken. Es hat einen einzigen Zähler (Variable vom Typ int ), einen Steckplatz, der den Zähler um eins erhöht und ein Signal auslöst, das ebenfalls eines in der Klasse ist und den Zählerwert an die QML-Schnittstelle überträgt
#ifndef APPCORE_H #define APPCORE_H #include <QObject> class AppCore : public QObject { Q_OBJECT public: explicit AppCore(QObject *parent = 0); signals: // Сигнал для передачи данных в qml-интерфейс void sendToQml(int count); public slots: // Слот для приёма данных из qml-интерфейса void receiveFromQml(); private: int count; // Счетчик, которым будем оперировать }; #endif // APPCORE_H
appcore.cpp
Nun, die Logik der Klasse in ihrem Quellcode.
#include "appcore.h" AppCore::AppCore(QObject *parent) : QObject(parent) { count = 0; } void AppCore::receiveFromQml() { count++; emit sendToQml(count); }
main.cpp
Und hier werden wir bereits die in QML geschriebene Schnittstelle mit unserer Klasse verbinden.
#include <QApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include "appcore.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); QQmlApplicationEngine engine; // Создаём движок qml AppCore appCore; // Создаём ядро приложения QQmlContext *context = engine.rootContext(); // Создаём корневой контекст /* Загружаем объект в контекст для установки соединения, * а также определяем имя, по которому будет происходить соединение * */ context->setContextProperty("appCore", &appCore); // И загружаем в него исходники qml engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); }
main.qml
Ich werde einen Teil des Codes geben, um Sie nicht von der Hauptsache abzulenken. Die Verbindung in der QML -Schicht wird nämlich über das Connections -Objekt hergestellt, dessen Ziel Ihre Klassengruppe im Kontext ist. Die Referenz erfolgt über den Textnamen, der zusammen mit dem Objekt selbst in den QML -Engine-Kontext geladen wird.
Um Signale von der C++-Schicht zu empfangen, müssen Sie in Connections eine Funktion schreiben, die fast so heißt wie das Signal des Zielobjekts, aber mit on beginnt und dann den Namen des Signals mit einem Großbuchstaben . Das heißt, die Logik ist wie folgt
- signalToQml - in C ++
- onSignalToQml - in QML
Aber die Herausforderung des Slots wird auf eine etwas andere Art und Weise stattfinden. Zum Beispiel haben wir ein Klassenobjekt in C++ , auf das wir unter dem Namen appCore zugreifen (als Ziel in Connections deklariert). Und dann rufen wir die Slot-Funktion auf. Das heißt wie folgt: appCore.slotSomething (count).
Der Aufruf des Slots in diesem Code erfolgt durch Drücken der OK -Schaltfläche im Dialog, und durch Abbrechen erfolgt der Aufruf nicht. Gleichzeitig erhöht das appCore-Objekt in C++-Code den Zähler um eins und ruft ein Signal auf, um den Wert des Zählers in das Textlabel der Anwendung einzutragen.
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" /* С помощью объекта Connections * Устанавливаем соединение с классом ядра приложения * */ Connections { target: appCore // Указываем целевое соединение /* Объявляем и реализуем функцию, как параметр * объекта и с имененем похожим на название сигнала * Разница в том, что добавляем в начале on и далее пишем * с заглавной буквы * */ onSendToQml: { labelCount.text = count // Устанавливаем счётчик в текстовый лейбл } } MainForm { // Растягиваем объект главного окна по всему родительскому элементу anchors.fill: parent // Создадим текстовый лейбл 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 { // Программный код из предыдущего урока } // Стилизуем вторую кнопку button2.style: ButtonStyle { // Программный код из предыдущего урока } // Запускаем диалог по нажатию любой из кнопок в главном окне button1.onClicked: dialogAndroid.open(); button2.onClicked: dialogAndroid.open(); // Создаём объект диалогового окна Dialog { id: dialogAndroid /* Когда деплоите под Android-устройства, * обязательно закоментируйте эти две строки, * иначе словите глюки в работе устройства */ width: 600 // Задаём ширину диалога, работает на десктопе, но на Android не сработает height: 500 // Задаём высоту диалога, работает на декстопе, но на Android не сработает // Создаём содержимое диалогового окна contentItem: Rectangle { width: 600 // Устанавливаем ширину, необходимо для Android-устройства height: 500 // Устанавливаем высоту, необходимо для Android-устройства color: "#f7f7f7" // Задаём цвет // Область для сообщения диалогового окна Rectangle { // Программный код из предыдущего урока } // Создаём горизонтальный разделитель с помощью Rectangle Rectangle { id: dividerHorizontal // Программный код из предыдущего урока } /* Создаём подложку для кнопок в виде объекта Строки * В данном объекте для объектов детей не работают некоторые параметры * anchors, кроме параметров anchors.top и anchors.bottom */ Row { id: row // Программный код из предыдущего урока Button { id: dialogButtonCancel // Программный код из предыдущего урока // По нажатию кнопки закрываем диалог onClicked: dialogAndroid.close() } // Создаём разделитель между кнопками шириной в 2 пикселя Rectangle { id: dividerVertical // Программный код из предыдущего урока } Button { id: dialogButtonOk // Программный код из предыдущего урока // По нажатию кнопки закрываем диалог onClicked: { /* Прежде, чем закрывать диалог по OK кнопке, * отправим данные в слот ядра приложения * */ appCore.receiveFromQml() // А потом и закроем диалог dialogAndroid.close() } } } } } } }
Ergebnis
Als Ergebnis erhalten wir eine einfache Interaktion zwischen C++ und QML basierend auf denselben Signalen und Slots. Nun, das Ergebnis der Anwendung können Sie im Video-Tutorial sehen.
Не понял, как будет передаваться значение 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 просто бы не было )))