diff --git a/src/publictransport/CMakeLists.txt b/src/publictransport/CMakeLists.txt index 812a207..5c87236 100644 --- a/src/publictransport/CMakeLists.txt +++ b/src/publictransport/CMakeLists.txt @@ -1,65 +1,63 @@ set(kpublictransport_srcs departurereply.cpp departurerequest.cpp journeyreply.cpp journeyrequest.cpp manager.cpp reply.cpp backends/abstractbackend.cpp backends/navitiabackend.cpp - backends/navitiaclient.cpp backends/navitiaparser.cpp datatypes/departure.cpp datatypes/journey.cpp datatypes/line.cpp datatypes/location.cpp networks/networks.qrc ) ecm_qt_declare_logging_category(kpublictransport_srcs HEADER logging.h IDENTIFIER KPublicTransport::Log CATEGORY_NAME org.kde.kpublictransport) add_library(KPublicTransport STATIC ${kpublictransport_srcs}) target_include_directories(KPublicTransport PUBLIC "$") target_link_libraries(KPublicTransport PUBLIC Qt5::Gui PRIVATE Qt5::Network ) ecm_generate_headers(KPublicTransport_FORWARDING_HEADERS HEADER_NAMES DepartureReply DepartureRequest JourneyReply JourneyRequest Manager Reply PREFIX KPublicTransport REQUIRED_HEADERS KPublicTransport_HEADERS ) # # ### for testing only ecm_generate_headers(KPublicTransport_Backends_FORWARDING_HEADERS HEADER_NAMES - NavitiaClient NavitiaParser PREFIX KPublicTransport REQUIRED_HEADERS KPublicTransport_Backends_HEADERS RELATIVE backends ) ecm_generate_headers(KPublicTransport_Datatypes_FORWARDING_HEADERS HEADER_NAMES Datatypes Departure Journey Line Location PREFIX KPublicTransport REQUIRED_HEADERS KPublicTransport_Datatypes_HEADERS RELATIVE datatypes ) install(FILES org_kde_kpublictransport.categories DESTINATION ${KDE_INSTALL_CONFDIR}) diff --git a/src/publictransport/backends/navitiabackend.cpp b/src/publictransport/backends/navitiabackend.cpp index d68992e..8660f81 100644 --- a/src/publictransport/backends/navitiabackend.cpp +++ b/src/publictransport/backends/navitiabackend.cpp @@ -1,83 +1,128 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "navitiabackend.h" #include "logging.h" #include "navitiaparser.h" #include #include #include +#include +#include +#include #include #include #include #include #include #include using namespace KPublicTransport; NavitiaBackend::NavitiaBackend() = default; bool NavitiaBackend::queryJourney(JourneyReply *reply, QNetworkAccessManager *nam) const { + const auto req = reply->request(); + QUrl url; + url.setScheme(QStringLiteral("https")); + url.setHost(m_endpoint); + url.setPath(QStringLiteral("/v1") + + (m_coverage.isEmpty() ? QString() : (QStringLiteral("/coverage/") + m_coverage)) + + QStringLiteral("/journeys")); + + QUrlQuery query; + query.addQueryItem(QStringLiteral("from"), QString::number(req.from().latitude()) + QLatin1Char(';') + QString::number(req.from().longitude())); + query.addQueryItem(QStringLiteral("to"), QString::number(req.to().latitude()) + QLatin1Char(';') + QString::number(req.to().longitude())); + if (req.dateTime().isValid()) { + query.addQueryItem(QStringLiteral("datetime"), req.dateTime().toString(QStringLiteral("yyyyMMddThhmmss"))); + query.addQueryItem(QStringLiteral("datetime_represents"), req.dateTimeMode() == JourneyRequest::Arrival ? QStringLiteral("arrival") : QStringLiteral("departure")); + } + + // TODO: disable reply parts we don't care about + query.addQueryItem(QStringLiteral("disable_geojson"), QStringLiteral("true")); // ### seems to have no effect? + query.addQueryItem(QStringLiteral("depth"), QStringLiteral("0")); // ### also has no effect? + url.setQuery(query); + + QNetworkRequest netReq(url); + netReq.setRawHeader("Authorization", m_auth.toUtf8()); + + qCDebug(Log) << "GET:" << url; + auto netReply = nam->get(netReq); + QObject::connect(netReply, &QNetworkReply::finished, [reply, netReply] { + switch (netReply->error()) { + case QNetworkReply::NoError: + addResult(reply, NavitiaParser::parseJourneys(netReply->readAll())); + break; + case QNetworkReply::ContentNotFoundError: + addError(reply, Reply::NotFoundError, NavitiaParser::parseErrorMessage(netReply->readAll())); + break; + default: + addError(reply, Reply::NetworkError, netReply->errorString()); + qCDebug(Log) << netReply->error() << netReply->errorString(); + break; + } + netReply->deleteLater(); + }); + return true; } bool NavitiaBackend::queryDeparture(DepartureReply *reply, QNetworkAccessManager *nam) const { const auto req = reply->request(); QUrl url; url.setScheme(QStringLiteral("https")); url.setHost(m_endpoint); url.setPath( QStringLiteral("/v1/coverage/") + (m_coverage.isEmpty() ? QString::number(req.stop().latitude()) + QLatin1Char(';') + QString::number(req.stop().longitude()) : m_coverage) + QStringLiteral("/coord/") + QString::number(req.stop().latitude()) + QLatin1Char(';') + QString::number(req.stop().longitude()) + QStringLiteral("/departures") ); QUrlQuery query; query.addQueryItem(QStringLiteral("from_datetime"), req.dateTime().toString(QStringLiteral("yyyyMMddThhmmss"))); query.addQueryItem(QStringLiteral("disable_geojson"), QStringLiteral("true")); query.addQueryItem(QStringLiteral("depth"), QStringLiteral("0")); url.setQuery(query); QNetworkRequest netReq(url); netReq.setRawHeader("Authorization", m_auth.toUtf8()); qCDebug(Log) << "GET:" << url; auto netReply = nam->get(netReq); QObject::connect(netReply, &QNetworkReply::finished, [netReply, reply] { switch (netReply->error()) { case QNetworkReply::NoError: addResult(reply, NavitiaParser::parseDepartures(netReply->readAll())); break; case QNetworkReply::ContentNotFoundError: addError(reply, Reply::NotFoundError, NavitiaParser::parseErrorMessage(netReply->readAll())); break; default: addError(reply, Reply::NetworkError, netReply->errorString()); qCDebug(Log) << netReply->error() << netReply->errorString(); break; } netReply->deleteLater(); }); return true; } diff --git a/src/publictransport/backends/navitiaclient.cpp b/src/publictransport/backends/navitiaclient.cpp deleted file mode 100644 index 093c95c..0000000 --- a/src/publictransport/backends/navitiaclient.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - Copyright (C) 2018 Volker Krause - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include "navitiaclient.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -using namespace KPublicTransport; - -QNetworkReply* NavitiaClient::findJourney(const JourneyRequest &req, QNetworkAccessManager *nam) -{ - QUrl url; - url.setScheme(QStringLiteral("https")); - url.setHost(QStringLiteral("api.navitia.io")); - url.setPath(QStringLiteral("/v1/journeys")); - - QUrlQuery query; - query.addQueryItem(QStringLiteral("from"), QString::number(req.from().latitude()) + QLatin1Char(';') + QString::number(req.from().longitude())); - query.addQueryItem(QStringLiteral("to"), QString::number(req.to().latitude()) + QLatin1Char(';') + QString::number(req.to().longitude())); - if (req.dateTime().isValid()) { - query.addQueryItem(QStringLiteral("datetime"), req.dateTime().toString(QStringLiteral("yyyyMMddThhmmss"))); - query.addQueryItem(QStringLiteral("datetime_represents"), req.dateTimeMode() == JourneyRequest::Arrival ? QStringLiteral("arrival") : QStringLiteral("departure")); - } - - // TODO: disable reply parts we don't care about - query.addQueryItem(QStringLiteral("disable_geojson"), QStringLiteral("true")); // ### seems to have no effect? - query.addQueryItem(QStringLiteral("depth"), QStringLiteral("0")); // ### also has no effect? - url.setQuery(query); - - QNetworkRequest netReq(url); - netReq.setRawHeader("Authorization", "48ed1733-d3f0-445a-9210-9fb36e20a8a3"); // ### this is the test key - - qDebug() << "GET:" << url; - return nam->get(netReq); -} - -QNetworkReply* NavitiaClient::queryDeparture(const DepartureRequest &req, QNetworkAccessManager *nam) -{ - QUrl url; - url.setScheme(QStringLiteral("https")); - url.setHost(QStringLiteral("api.navitia.io")); - url.setPath( - QStringLiteral("/v1/coverage/") + QString::number(req.stop().latitude()) + QLatin1Char(';') + QString::number(req.stop().longitude()) + - QStringLiteral("/coord/") + QString::number(req.stop().latitude()) + QLatin1Char(';') + QString::number(req.stop().longitude()) + - QStringLiteral("/departures") - ); - - QUrlQuery query; - query.addQueryItem(QStringLiteral("from_datetime"), req.dateTime().toString(QStringLiteral("yyyyMMddThhmmss"))); - - // TODO: disable reply parts we don't care about - query.addQueryItem(QStringLiteral("disable_geojson"), QStringLiteral("true")); - query.addQueryItem(QStringLiteral("depth"), QStringLiteral("0")); - url.setQuery(query); - - QNetworkRequest netReq(url); - netReq.setRawHeader("Authorization", "48ed1733-d3f0-445a-9210-9fb36e20a8a3"); // ### this is the test key - - qDebug() << "GET:" << url; - return nam->get(netReq); -} diff --git a/src/publictransport/backends/navitiaclient.h b/src/publictransport/backends/navitiaclient.h deleted file mode 100644 index 7fb90a9..0000000 --- a/src/publictransport/backends/navitiaclient.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - Copyright (C) 2018 Volker Krause - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef KPUBLICTRANSPORT_NAVITIACLIENT_H -#define KPUBLICTRANSPORT_NAVITIACLIENT_H - -class QDateTime; -class QNetworkAccessManager; -class QNetworkReply; - -namespace KPublicTransport { - -class DepartureRequest; -class JourneyRequest; - -/** Navitia REST client methods. */ -namespace NavitiaClient -{ - -QNetworkReply* findJourney(const JourneyRequest &req, QNetworkAccessManager *nam); -QNetworkReply* queryDeparture(const DepartureRequest &req, QNetworkAccessManager *nam); - -} - -} - -#endif // KPUBLICTRANSPORT_NAVITIACLIENT_H diff --git a/src/publictransport/journeyreply.cpp b/src/publictransport/journeyreply.cpp index ca26916..edd99c8 100644 --- a/src/publictransport/journeyreply.cpp +++ b/src/publictransport/journeyreply.cpp @@ -1,126 +1,119 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "journeyreply.h" #include "reply_p.h" #include "journeyrequest.h" #include "logging.h" -#include "backends/navitiaclient.h" -#include "backends/navitiaparser.h" - #include #include #include -#include #include using namespace KPublicTransport; namespace KPublicTransport { class JourneyReplyPrivate : public ReplyPrivate { public: void finalizeResult() override; void postProcessJourneys(); JourneyRequest request; std::vector journeys; }; } void JourneyReplyPrivate::finalizeResult() { if (journeys.empty()) { return; } error = Reply::NoError; errorMsg.clear(); + + postProcessJourneys(); + std::sort(journeys.begin(), journeys.end(), [](const auto &lhs, const auto &rhs) { + return lhs.departureTime() < rhs.departureTime(); + }); } void JourneyReplyPrivate::postProcessJourneys() { // try to fill gaps in timezone data for (auto &journey : journeys) { auto sections = journey.takeSections(); for (auto §ion : sections) { if (section.mode() == JourneySection::Walking) { if (!section.from().timeZone().isValid() && section.to().timeZone().isValid()) { auto from = section.from(); from.setTimeZone(section.to().timeZone()); section.setFrom(from); auto dt = section.departureTime(); dt.setTimeZone(from.timeZone()); section.setDepartureTime(dt); } if (section.from().timeZone().isValid() && !section.to().timeZone().isValid()) { auto to = section.to(); to.setTimeZone(section.from().timeZone()); section.setTo(to); auto dt = section.arrivalTime(); dt.setTimeZone(to.timeZone()); section.setArrivalTime(dt); } } } journey.setSections(std::move(sections)); } } -JourneyReply::JourneyReply(const JourneyRequest &req, QNetworkAccessManager *nam) +JourneyReply::JourneyReply(const JourneyRequest &req) : Reply(new JourneyReplyPrivate) { Q_D(JourneyReply); d->request = req; - auto reply = NavitiaClient::findJourney(req, nam); - connect(reply, &QNetworkReply::finished, [reply, this] { - Q_D(JourneyReply); - switch (reply->error()) { - case QNetworkReply::NoError: - d->journeys = NavitiaParser::parseJourneys(reply->readAll()); - d->postProcessJourneys(); - break; - case QNetworkReply::ContentNotFoundError: - d->error = NotFoundError; - d->errorMsg = NavitiaParser::parseErrorMessage(reply->readAll()); - break; - default: - d->error = NetworkError; - d->errorMsg = reply->errorString(); - qCDebug(Log) << reply->error() << reply->errorString(); - } - - emit finished(); - deleteLater(); - }); } JourneyReply::~JourneyReply() = default; JourneyRequest JourneyReply::request() const { Q_D(const JourneyReply); return d->request; } std::vector JourneyReply::journeys() const { Q_D(const JourneyReply); // TODO avoid the copy here return d->journeys; } + +void JourneyReply::addResult(std::vector &&res) +{ + Q_D(JourneyReply); + if (d->journeys.empty()) { + d->journeys = std::move(res); + } else { + d->journeys.insert(d->journeys.end(), res.begin(), res.end()); + } + + d->pendingOps--; + d->emitFinishedIfDone(this); +} diff --git a/src/publictransport/journeyreply.h b/src/publictransport/journeyreply.h index 3d64b6a..e2f3e50 100644 --- a/src/publictransport/journeyreply.h +++ b/src/publictransport/journeyreply.h @@ -1,54 +1,56 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KPUBLICTRANSPORT_JOURNEYREPLY_H #define KPUBLICTRANSPORT_JOURNEYREPLY_H #include "reply.h" #include -class QNetworkAccessManager; - namespace KPublicTransport { class Journey; class JourneyReplyPrivate; class JourneyRequest; /** Journey query response. */ class JourneyReply : public Reply { Q_OBJECT public: ~JourneyReply(); /** The request this is the reply for. */ JourneyRequest request() const; /** Returns the found journeys. */ std::vector journeys() const; private: friend class Manager; - explicit JourneyReply(const JourneyRequest &req, QNetworkAccessManager *nam); + explicit JourneyReply(const JourneyRequest &req); + + friend class AbstractBackend; + void addResult(std::vector &&res); + Q_DECLARE_PRIVATE(JourneyReply) }; } #endif // KPUBLICTRANSPORT_JOURNEYREPLY_H diff --git a/src/publictransport/manager.cpp b/src/publictransport/manager.cpp index 90c1be5..b26c62d 100644 --- a/src/publictransport/manager.cpp +++ b/src/publictransport/manager.cpp @@ -1,137 +1,145 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "manager.h" #include "departurereply.h" #include "journeyreply.h" #include "logging.h" #include "backends/navitiabackend.h" #include #include #include #include #include using namespace KPublicTransport; static inline void initResources() { Q_INIT_RESOURCE(networks); } namespace KPublicTransport { class ManagerPrivate { public: QNetworkAccessManager* nam(); void loadNetworks(); std::unique_ptr loadNetwork(const QJsonObject &obj); template std::unique_ptr loadNetwork(const QJsonObject &obj); QNetworkAccessManager *m_nam = nullptr; std::vector> m_backends; }; } QNetworkAccessManager* ManagerPrivate::nam() { if (!m_nam) { m_nam = new QNetworkAccessManager; } return m_nam; } void ManagerPrivate::loadNetworks() { QDirIterator it(QStringLiteral(":/org.kde.pim/kpublictransport/networks")); while (it.hasNext()) { QFile f(it.next()); if (!f.open(QFile::ReadOnly)) { qCWarning(Log) << "Failed to open public transport network configuration:" << f.errorString(); continue; } const auto doc = QJsonDocument::fromJson(f.readAll()); auto net = loadNetwork(doc.object()); if (net) { m_backends.push_back(std::move(net)); } } qCDebug(Log) << m_backends.size() << "public transport network configurations loaded"; } std::unique_ptr ManagerPrivate::loadNetwork(const QJsonObject &obj) { const auto type = obj.value(QLatin1String("type")).toString(); if (type == QLatin1String("navitia")) { return loadNetwork(obj); } return {}; } static void applyBackendOptions(void *backend, const QMetaObject *mo, const QJsonObject &obj) { const auto opts = obj.value(QLatin1String("options")).toObject(); for (auto it = opts.begin(); it != opts.end(); ++it) { const auto idx = mo->indexOfProperty(it.key().toUtf8()); const auto mp = mo->property(idx); mp.writeOnGadget(backend, it.value().toVariant()); } } template std::unique_ptr ManagerPrivate::loadNetwork(const QJsonObject &obj) { std::unique_ptr backend(new T); applyBackendOptions(backend.get(), &T::staticMetaObject, obj); return backend; } Manager::Manager() : d(new ManagerPrivate) { initResources(); d->loadNetworks(); } Manager::Manager(Manager&&) noexcept = default; Manager::~Manager() = default; void Manager::setNetworkAccessManager(QNetworkAccessManager *nam) { // TODO delete d->nam if we created it ourselves d->m_nam = nam; } -JourneyReply* Manager::findJourney(const JourneyRequest &req) const +JourneyReply* Manager::queryJourney(const JourneyRequest &req) const { - return new JourneyReply(req, d->nam()); + auto reply = new JourneyReply(req); + int pendingOps = 0; + for (const auto &backend : d->m_backends) { + if (backend->queryJourney(reply, d->nam())) { + ++pendingOps; + } + } + reply->setPendingOps(pendingOps); + return reply; } DepartureReply* Manager::queryDeparture(const DepartureRequest &req) const { auto reply = new DepartureReply(req); int pendingOps = 0; for (const auto &backend : d->m_backends) { if (backend->queryDeparture(reply, d->nam())) { ++pendingOps; } } reply->setPendingOps(pendingOps); return reply; } diff --git a/src/publictransport/manager.h b/src/publictransport/manager.h index 280c14e..a8bae94 100644 --- a/src/publictransport/manager.h +++ b/src/publictransport/manager.h @@ -1,59 +1,59 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KPUBLICTRANSPORT_MANAGER_H #define KPUBLICTRANSPORT_MANAGER_H #include class QNetworkAccessManager; namespace KPublicTransport { class DepartureReply; class DepartureRequest; class JourneyReply; class JourneyRequest; class ManagerPrivate; /** Entry point for starting public transport queries. */ class Manager { public: Manager(); Manager(Manager&&) noexcept; Manager(const Manager&) = delete; ~Manager(); /** Set the network access manager to use for network operations. * If not set, an instance is created internally. */ void setNetworkAccessManager(QNetworkAccessManager *nam); /** Query a journey. */ - JourneyReply* findJourney(const JourneyRequest &req) const; + JourneyReply* queryJourney(const JourneyRequest &req) const; /** Query departures from a specific station. */ DepartureReply* queryDeparture(const DepartureRequest &req) const; private: std::unique_ptr d; }; } #endif // KPUBLICTRANSPORT_MANAGER_H diff --git a/tests/journeyquery.cpp b/tests/journeyquery.cpp index 8067403..54ca75b 100644 --- a/tests/journeyquery.cpp +++ b/tests/journeyquery.cpp @@ -1,125 +1,125 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KPublicTransport; class QueryManager : public QObject { Q_OBJECT Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged) Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorMessageChanged) public: QueryManager() { ptMgr.setNetworkAccessManager(&nam); } Q_INVOKABLE void findJourney(double fromLat, double fromLon, double toLat, double toLon) { engine->rootContext()->setContextProperty(QStringLiteral("_journeys"), QVariantList()); m_loading = true; emit loadingChanged(); m_errorMsg.clear(); emit errorMessageChanged(); Location from; from.setCoordinate(fromLat, fromLon); Location to; to.setCoordinate(toLat, toLon); - auto reply = ptMgr.findJourney({from, to}); + auto reply = ptMgr.queryJourney({from, to}); QObject::connect(reply, &JourneyReply::finished, [reply, this]{ m_loading = false; emit loadingChanged(); if (reply->error() == JourneyReply::NoError) { const auto res = reply->journeys(); QVariantList l; l.reserve(res.size()); std::transform(res.begin(), res.end(), std::back_inserter(l), [](const auto &journey) { return QVariant::fromValue(journey); }); engine->rootContext()->setContextProperty(QStringLiteral("_journeys"), l); for (const auto &journey : res) { qDebug() << journey.sections().size(); for (const auto §ion : journey.sections()) { qDebug() << " From" << section.from().name() << section.departureTime(); qDebug() << " Mode" << section.mode() << section.route().line().name() << section.route().direction() << section.route().line().modeString(); qDebug() << " To" << section.to().name() << section.arrivalTime(); } } } else { m_errorMsg = reply->errorString(); emit errorMessageChanged(); } }); } bool loading() const { return m_loading; } QString errorMessage() const { return m_errorMsg; } QQmlEngine *engine = nullptr; signals: void loadingChanged(); void errorMessageChanged(); private: QNetworkAccessManager nam; Manager ptMgr; QString m_errorMsg; bool m_loading = false; }; int main(int argc, char **argv) { QCoreApplication::setApplicationName(QStringLiteral("journeyquery")); QCoreApplication::setOrganizationName(QStringLiteral("KDE")); QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org")); QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QGuiApplication app(argc, argv); qmlRegisterUncreatableType("org.kde.kpublictransport", 1, 0, "Line", {}); qmlRegisterUncreatableType("org.kde.kpublictransport", 1, 0, "JourneySection", {}); QueryManager mgr; QQmlApplicationEngine engine; mgr.engine = &engine; engine.rootContext()->setContextProperty(QStringLiteral("_queryMgr"), &mgr); engine.load(QStringLiteral("qrc:/journeyquery.qml")); return app.exec(); } #include "journeyquery.moc"