Solved Acess engine within function
-
Hey guys.
I am trying to get a basic QML app working. After pushing a button I want to check a sqlite file for results and push the data to QML. I have the database part working and I can get the data pushed to the screen, but only when I put the code inside main(). The function that get's launched once I push the button can't access the engine object. How can I make this global so I can push data to QML inside a function?
main.cpp
int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); MyClass myClass; QQmlApplicationEngine engine; QQmlEngine engine2; QQmlComponent component(&engine2,QUrl(QStringLiteral("qrc:/MainForm.ui.qml"))); QObject *object = component.create(); engine.rootContext()->setContextProperty(QStringLiteral("obj"), &myClass); QString decile = "N/A"; QString postcode = "N/A"; //engine.rootContext()->setContextProperty("decile", decile); //engine.rootContext()->setContextProperty("postcode", postcode); //qDebug() << "Property value:" << QQmlProperty::read(object, "icoordlat"); //qDebug() << "Property value:" << QQmlProperty::read(object, "icoordlon"); QVariant latitude = QQmlProperty::read(object, "icoordlat"); QVariant longitude = QQmlProperty::read(object, "icoordlon"); /* database code */ engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); } void MyClass::clickedButton() { QString decile ="n/a"; QString postcode = "n/a"; //QQmlApplicationEngine engine; QQmlEngine engine2; QQmlComponent component(&engine2,QUrl(QStringLiteral("qrc:/MainForm.ui.qml"))); QObject *object2 = component.create(); <<<<<< do I need to reinitialize this? MyClass myClass; */ grab result from database code */ engine->rootContext()->setContextProperty("decile", decile); <<<<< how to access engine within this function? engine->rootContext()->setContextProperty("postcode", postcode) }
main.qml
Item { Window { visible: true MainForm { toolButton1.onClicked: { obj.clickedButton(); } anchors.fill: parent } } }
MainForm.ui.qml
import QtQuick 2.5 import QtPositioning 5.5 import QtLocation 5.5 import QtQuick.Controls 1.4 Rectangle { width: 360 height: 360 property alias item1: item1 property alias toolButton1: toolButton1 property alias src: src property string icoordlat: src.position.coordinate.latitude property string icoordlon: src.position.coordinate.longitude Item { id: item1 PositionSource { id: src updateInterval: 1000 active: true property double icoordlat: src.position.coordinate.latitude property double icoordlon: src.position.coordinate.longitude } } MouseArea { id: mouseArea anchors.rightMargin: 0 anchors.bottomMargin: 0 anchors.leftMargin: 0 anchors.topMargin: 0 anchors.fill: parent Text { id: t1 width: 343 height: 145 font.pointSize: 60 anchors.centerIn: parent text: decile anchors.verticalCenterOffset: 9 anchors.horizontalCenterOffset: 1 } Text { id: t2 width: 343 height: 106 font.pointSize: 44 anchors.centerIn: parent text: postcode anchors.verticalCenterOffset: -119 anchors.horizontalCenterOffset: 1 } Text { id: t3 width: 343 height: 106 font.pointSize: 16 anchors.centerIn: parent text: src.position.coordinate.latitude anchors.verticalCenterOffset: 120 anchors.horizontalCenterOffset: 1 } Text { id: t4 width: 343 height: 106 font.pointSize: 16 anchors.centerIn: parent text: src.position.coordinate.longitude anchors.verticalCenterOffset: 160 anchors.horizontalCenterOffset: 1 } ToolButton { id: toolButton1 width: 233 height: 233 anchors.verticalCenterOffset: 200 text: "Refresh" } } }
I've been going round in circles for days now. I just purchased a book on Qt so hopefully won't be scratching my head too much longer but any help to get moving forward would be great. Thanks
-
Well I want to return/update the values every time the button is pressed based upon the current qml co-ordinates at that time. I can get the function to launch and grab the appropriate data when I push the button. I just can't update the screen at that point with the new data because engine.rootcontext() doesn't appear to be global. Basically, how can I push stuff to the screen within a function, or is there another more appropriate way to do it?
-
Hi,
Can you explain what exactly you want to return to your QML ?
Looks like using a signal would be simpler or depending what you want to communicate maybe use a property.
-
Hi SGaist
I am simply returning 2 string values from a sqlite database. It works fine if I put all the code in main() but the problem is I want to be able to execute this every time I click the button so I created a function (which can't attach to qml)
-
Do you want to return them as a result of the function call ?
-
Well I want to return/update the values every time the button is pressed based upon the current qml co-ordinates at that time. I can get the function to launch and grab the appropriate data when I push the button. I just can't update the screen at that point with the new data because engine.rootcontext() doesn't appear to be global. Basically, how can I push stuff to the screen within a function, or is there another more appropriate way to do it?
-
One thing I didn't notice at first. You are creating a new engine, a new ui and a new temporary MyClass object in your clickedButton slots. Looks that's not really what you wanted to do.
A cleaner way would be to add two properties to your MyClass object and update them with the content of your database.
class MyClass : public QObject { Q_OBJECT Q_PROPERTY(QString decile READ decile WRITE setDecile NOTIFY decileChanged) Q_PROPERTY(QString postCode READ postCode WRITE setPostCode NOTIFY postCodeChanged) QString postCode() const { return _postCode; } signals: void decileChanged(const QString& decile); void postCodeChanged(const QString& postCode); public slots: void setDecile(const QString& decile) { if (_decile == decile) { return; } _decile = decile; emit decileChanged(decile); } // same for post code void update(/*Parameters you need*/) { // grab data from your database setDecile(decileFromDatabase); setPostCode(postCodeFromDatabase); } private: QString _decile; QString _postCode; };
Then you only have to use the object in your QML and call
update
where needed.WARNING: Not built nor tested. There might be better designs but the specs were a little short.
-
Thanks very much for your reply. I am currently trying to implement it now.
The problem is how do I read the qml latitude and longitude inside the update function. The current GPS co-ordinates are required in order to complete the database check.void update(/*Parameters you need*/) { *** Need GPS co-ordinates to grab data *** //QVariant latitude = QQmlProperty::read(object2, "icoordlat"); //QVariant longitude = QQmlProperty::read(object2, "icoordlon"); **<<<< how to read qml variables here?** // grab data from your database setDecile(decileFromDatabase); setPostCode(postCodeFromDatabase); }
Thanks for all your help
-
What are you using to get the GPS data ?
-
I used QQmlProperty to read *object
MyClass myClass; QQmlApplicationEngine engine; QQmlEngine engine2; QQmlComponent component(&engine2,QUrl(QStringLiteral("qrc:/MainForm.ui.qml"))); QObject *object = component.create(); // QVariant latitude = QQmlProperty::read(object, "icoordlat"); QVariant longitude = QQmlProperty::read(object, "icoordlon");
-
It looks like you are trying to go back and forth between QML and C++ rather than trying to use the usual channels to communicate between both.
Where are you setting these two values ?
-
Hey I set them in my MainForm.ui.qml. From my research of QML it seemed this was the common way to communicate between C++ and QML. Basically, I am trying to get these qt positioning values over to C++ so that they can be evaluated against a database and then the output presented on the screen. I didn't think it would be this difficult to interface between the two. Any help sending me down the right path would be great.
Mainform.ui.qml
import QtQuick 2.5 import QtPositioning 5.5 import QtLocation 5.5 import QtQuick.Controls 1.4 Rectangle { width: 360 height: 360 property alias item1: item1 property alias toolButton1: toolButton1 property alias src: src property string icoordlat: src.position.coordinate.latitude property string icoordlon: src.position.coordinate.longitude Item { id: item1 PositionSource { id: src updateInterval: 1000 active: true property double icoordlat: src.position.coordinate.latitude property double icoordlon: src.position.coordinate.longitude } } MouseArea { id: mouseArea anchors.rightMargin: 0 anchors.bottomMargin: 0 anchors.leftMargin: 0 anchors.topMargin: 0 anchors.fill: parent Text { id: t1 width: 343 height: 145 font.pointSize: 60 anchors.centerIn: parent text: decile anchors.verticalCenterOffset: 9 anchors.horizontalCenterOffset: 1 } Text { id: t2 width: 343 height: 106 font.pointSize: 44 anchors.centerIn: parent text: postcode anchors.verticalCenterOffset: -119 anchors.horizontalCenterOffset: 1 } Text { id: t3 width: 343 height: 106 font.pointSize: 16 anchors.centerIn: parent text: src.position.coordinate.latitude anchors.verticalCenterOffset: 120 anchors.horizontalCenterOffset: 1 } Text { id: t4 width: 343 height: 106 font.pointSize: 16 anchors.centerIn: parent text: src.position.coordinate.longitude anchors.verticalCenterOffset: 160 anchors.horizontalCenterOffset: 1 } ToolButton { id: toolButton1 width: 233 height: 233 anchors.verticalCenterOffset: 200 text: "Refresh" } } }
This is where I get the function to execute on button push
main.qml
Window { visible: true MainForm { toolButton1.onClicked: { obj.clickedButton(); } anchors.fill: parent } }
-
In your
onClick
handler, you can retrieve the values fromicoordlat
,icoordlon
since they are properties of your Mainform and pass them toclickButton
.
Some thing likeonClicked: { obj.clicked(icoordlat, icoordlon); }
No need to try to go through the engine.
I'd recommend taking a look at the QtQml C++ integration chapter from Qt's documentation.
-
Hi thanks very much for the reply again. I have modified my code and I am able to get the application to compile.
I'm able to pass the long & lat over to C++ and get the result (seen with qDebug) but the qml screen data does not get refreshed with :setDecile(decile);
setPostCode(postCode);main.h
#ifndef MAIN_H #define MAIN_H #include <QObject> #include <QDebug> #include <QQuickItem> #include <QGuiApplication> #include <QQmlApplicationEngine> #include <QDebug> #include <QUrl> #include <QQmlContext> #include <QQmlComponent> #include <QQmlProperty> #include <QSqlDatabase> #include <QtSql> #include <QtPositioning/QGeoCoordinate> #include <QtNetwork/QNetworkAccessManager> #include <QtNetwork/QNetworkReply> #include <QGeoAddress> #include <QObject> #include <QQuickItem> #include <QQuickView> class MyClass : public QObject { Q_OBJECT Q_PROPERTY(QString decile READ decile WRITE setDecile NOTIFY decileChanged) Q_PROPERTY(QString postCode READ postCode WRITE setPostCode NOTIFY postCodeChanged) QString postCode() const { return _postCode; } QString decile() const { return _decile; } signals: void decileChanged(const QString& decile); void postCodeChanged(const QString& postCode); public slots: void setDecile(const QString& decile) { if (_decile == decile) { return; } _decile = decile; emit decileChanged(decile); } void setPostCode(const QString& postCode) { if (_postCode == postCode) { return; } _postCode = postCode; emit postCodeChanged(postCode); } void update(QString icoordlat, QString icoordlon) { QString dbname = "affluence.sqlite"; QString filePath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); filePath.append( "/"+dbname); QSqlDatabase dbsqlite = QSqlDatabase::addDatabase("QSQLITE","dbsqlite"); dbsqlite.setDatabaseName(filePath); dbsqlite.open(); if (dbsqlite.open()) { QString Qlatitude2 = icoordlat.left(6); QString Qlongitude2 = icoordlon.left(6); QString perc = "%"; Qlatitude2 = Qlatitude2 + perc; Qlongitude2 = Qlongitude2 + perc; qDebug() << "soup: " << Qlatitude2 << Qlongitude2; QSqlQuery querydb(dbsqlite); querydb.prepare("select * FROM affluence WHERE latitude LIKE :Qlatitude AND longitude LIKE :Qlongitude"); querydb.bindValue(":Qlatitude", Qlatitude2); querydb.bindValue(":Qlongitude", Qlongitude2); querydb.exec(); while (querydb.next()) { QString Qlatitude3 = querydb.value(4).toString(); QString Qlongitude3 = querydb.value(5).toString(); QString decile = querydb.value(3).toString(); QString postCode = querydb.value(2).toString(); setDecile(decile); setPostCode(postCode); qDebug() << "latitude: " << Qlatitude3 << " longitude: " << Qlongitude3 << " decile: " << decile << "post code:" << postCode; } } else { qDebug() << "can't connect to sqlite " << dbsqlite.lastError(); } } private: QString _decile; QString _postCode; }; #endif // MAIN_H
main.cpp
int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); MyClass myClass; QQmlApplicationEngine engine; QQmlEngine engine2; QQmlComponent component(&engine2,QUrl(QStringLiteral("qrc:/MainForm.ui.qml"))); QObject *object = component.create(); engine.rootContext()->setContextProperty(QStringLiteral("obj"), &myClass); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); }
main.qml
import QtQuick 2.5 import QtQuick.Window 2.2 import QtPositioning 5.5 import QtLocation 5.6 import QtPositioning 5.2 Item { Window { visible: true MainForm { toolButton1.onClicked: { obj.update(icoordlat, icoordlon); } anchors.fill: parent } } }
MainForm.ui.qml
import QtQuick 2.5 import QtPositioning 5.5 import QtLocation 5.5 import QtQuick.Controls 1.4 Rectangle { width: 360 height: 360 property alias item1: item1 property alias toolButton1: toolButton1 property alias src: src property string icoordlat: src.position.coordinate.latitude property string icoordlon: src.position.coordinate.longitude Item { id: item1 PositionSource { id: src updateInterval: 1000 active: true property double icoordlat: src.position.coordinate.latitude property double icoordlon: src.position.coordinate.longitude } } MouseArea { id: mouseArea anchors.rightMargin: 0 anchors.bottomMargin: 0 anchors.leftMargin: 0 anchors.topMargin: 0 anchors.fill: parent Text { id: t1 width: 343 height: 145 font.pointSize: 60 anchors.centerIn: parent text: decile anchors.verticalCenterOffset: 9 anchors.horizontalCenterOffset: 1 } Text { id: t2 width: 343 height: 106 font.pointSize: 44 anchors.centerIn: parent text: postcode anchors.verticalCenterOffset: -119 anchors.horizontalCenterOffset: 1 } Text { id: t3 width: 343 height: 106 font.pointSize: 16 anchors.centerIn: parent text: src.position.coordinate.latitude anchors.verticalCenterOffset: 120 anchors.horizontalCenterOffset: 1 } Text { id: t4 width: 343 height: 106 font.pointSize: 16 anchors.centerIn: parent text: src.position.coordinate.longitude anchors.verticalCenterOffset: 160 anchors.horizontalCenterOffset: 1 } ToolButton { id: toolButton1 width: 233 height: 233 anchors.verticalCenterOffset: 200 text: "Refresh" } } }
-
I may have missed it but I don't see any use of
obj
properties in your code. -
when I used the "go to slot" option on the button it created the function inside main.qml so I just went with that
main.qml
import QtQuick 2.5 import QtQuick.Window 2.2 import QtPositioning 5.5 import QtLocation 5.6 import QtPositioning 5.2 Item { Window { visible: true MainForm { toolButton1.onClicked: { obj.update(icoordlat, icoordlon); } anchors.fill: parent } } }
and obj is set inside main.cpp
int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); MyClass myClass; QQmlApplicationEngine engine; QQmlEngine engine2; QQmlComponent component(&engine2,QUrl(QStringLiteral("qrc:/MainForm.ui.qml"))); QObject *object = component.create(); engine.rootContext()->setContextProperty(QStringLiteral("obj"), &myClass); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); }
-
for anybody with the same questions I managed to get it working by adding a connection in the QML
Connections { id: conn2 target: obj ignoreUnknownSignals: true onPostCodeChanged: { t2.text = postCode } }
Thanks SGaist !
-
AFAIK, you shouldn't need that:
Text { id: t2 text: obj.postCode }
should do what you want automatically through the binding.