I have written code for Firebase Authentication for a desktop application. The authentication is working fine, I am having issues using StackView to push the next page based on successful authentication. Here's my code:
h file:
#ifndef AUTHHANDLER_H
#define AUTHHANDLER_H
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QtQml/qqml.h>
class AuthHandler : public QObject
{
Q_OBJECT
public:
explicit AuthHandler(QObject *parent = nullptr);
~AuthHandler();
void setAPIKey(const QString & apiKey);
//void signUserUp(const QString & emailAddress, const QString & password);
//void signUserIn(const QString & emailAddress, const QString & password);
public slots:
void networkReplyReadyRead();
void signUserIn(const QString & emailAddress, const QString & password);
void signUserUp(const QString & emailAddress, const QString & password);
void performAuthenticatedDatabaseCall();
// New slot function
void onUserSignedIn()
{
qDebug() << "onUserSignedIn called";
// Get a reference to the StackView object
QObject* stackView = this->parent()->findChild<QObject*>("stackView"); //problem here most likely
if (stackView)
{
qDebug() << "Calling push() on StackView";
//Push the main root window onto the StackView
QMetaObject::invokeMethod(stackView, "push", Q_ARG(QVariant, "qrc:/qml/MainRootWindow.qml"));
}
}
signals:
void userSignedIn(); //void
private:
void performPOST( const QString & url, const QJsonDocument & payload);
void parseResponse( const QByteArray & response);
QString m_apiKey;
QNetworkAccessManager * m_networkAccessManager;
QNetworkReply * m_networkReply;
QString m_idToken;
};
#endif // AUTHHANDLER_H
cpp file:
#include "authhandler.h"
#include <QDebug>
#include <QVariantMap>
#include <QNetworkRequest>
#include <QJsonObject>
#include <QQmlApplicationEngine>
#include <QQmlContext>
AuthHandler::AuthHandler(QObject *parent)
: QObject{parent}
, m_apiKey( QString() )
{
m_networkAccessManager = new QNetworkAccessManager( this );
connect( this, &AuthHandler::userSignedIn, this, &AuthHandler::performAuthenticatedDatabaseCall );
connect(this, &AuthHandler::userSignedIn, this, &AuthHandler::onUserSignedIn);
}
AuthHandler::~AuthHandler()
{
m_networkAccessManager->deleteLater();
}
void AuthHandler::setAPIKey(const QString &apiKey)
{
m_apiKey = apiKey;
}
void AuthHandler::signUserUp(const QString &emailAddress, const QString &password)
{
QString signUpEndpoint = "https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=" + m_apiKey;
QVariantMap variantPayload;
variantPayload["email"] = emailAddress;
variantPayload["password"] = password;
variantPayload["returnSecureToken"] = true;
QJsonDocument jsonPayload = QJsonDocument::fromVariant( variantPayload );
performPOST( signUpEndpoint, jsonPayload);
}
void AuthHandler::signUserIn(const QString &emailAddress, const QString &password)
{
QString signInEndpoint = "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=" + m_apiKey;
QVariantMap variantPayload;
variantPayload["email"] = emailAddress;
variantPayload["password"] = password;
variantPayload["returnSecureToken"] = true;
QJsonDocument jsonPayload = QJsonDocument::fromVariant( variantPayload );
performPOST( signInEndpoint, jsonPayload);
}
void AuthHandler::networkReplyReadyRead()
{
QByteArray response = m_networkReply->readAll();
qDebug() << response;
m_networkReply->deleteLater();
parseResponse( response);
}
void AuthHandler::performAuthenticatedDatabaseCall()
{
QString endPoint = "https://karmandrones-7da76-default-rtdb.asia-southeast1.firebasedatabase.app/Pets.json?auth=" +m_idToken;
m_networkReply = m_networkAccessManager->get( QNetworkRequest(QUrl(endPoint)));
connect( m_networkReply, &QNetworkReply::readyRead, this, &AuthHandler::networkReplyReadyRead );
}
void AuthHandler::performPOST(const QString &url, const QJsonDocument &payload)
{
QNetworkRequest newRequest( (QUrl(url)));
newRequest.setHeader( QNetworkRequest::ContentTypeHeader, QString( "application/json"));
m_networkReply = m_networkAccessManager->post( newRequest, payload.toJson());
connect( m_networkReply, &QNetworkReply::readyRead, this, &AuthHandler::networkReplyReadyRead );
}
void AuthHandler::parseResponse(const QByteArray &response)
{
QJsonDocument jsonDocument = QJsonDocument::fromJson(( response ));
if ( jsonDocument.object().contains("error"))
{
qDebug() << "Error occured!" << response;
}
else if (jsonDocument.object().contains("kind"))
{
QString idToken = jsonDocument.object().value("idToken").toString();
qDebug() << "Obtained user ID Token: " << idToken;
qDebug() << "User signed in successfully!";
m_idToken = idToken;
emit userSignedIn();
}
else
qDebug() << "The response was: " << response;
}
qml file:
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.1
import QtQuick.LocalStorage 2.0
//import "AccountToolbar/Backend.js" as Backend
import "AuthBackend.js" as Backend
//////////
import QtQuick.Dialogs 1.3
import QtQuick.Window 2.11
import QGroundControl 1.0
import QGroundControl.Palette 1.0
import QGroundControl.Controls 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.FlightDisplay 1.0
import QGroundControl.FlightMap 1.0
import QGroundControl.MultiVehicleManager 1.0
import QGroundControl.Controllers 1.0
import QtQuick.Controls.Styles 1.4
ApplicationWindow {
id: authWindow
visible: true
width: Screen.width
height: Screen.height
minimumWidth: ScreenTools.isMobile ? Screen.width : Math.min(ScreenTools.defaultFontPixelWidth * 100, Screen.width)
minimumHeight: ScreenTools.isMobile ? Screen.height : Math.min(ScreenTools.defaultFontPixelWidth * 50, Screen.height)
title: qsTr("KarmanGCS")
property color backGroundColor : "white" // "#434343" //"#394454" // navy
property color mainAppColor: "#434343"// "#EBEBEB" //"#6fda9c" // green
property color mainTextCOlor: "grey" // gray
property color popupBackGroundColor: "#b44"
property color popupTextCOlor: "#ffffff"
property var dataBase
FontLoader {
id: fontAwesome
name: "fontawesome"
source: "qrc:/qml/fontawesome-webfont.ttf"
}
// Main stackview
StackView{
id: stackView
focus: true
anchors.fill: parent
}
// After loading show initial Login Page
Component.onCompleted: {
stackView.push("qrc:/qml/LoginPage.qml") //initial page
dataBase = userDataBase()
console.log(dataBase.version)
}
As you can see the AuthWindow.qml loads the login page qml correctly.
The userSigneIn() signal is also emitted correctly on successful login. This can be checked by the qdebug statement that I have put in the cpp file. I tried making a new slot function, which would push the MainRootWindow.qml if the userSignedIn() signal is emitted. The connection is in the cpp file. It is being called as the first qdebug statement works, but the push is not happening. Can someone help me debug or suggest an alterantive approach to push a new page to stackview?
Related
I'm trying to create a simple login application which authenticates via firebase. My current issue is how to display a signal emitted in the .cpp file in a .qml file.
.h file:
#ifndef AUTHHANDLER_H
#define AUTHHANDLER_H
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QtQml/qqml.h>
class AuthHandler : public QObject
{
Q_OBJECT
public:
explicit AuthHandler(QObject *parent = nullptr);
~AuthHandler();
void setAPIKey(const QString & apiKey);
//void signUserUp(const QString & emailAddress, const QString & password);
//void signUserIn(const QString & emailAddress, const QString & password);
public slots:
void networkReplyReadyRead();
void signUserIn(const QString & emailAddress, const QString & password);
void signUserUp(const QString & emailAddress, const QString & password);
void performAuthenticatedDatabaseCall();
signals:
void userSignedIn(); //void
private:
void performPOST( const QString & url, const QJsonDocument & payload);
void parseResponse( const QByteArray & response);
QString m_apiKey;
QNetworkAccessManager * m_networkAccessManager;
QNetworkReply * m_networkReply;
QString m_idToken;
};
#endif // AUTHHANDLER_H
.cpp file: This is one of the functions in my .cpp file. As you can see on running the code it displays the "User signed in successfully" in the application output window. This code works as intended.
void AuthHandler::parseResponse(const QByteArray &response)
{
QJsonDocument jsonDocument = QJsonDocument::fromJson(( response ));
if ( jsonDocument.object().contains("error"))
{
qDebug() << "Error occured!" << response;
}
else if (jsonDocument.object().contains("kind"))
{
QString idToken = jsonDocument.object().value("idToken").toString();
qDebug() << "Obtained user ID Token: " << idToken;
qDebug() << "User signed in successfully!";
m_idToken = idToken;
emit userSignedIn();
}
else
qDebug() << "The response was: " << response;
}
.qml file code:
Button {
id: loginbutton
//anchors.bottom: parent.bottom
property color colorNormal: "white"
property color colorHovered: "grey"
property color colorClicked: "black"
property color hoverColor: loginbutton.down ? colorClicked : (loginbutton.hovered ? colorHovered : colorNormal)
anchors.left: parent.left
anchors.leftMargin: 5
anchors.top: password.bottom
anchors.topMargin: 10
text: qsTr("Login");
font.family: "/fonts/Montserrat-Italic"
font.pointSize: ScreenTools.mediumFontPointSize * 1.3
background: Rectangle {
implicitWidth: mainWindow.width*0.05
implicitHeight: mainWindow.height*0.05
opacity: enabled ? 1 : 0.3
//border.color: controlBt1.down ? "#A9A9A9" : "#373737"
//border.width: 1
radius: 10
gradient: Gradient {
GradientStop { position: 0; color: "#ffffff" }
GradientStop { position: 1; color: "#ADD8E6" }
}
}
onClicked: {
console.log("login: " + username.text + " password: " + password.text);
AuthHandler.signUserIn(username.text, password.text);
This is part of my qml file. On clicking the login button, the signUserIn function executes correctly. My question is, how would I display a succesful login message via my qml if there is a succesful login attempt.
You can use Connection QML type.
Should look something like this:
Connection{
target: AuthHandler
function onUserSignedIn(){console.log("QML: User signed in!")}
}
The pattern is function on<signal name capitalized>(){...}
This seemed like a simple task at first, but honestly I am struggling. I have a Map of profiles I load at startup, and profiles can be added and deleted at runtime. Those profiles need to be listed as RadioButtons or some other element for selection/editing in my UI.
I've tried using createQMLObject() while looping trough a QVariantMap getter:
profile.h
#ifndef PROFILE_H
#define PROFILE_H
#include <QObject>
class Profile final : public QObject
{
Q_OBJECT
public:
Profile(QObject *parent = nullptr);
~Profile(){}
Profile(QString profileName = "default", QObject *parent = nullptr);
QString getProfileName() const;
void setProfileName(const QString &value);
private:
QString profileName;
};
#endif // PROFILE_H
profile.cpp
#include "profile.h"
Profile::Profile(QString profileName, QObject *parent)
: QObject(parent), profileName{ profileName }{}
QString Profile::getProfileName() const
{
return profileName;
}
void Profile::setProfileName(const QString &value)
{
profileName = value;
}
controller.h
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include "profile.h"
#include <QObject>
#include <QQmlEngine>
class Controller : public QObject
{
Q_OBJECT
public:
Controller(QObject *parent = nullptr);
~Controller(){}
static QObject* controllerSingletonProvider(QQmlEngine *engine, QJSEngine *scriptEngine);
static Controller* instance();
Q_INVOKABLE void addProfile(QString profileName);
Q_INVOKABLE QVariantList getLoadedProfiles(){
QVariantList rval;
foreach (QString key, loadedProfiles.keys()) {
rval.append(QVariant::fromValue<Profile*>(loadedProfiles[key].get()));
}
return rval;
};
private:
QMap<QString, std::shared_ptr<Profile>> loadedProfiles;
signals:
};
#endif // CONTROLLER_H
controller.cpp
#include "controller.h"
Controller::Controller(QObject *parent) : QObject(parent){}
void Controller::addProfile(QString profileName)
{
std::shared_ptr<Profile> profile{std::make_shared<Profile>(profileName)};
loadedProfiles[profile->getProfileName()] = profile;
}
QObject* Controller::controllerSingletonProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
return Controller::instance();
}
Controller* Controller::instance(){
static Controller* controller = new Controller();
return controller;
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <controller.h>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterSingletonType<Controller>("com.controller", 1, 0, "Controller", Controller::controllerSingletonProvider);
Controller::instance()->addProfile("Profile1");
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import com.controller 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Column {
id: radioButtonColumn
Component.onCompleted: {
for(var profile in Controller.getLoadedProfiles()){
var obj = Qt.createQmlObject('import QtQuick 2.12; RadioButton { id: '+ profile.getProfileName() +';
text: '+ profile.getProfileName() +'; }', radioButtonColumn, "dynamicSnippet1");
}
}
}
}
I've tried using a signal sent from my Controller Singleton using Connections, but the parameter of the signal does not arrive. I can however call my Controllers c++ functions from qml. I'll only post the changes to the previous code sample:
main.qml changes:
Connections {
target: Controller
onProfileAdded: {
var obj = Qt.createQmlObject('import QtQuick 2.12; RadioButton { id: '+ profileName +';
text: '+ profileName +'; }', radioButtonColumn, "dynamicSnippet1");
console.log("Value changed")
}
}
controller.h changes:
signals:
void profileAdded(QString &profileName);
};
controller.cpp changes:
void Controller::addProfile(QString profileName)
{
std::shared_ptr<Profile> profile{std::make_shared<Profile>(profileName)};
loadedProfiles[profile->getProfileName()] = profile;
emit profileAdded(profileName);
}
main.cpp changes:
engine.load(url);
Controller::instance()->addProfile("Profile2");
return app.exec();
Edit: Curious, that I had to change my onProfileAdded in controller.h to profileAdded for the signal to work. However, I still do not receive the parameter profileName in my qml.
I would like to ask what is the best way to handle a requirement like this. Maybe there is another, simpler way?
Im new to both c++ and QT, so please bear with my perhaps trivial question, but my research didnt yield me anything, that I could understand at least.
I have problem when display value of 2D array QString on QML from C++
I have data and a qmlRegisterType to use on QML.
This is QML file:
import QtQuick 2.12
import QtQuick.Window 2.2
import io.myserial 1.0
import QtQuick.Controls 2.3
Window {
visible: true
width: 8*square_size
height: 8*square_size
title: qsTr("Matrix LDR Value")
property int square_size: 50
Grid {
id: grid
columns: 8
Repeater{
id: rpt
model: 8*8
Rectangle{
width: square_size
height: square_size
border.color: "black"
border.width: 1
Text {
id: txt
anchors.centerIn: parent
}
MSerial{
onModelDataChanged:{
txt.text = Serial.model_data[i]
}
}
}
}
}
}
This is main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "serial.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterType<Serial>("io.myserial", 1, 0, "MSerial");
QQmlApplicationEngine engine;
Serial mySerial(&engine);
engine.rootContext()->setContextProperty("Serial", &mySerial);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
serial.h
#ifndef SERIAL_H
#define SERIAL_H
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QQmlApplicationEngine>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QtDebug>
#include <QObject>
#include <QString>
#include <QString>
#include "toInt16.h"
class Serial: public QObject
{
Q_OBJECT
Q_PROPERTY(QList<QString> modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
public:
explicit Serial(QObject *parent = nullptr);
QList<QString> modelData();
Q_INVOKABLE void setModelData(const QList<QString> &modelData);
void ReadSerial();
void setUpSerial();
QByteArray serialData;
QSerialPort stm32;
QString portName;
QString buffer;
bool isStm32Avaiable;
private slots:
void readSerial();
signals:
void modelDataChanged();
private:
QList<QString> myModelData;
public slots:
};
#endif // SERIAL_H
serial.cpp
#include "serial.h"
#include "toInt16.h"
Serial::Serial(QObject *parent) : QObject(parent)
{
setUpSerial();
}
QList<QString> Serial::modelData()
{
return myModelData;
}
void Serial::setUpSerial(){
buffer = "";
isStm32Avaiable = false;
portName = "";
qDebug()<<"Num port: "<<QSerialPortInfo::availablePorts().length();
foreach (const QSerialPortInfo &serialPortInfor,QSerialPortInfo::availablePorts()) {
qDebug()<<"Has Vendo ID: "<<serialPortInfor.hasVendorIdentifier();
if(serialPortInfor.hasVendorIdentifier()){
qDebug()<<"Vendo ID: "<<serialPortInfor.vendorIdentifier();
}
qDebug()<<"Has Product ID: "<<serialPortInfor.hasProductIdentifier();
if(serialPortInfor.hasProductIdentifier()){
qDebug()<<"Product ID: "<<serialPortInfor.productIdentifier();
portName = serialPortInfor.portName();
}
if(QSerialPortInfo::availablePorts().length() >0){
isStm32Avaiable = true;
}
}
if(isStm32Avaiable){
stm32.setPortName(portName);
stm32.open(QSerialPort::ReadWrite);
stm32.setBaudRate(QSerialPort::Baud115200);
stm32.setDataBits(QSerialPort::Data8);
stm32.setParity(QSerialPort::NoParity);
stm32.setStopBits(QSerialPort::OneStop);
stm32.setFlowControl(QSerialPort::NoFlowControl);
QObject::connect(&stm32, SIGNAL(readyRead()),this,SLOT(readSerial()));
}else{
qDebug()<<"warning: Port Error . Couldn't find kit";
}
}
void Serial::setModelData(const QList<QString> &modelData)
{
if (modelData == myModelData)
return;
myModelData = modelData;
emit modelDataChanged();
}
void Serial::ReadSerial()
{
serialData.clear();
serialData = stm32.readAll();
QByteArray buffer;
qDebug()<< "Serial Data: "<<serialData;
buffer = serialData;//.split(',');
// QStringList buffer2;
//uint16_t data[64];
union INT_TO_BYTE receivedData[64];
for(int i=0; i<64; i++){
receivedData[i].b[0] = buffer.data()[i*2];
receivedData[i].b[1] = buffer.data()[i*2+1];
receivedData[i].i = (uint16_t)receivedData[i].b[0]|(uint16_t)receivedData[i].b[1]<<8;
qDebug() << "This is data "<<i<<" "<<receivedData[i].i;
myModelData.append(QString::number(receivedData[i].i));
}
//setModelData(myModelData);
emit modelDataChanged();
}
toInt16.h
#ifndef TOINT16_H
#define TOINT16_H
#include <QTextCodec>
union INT_TO_BYTE
{
uint16_t i;
char b[2];
};
//union INT_TO_BYTE receivedData[128];
#endif // TOINT16_H
I received data but I cannot display it on QML whenever data is changed. I set it change 1view/sec.
My logic is: Whenever data change, QML display new data
In your code I see the following errors:
Or you register a type with qmlRegisterType or set it as contextProperty, not both. I recommend you check Choosing the Correct Integration Method Between C++ and QML.
In your repeater you are creating N MySerial but you can only have 1, the idea is that you create a MySerial and the information is passed to the model.
Do not use QList<QString> but QStringList.
Your data should not be written from QML so it is not necessary for the modelData property to be Writeble.
Considering the above and avoiding the part of the decoding of the data since I do not know the format that will replace it creating random data the solution is the following:
serial.cpp
#ifndef SERIAL_H
#define SERIAL_H
#include <QObject>
#include <QSerialPort>
class Serial : public QObject
{
Q_OBJECT
Q_PROPERTY(QStringList modelData READ modelData NOTIFY modelDataChanged)
public:
explicit Serial(QObject *parent = nullptr);
QStringList modelData() const;
Q_SIGNALS:
void modelDataChanged();
private Q_SLOTS:
void readSerial();
private:
void setUpSerial();
QStringList m_modelData;
bool isStm32Avaiable;
QString portName;
QSerialPort stm32;
};
#endif // SERIAL_H
serial.cpp
#include "serial.h"
#include "toInt16.h"
#include <QSerialPortInfo>
#include <QDebug>
Serial::Serial(QObject *parent) : QObject(parent){
m_modelData.reserve(64);
setUpSerial();
}
QStringList Serial::modelData() const{
return m_modelData;
}
void Serial::setUpSerial(){
isStm32Avaiable = false;
portName = "";
qDebug()<<"Num port: "<<QSerialPortInfo::availablePorts().length();
for (const QSerialPortInfo &serialPortInfor : QSerialPortInfo::availablePorts()) {
qDebug()<<"Has Vendo ID: "<<serialPortInfor.hasVendorIdentifier();
if(serialPortInfor.hasVendorIdentifier()){
qDebug()<<"Vendo ID: "<<serialPortInfor.vendorIdentifier();
}
if(serialPortInfor.hasProductIdentifier()){
qDebug()<<"Product ID: "<<serialPortInfor.productIdentifier();
portName = serialPortInfor.portName();
}
if(!QSerialPortInfo::availablePorts().isEmpty()){
isStm32Avaiable = true;
break;
}
}
if(isStm32Avaiable){
stm32.setPortName(portName);
stm32.open(QSerialPort::ReadWrite);
stm32.setBaudRate(QSerialPort::Baud115200);
stm32.setDataBits(QSerialPort::Data8);
stm32.setParity(QSerialPort::NoParity);
stm32.setStopBits(QSerialPort::OneStop);
stm32.setFlowControl(QSerialPort::NoFlowControl);
connect(&stm32, &QIODevice::readyRead, this, &Serial::readSerial);
}else{
qDebug()<<"warning: Port Error . Couldn't find kit";
}
}
void Serial::readSerial(){
m_modelData.clear();
// FIXME
static int counter;
for(int i=0; i<64; i++){
m_modelData.append(QString::number(counter));
counter++;
}
//
emit modelDataChanged();
}
main.cpp
#include "serial.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
qmlRegisterType<Serial>("io.myserial", 1, 0, "MSerial");
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import io.myserial 1.0
Window {
visible: true
width: 8*square_size
height: 8*square_size
title: qsTr("Matrix LDR Value")
property int square_size: 50
MSerial{
id: serial
}
Grid {
columns: 8
Repeater{
model: serial.modelData
Rectangle{
width: square_size
height: square_size
border.color: "black"
border.width: 1
Text {
id: txt
anchors.centerIn: parent
text: model.modelData
}
}
}
}
}
I want to display a huge number of points on a map with Qt/QML. These points are detailled in a .txt file.
I'm looking for the process to use but, unfortunately, I only find a query method via a plugin to display places from a server (osm, google...). I can't use it, these points being very specific.
What is the best way to achieve this task ?
Also, is it necessary to use "plugin itemsoverlay" in addition to the "osm plugin" to show these points on a specific layer ?
Thanks for help.
Ok, back to fight with code now.
Here is the main .cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QObject>
#include <QGeoCoordinate>
#include <QDebug>
class NavaidsPoint: public QObject
{
Q_OBJECT
Q_PROPERTY(QGeoCoordinate position READ position WRITE setPosition NOTIFY positionChanged)
public:
NavaidsPoint(QString code, double latitude, double longitude, QString country = "")
{
m_code = code;
m_latitude = latitude;
m_longitude = longitude;
m_country = country;
m_position.setLatitude(latitude);
m_position.setLongitude(longitude);
}
void setPosition(const QGeoCoordinate &c) { //Affectation des nouvelles coordonnées de position
if (m_position == c)
return;
m_position = c;
emit positionChanged(); //Emission du signal de changement de position
}
QGeoCoordinate position() const
{
return m_position; //Lecture des coordonnées de position
}
Q_INVOKABLE QString oaciCode() const {
return m_code;
}
Q_INVOKABLE QString countryCode() const {
return m_country;
}
signals:
void positionChanged();
private:
QGeoCoordinate m_position;
double m_latitude;
double m_longitude;
double m_altitude;
QString m_code;
QString m_country;
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
NavaidsPoint oslo("Oslo", 59.9154, 10.7425, "NG");
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("oslo", &oslo);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
#include "main.moc"
And the main.qml
import QtQuick 2.6
import QtQuick.Window 2.2
import QtPositioning 5.5
import QtLocation 5.6
Window {
width: 700
height: 500
visible: true
title: qsTr("Test implantation coordonnées")
property variant topLeftEurope: QtPositioning.coordinate(60.5, 0.0)
property variant bottomRightEurope: QtPositioning.coordinate(51.0, 14.0)
property variant viewOfEurope:
QtPositioning.rectangle(topLeftEurope, bottomRightEurope)
Map {
id: mapOfEurope
anchors.centerIn: parent;
anchors.fill: parent
plugin: Plugin {
name: "osm"
}
MapCircle {
center: oslo.position
radius: 5000.0
color: 'green'
border.width: 3
MouseArea {
anchors.fill: parent
onDoubleClicked: {
console.log("Doubleclick on " + oslo.oaciCode())
}
onClicked: {
console.log("Point : " + oslo.oaciCode() + " " + oslo.position + " " + oslo.countryCode())
}
}
}
visibleRegion: viewOfEurope
}
}
everything works fine with this unique point Oslo. Now I need to place thousands of points. This structure cannot work like that, because I need to implement one NavaidsPoint for each of them and setContextProperty for each of them again. Likewise, in the main.QML the circle is tied with the object :
center : oslo.position
and in addition, I nead the oaciCode and the countryCode. So this part of the code must be generic, not specific to a single object.
So, what could be the best way to solve this problem ?
I hope to be in the SO scope with these precisions.
Thanks again for help.
If you want to load many elements a good option is to use MapItemView, this requires a model that provides the data, and indicate the appropriate delegate.
The model is created based on a QAbstractListModel where we will use the roles to expose the properties to QML, for it creates a class that does not necessarily have to inherit from QObject, in this case it will be the following.
#ifndef NAVAIDSPOINT_H
#define NAVAIDSPOINT_H
#include <QGeoCoordinate>
#include <QString>
class NavaidsPoint
{
public:
NavaidsPoint(QString code, double latitude, double longitude, QString country = ""){
m_code = code;
m_country = country;
m_position.setLatitude(latitude);
m_position.setLongitude(longitude);
m_position.setAltitude(0.0);
}
void setPosition(const QGeoCoordinate &c) { //Affectation des nouvelles coordonnées de position
m_position = c;
}
QGeoCoordinate position() const{
return m_position; //Lecture des coordonnées de position
}
QString oaciCode() const {
return m_code;
}
QString countryCode() const {
return m_country;
}
private:
QGeoCoordinate m_position;
QString m_code;
QString m_country;
};
#endif // NAVAIDSPOINT_H
QAbstractListModel requires that you implement the methods rowCount, data and roleNames, also I have implemented the add method where you must notify the model of the changes, an example is as follow
#ifndef NAVAIDSMODEL_H
#define NAVAIDSMODEL_H
#include "navaidspoint.h"
#include <QAbstractListModel>
#include <QFile>
#include <QTextStream>
#include <QDebug>
class NavaidsModel : public QAbstractListModel
{
Q_OBJECT
public:
NavaidsModel(QObject *parent = Q_NULLPTR):QAbstractListModel(parent){
}
enum NavaidsRoles {
PositionRole = Qt::UserRole + 1,
OACICodeRole,
CountryCodeRole
};
void readFromCSV(const QString &filename){
QFile file(filename);
if(!file.open(QFile::ReadOnly | QFile::Text))
return;
QTextStream in(&file);
while (!in.atEnd()) {
QString line = in.readLine();
QStringList elements = line.split(",");
if(elements.count()==4){
QString code = elements[0];
double latitude = elements[1].toDouble();
double longitude = elements[2].toDouble();
QString country = elements[3];
NavaidsPoint p(code, latitude, longitude, country);
addNavaidsPoint(p);
}
}
}
void addNavaidsPoint(const NavaidsPoint &point){
beginInsertRows(QModelIndex(), rowCount(), rowCount());
mPoints << point;
endInsertRows();
}
int rowCount(const QModelIndex & parent = QModelIndex()) const{
Q_UNUSED(parent)
return mPoints.count();
}
QVariant data(const QModelIndex & index, int role=Qt::DisplayRole) const {
if (index.row() < 0 || index.row() >= mPoints.count())
return QVariant();
const NavaidsPoint &point = mPoints[index.row()];
if (role == PositionRole)
return QVariant::fromValue(point.position());
else if (role == OACICodeRole)
return point.oaciCode();
else if (role == CountryCodeRole)
return point.countryCode();
return QVariant();
}
protected:
QHash<int, QByteArray> roleNames() const {
QHash<int, QByteArray> roles;
roles[PositionRole] = "position";
roles[OACICodeRole] = "oaci";
roles[CountryCodeRole] = "country";
return roles;
}
private:
QList<NavaidsPoint> mPoints;
};
#endif // NAVAIDSMODEL_H
After the model is created, the models are added and we expose it to QML:
#include "navaidsmodel.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
NavaidsModel model;
model.readFromCSV("/home/eyllanesc/data.csv"); //from file
model.addNavaidsPoint(NavaidsPoint("Oslo", 59.9154, 10.7425, "NG")); // add new point
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("navaidsModel", &model);
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
and at the end, MapItemView is used with the appropriate model and delegate:
import QtQuick 2.6
import QtQuick.Window 2.2
import QtPositioning 5.5
import QtLocation 5.6
Window {
width: 700
height: 500
visible: true
title: qsTr("Test implantation coordonnées")
property variant topLeftEurope: QtPositioning.coordinate(60.5, 0.0)
property variant bottomRightEurope: QtPositioning.coordinate(51.0, 14.0)
property variant viewOfEurope:
QtPositioning.rectangle(topLeftEurope, bottomRightEurope)
Map {
id: mapOfEurope
anchors.centerIn: parent;
anchors.fill: parent
plugin: Plugin {
name: "osm"
}
MapItemView {
model: navaidsModel
delegate: MapCircle{
center: position
radius: 10000
color: 'green'
border.width: 3
MouseArea {
anchors.fill: parent
onDoubleClicked: {
console.log("Doubleclick on " + oaci)
}
onClicked: {
console.log("Point : " + oaci + " " + position + " " + country)
}
}
}
}
visibleRegion: viewOfEurope
}
}
In the following link is the complete example.
Please help me.,i m stuck with this for more than a week.I am emitting a signal with image from my cpp file.I need to replace the default image that i placed in the imageView at QMl using this emitted image.
Here is my full code.
PostHttp.hpp
/* Copyright (c) 2012 Research In Motion Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef POSTHTTP_HPP
#define POSTHTTP_HPP
#include "qvariant.h"
#include <bb/ImageData>
#include <bb/cascades/GroupDataModel>
#include <QtCore/QObject>
#include <bb/data/JsonDataAccess>
#include <bb/cascades/QListDataModel>
#include <bb/cascades/Image>
#include <bb/cascades/ImageView>
#include <bb/cascades/CustomControl>
namespace bb {
namespace cascades {
class Container;
}
}
using namespace bb::cascades;
class QNetworkAccessManager;
class PostHttp: public QObject {
Q_OBJECT
public:
PostHttp(QObject* parent = 0);
bb::cascades::Image m_image;
ImageView* imageView;
Container* mRootContainer;
bool createFolder(QString path);
bool openAndSaveFile(QString filePathWithName, QNetworkReply* reply);
public Q_SLOTS:
void loginWebService(const QString &body, const QString &pass,
bool istoken);
void newsFeedWebService(const qint16 num);
void logoutWebService();
void imageFetcher();
void get(const QUrl &url);
void post(const QVariantMap &body, const QUrl &url);
Q_SIGNALS:
void complete(const QVariantList &info);
void newsfeedComplete(const QVariantList &info);
void imageLoaded(const QVariant &image);
private Q_SLOTS:
void onGetReply();
void onNewsFeedReply();
void onImageReply();
Q_INVOKABLE void generatePage();
Q_INVOKABLE void loadImages();
private:
bb::cascades::QListDataModel<QObject*>* m_model;
QImage setImage(const QImage &image);
bb::cascades::DataModel* model() const;
QNetworkAccessManager* m_networkAccessManager;
bb::data::JsonDataAccess* dataAccess;
public:
QString token;
};
#endif
PostHttp.cpp
#include "PostHttp.hpp"
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QSslConfiguration>
#include <QUrl>
#include <bb/data/JsonDataAccess>
#include <QDateTime>
#include <bb/cascades/AbstractPane>
#include <bb/cascades/Application>
#include <bb/cascades/QmlDocument>
#include <bb/cascades/Page>
#include <bb/cascades/StandardListItem>
#include <QFile>
#include <bb/ImageData>
#include <QNetworkReply>
#include <QNetworkDiskCache>
#include <QDesktopServices>
#include <bb/cascades/Image>
#include <bb/cascades/Container>
#include <bb/cascades/ImageView>
#include <bb/cascades/ScalingMethod>
#include <bb/cascades/DockLayout>
#include <bb/cascades/controls/activityindicator.h>
#include <bb/cascades/controls/scrollview.h>
#include <bb/cascades/controls/page.h>
#include <bb/cascades/NavigationPaneProperties>
#include <bb/cascades/Color>
using namespace bb::data;
using namespace bb::cascades;
using namespace bb::utility;
QString globalTokenValue;
int globalUserId;
bool flag = true;
bool flag1 = true;
QVariantList data;
PostHttp::PostHttp(QObject* parent) :
QObject(parent), m_networkAccessManager(
new QNetworkAccessManager(this)), m_model(
new QListDataModel<QObject*>()) {
}
//! [0]
/**
* PostHttp::post
*
* Make a network request to httpbin.org/post with POST data and get
* the response
*/
//! [1]
void PostHttp::post(const QVariantMap &body, const QUrl &url) {
JsonDataAccess jda;
QByteArray jsonData;
jda.saveToBuffer(*(&body), &jsonData);
QByteArray postDataSize = QByteArray::number(jsonData.size());
QNetworkRequest request(*(&url));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setHeader(QNetworkRequest::ContentLengthHeader,
QString(postDataSize).toUtf8());
//QNetworkReply* reply = m_networkAccessManager->post(request, body.toAscii());
qDebug() << "json" << jsonData;
QNetworkReply* reply = m_networkAccessManager->post(request, jsonData);
qDebug() << "strdgfyusujnm kjh " << (&url)->toString();
if ((&url)->toString()
== "http:///GetNewsFeed") {
connect(reply, SIGNAL(finished()), this, SLOT(onNewsFeedReply()));
} else {
connect(reply, SIGNAL(finished()), this, SLOT(onGetReply()));
}
}
void PostHttp::loginWebService(const QString &body, const QString &pass,
bool istoken) {
qint64 date = QDateTime::currentMSecsSinceEpoch();
QString time = QString::number(date);
QVariantMap data;
QVariantMap loginData;
QVariantMap devicedata;
devicedata.insert("OS", "BlackBerry OS 6.0.0.706");
devicedata.insert("deviceId", "232BC441");
devicedata.insert("deviceModel", "9800");
devicedata.insert("screenSize", "480x360");
loginData.insert("device", devicedata);
loginData.insert("email", *(&body));
loginData.insert("password", *(&pass));
loginData.insert("requestDate", "/Date(" + time + "+200)/");
data.insert("apiKey", "4f74721be9b51f24f065b044");
data.insert("data", loginData);
data.insert("requestDate", "/Date(" + time + "+200)/");
if (istoken) {
} else {
data.insert("token", "");
}
const QUrl url(
"http:///LoginRequest");
post(data, url);
}
void PostHttp::newsFeedWebService(const qint16 num) {
qint64 date = QDateTime::currentMSecsSinceEpoch();
QString time = QString::number(date);
QVariantMap data;
QVariantMap newsfeedData;
newsfeedData.insert("postId", 0);
newsfeedData.insert("requestType", 2);
newsfeedData.insert("requestedCount", num);
newsfeedData.insert("userId", globalUserId);
data.insert("apiKey", "4f74721be9b51f24f065b044");
data.insert("data", newsfeedData);
data.insert("requestDate", "/Date(" + time + "+200)/");
data.insert("token", globalTokenValue);
const QUrl url(
"http:///GetNewsFeed");
if (flag == true) {
post(data, url);
}
}
void PostHttp::logoutWebService() {
qint64 date = QDateTime::currentMSecsSinceEpoch();
QString time = QString::number(date);
QVariantMap data;
QVariantMap logoutData;
logoutData.insert("logoutRequestType", 1);
logoutData.insert("userId", globalUserId);
data.insert("apiKey", "4f74721be9b51f24f065b044");
data.insert("data", logoutData);
data.insert("requestDate", "/Date(" + time + "+200)/");
data.insert("token", globalTokenValue);
const QUrl url(
"http:///LogoutUser");
post(data, url);
}
void PostHttp::imageFetcher() {
const QUrl url(
"http://upload.wikimedia.org/wikipedia/commons/e/e7/Nuvola_filesystems_services.png");
if (flag1 == true) {
get(url);
}
}
void PostHttp::get(const QUrl &url) {
QNetworkRequest request(*(&url));
QNetworkReply* reply = m_networkAccessManager->get(request);
connect(reply, SIGNAL(finished()), this, SLOT(onImageReply()));
}
/**
* PostHttp::onGetReply()
*
* SLOT
* Read and return the http response from our http post request
*/
void PostHttp::onGetReply() {
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
QString response;
if (reply) {
if (reply->error() == QNetworkReply::NoError) {
const int available = reply->bytesAvailable();
if (available > 0) {
const QByteArray buffer(reply->readAll());
response = QString::fromUtf8(buffer);
JsonDataAccess dataAccess;
QVariantMap results =
dataAccess.loadFromBuffer(response).toMap();
QString token = (results["Token"]).value<QString>();
int userId = (results["userId"]).value<int>();
if (globalTokenValue == "") {
globalTokenValue = token;
globalUserId = userId;
} else
flag = false;
QString success = (results["Success"]).value<QString>();
}
} else {
response =
tr("Error: %1 status: %2").arg(reply- >errorString(),
reply->attribute(
QNetworkRequest::HttpStatusCodeAttribute).toString());
qDebug() << response;
}
reply->deleteLater();
}
if (response.trimmed().isEmpty()) {
response = tr("Unable to retrieve post response");
}
qDebug() << "response" << response;
}
void PostHttp::onNewsFeedReply() {
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
QString response;
if (reply) {
if (reply->error() == QNetworkReply::NoError) {
flag = false;
const int available = reply->bytesAvailable();
if (available > 0) {
const QByteArray buffer(reply->readAll());
response = QString::fromUtf8(buffer);
JsonDataAccess dataAccess;
QVariantMap results =
dataAccess.loadFromBuffer(response).toMap();
data = results.value("Data").toList();
qDebug() << "first element is" << data.first().toString();
emit newsfeedComplete(data);
}
}
}
}
void PostHttp::onImageReply() {
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
QString response;
QImage img;
QString filePathWithName = "data/img/";
QString imageName;
if (reply) {
if (reply->error() == QNetworkReply::NoError) {
flag1 = false;
const int available = reply->bytesAvailable();
if (available > 0) {
const QByteArray buffer(reply->readAll());
response = QString::fromUtf8(buffer);
img.loadFromData(buffer);
img = img.scaled(40, 40, Qt::KeepAspectRatioByExpanding);
const QImage swappedImage = img.rgbSwapped();
const bb::ImageData imageData = bb::ImageData::fromPixels(
swappedImage.bits(), bb::PixelFormat::RGBX,
swappedImage.width(), swappedImage.height(),
swappedImage.bytesPerLine());
QByteArray byteArray = bb::utility::ImageConverter::encode(
"image/png", imageData, 75);
qDebug() << "bytearray is" << byteArray;
// QVariant image(byteArray);
QVariant realImage(byteArray);
qDebug() << "imag of image is" << realImage;
emit imageLoaded(realImage);
}
}
}
}
//! [1]
And finally my QML file is
NewsFeed.qml
import bb.cascades 1.0
import Network.PostHttp 1.0
import bb.cascades 1.0
import "controls"
import my.library 1.0
Page {
actions: [
ActionItem {
title: "Logout"
onTriggered: {
netpost.logoutWebService();
Application.quit();
}
ActionBar.placement: ActionBarPlacement.OnBar
}
]
id:mainpage
onCreationCompleted: {
Qt.mainImageview = imageviewid;
}
Container {
layout: DockLayout {
}
// The background image
ImageView {
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Fill
imageSource: "asset:///images/background.png"
}
//! [0]
Container {
id : innercontainer
ActivityIndicator {
id: progressIndicator
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Fill
onStarted: {
}
onCreationCompleted: {
progressIndicator.running = true;
}
}
ListView {
id: listView
objectName: "listView"
dataModel: ArrayDataModel {
id: myListModel
}
// Override default GroupDataModel::itemType() behaviour, which is to return item type "header"
listItemComponents: ListItemComponent {
id: listcomponent
// StandardListItem is a convivience component for lists with default cascades look and feel
StandardListItem {
title: ListItemData.postText
description: ListItemData.postDate
status: ListItemData.filePath
imageSource: "asset:///images/4.png"
}
}
layoutProperties: StackLayoutProperties {
spaceQuota: 1.0
}
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Fill
}
Container {
id: root
layout: StackLayout {
}
Label {
text: ListItemData.postText
horizontalAlignment: HorizontalAlignment.Left
verticalAlignment: VerticalAlignment.Bottom
}
Label {
text: ListItemData.postDate
// textStyle.fontSize: 5
horizontalAlignment: HorizontalAlignment.Right
verticalAlignment: VerticalAlignment.Bottom
}
attachedObjects: [
QTimer {
id: timer
property int f: 0
interval: 5000
onTimeout: {
progressIndicator.running = false;
netpost.imageFetcher();
netpost.newsFeedWebService("10");
}
},
PostHttp {
id: netpost
onComplete: {
progressIndicator.running = false;
progressIndicator.visible = false;
console.log("dsfdsafs"+netpost.model)
timer.stop();
}
onImageLoaded:{
console.log("value is image from cpp jhgsdh " + image)
imageviewid.setImageSource(image)
}
onNewsfeedComplete: {
console.log("response from newsfeed is "+info)
myListModel.append(info)
}
}
]
}
onCreationCompleted: {
// this slot is called when declarative scene is created
// write post creation initialization here
console.log("Page - onCreationCompleted()")
// enable layout to adapt to the device rotation
// don't forget to enable screen rotation in bar-bescriptor.xml (Application->Orientation->Auto-orient)
OrientationSupport.supportedDisplayOrientation = SupportedDisplayOrientation.All;
// populate list view model with the sample data
timer.start();
// myListModel.load("app/native/assets/mydata.json")
}
ImageView {
id: imageviewid
imageSource: "asset:///images/4.png"
enabled: true
loadEffect: ImageViewLoadEffect.FadeZoom
}
}
}
}
Please help me with this.
So you've defined your slot with QVariant parameter however trying to emit it with QByteArray type which is returned by ImageConverter::encode() call.
Try to change that part of your code to this and give it a go:
QByteArray byteArray = bb::utility::ImageConverter::encode(QUrl(QDir::currentPath() + "/shared/camera/img.png"), imageData, 75);
QVariant image(byteArray);
emit imageLoaded(image);
Also, double check that everywhere where you declare/define this pair of signal/slot you're specified exactly the same parameter notation (ie const QVariant& in case of imageLoaded() signal and so on)
Try this sample code and implement by re-altering in your project
1.QML FILE
import bb.cascades 1.0
Page {
content: Container {
ListView {
dataModel: _app.model
function itemType (data, indexPath) {
return data["type"];
}
listItemComponents: [
]
}
}
}
2.HPP FILE
#ifndef APP_HPP
#define APP_HPP
#include <QtCore/QObject>
#include <QtNetwork/QNetworkAccessManager>
#include <bb/cascades/QListDataModel>
class App: public QObject
{
Q_OBJECT
Q_PROPERTY(bb::cascades::DataModel *model READ model NOTIFY modelChanged)
public:
App();
bb::cascades::DataModel * model() const;
Q_SIGNALS:
void modelChanged();
private Q_SLOTS:
void handleNetworkData(QNetworkReply *reply);
private:
mutable bb::cascades::QMapListDataModel *dataModel;
QNetworkAccessManager networkManager;
};
#endif // APP_HPP
3.CPP FILE
#include <bb/cascades/Page>
#include <bb/cascades/QmlDocument>
#include <bb/data/JsonDataAccess>
#include "App.hpp"
using namespace bb::cascades;
using namespace bb::data;
App::App() :
{
// Load up the QML for our view
QmlDocument *qml = QmlDocument::create("view.qml");
// Provide a reference to this class
qml->setContextProperty("_app", this);
// Create the root node of this view
Page *view = qml->createRootNode<Page>();
dataModel = new QMapListDataModel();
// Hook this signal so we can respond to network replies
connect(&networkManager, SIGNAL(finished(QNetworkReply *)), this,
SLOT(handleNetworkData(QNetworkReply *)));
// Do the request
QUrl url = "http://jsonservice.com/news/headlines/";
networkManager.get(QNetworkRequest(url));
}
DataModel * App::model() const
{
return dataModel;
}
void App::handleNetworkData(QNetworkReply *reply)
{
if (!reply->error()) {
const QByteArray response(reply->readAll());
JsonDataAccess jda;
QVariantMap results = jda.loadFromBuffer(response).toMap();
// Get the relevant parts we want from the JSON
QVariantList posts = results["data"].toMap()["children"].toList();
Q_FOREACH(QVariant post, posts) {
dataModel->append(post);
}
}
// Cleanup
reply->deleteLater();
}