The Weather Info example shows how to use the user's current position to retrieve local content from a web service in a C++ plugin for Qt Quick ,使用 Qt Positioning .
Key Qt Positioning 类被用于此范例:
要运行范例从 Qt Creator ,打开 欢迎 模式,然后选择范例从 范例 。更多信息,拜访 构建和运行范例 .
The example uses several unrelated weather data providers:
The provider to be used is selected automatically at runtime and can be changed if the selected provider is not available. However, it can't be specified manually.
注意: Free plans are used for all providers, which implies certain limitations on the amount of weather requests. If the limits are exceeded, the providers become temporary unavailable. When all providers are unavailable, the application would not be able to show any weather information. In this case it is required to wait until at least one of the providers becomes available again.
The key part of this example is the application's data model, contained in the
WeatherData
and
AppModel
类。
WeatherData
represents the weather information taken from the HTTP service. It is a simple data class, but we use
Q_PROPERTY
to expose it nicely to QML later. It also uses
QML_ANONYMOUS
macro, which makes it recognized in QML.
class WeatherData : public QObject { Q_OBJECT Q_PROPERTY(QString dayOfWeek READ dayOfWeek WRITE setDayOfWeek NOTIFY dataChanged) Q_PROPERTY(QString weatherIcon READ weatherIcon WRITE setWeatherIcon NOTIFY dataChanged) Q_PROPERTY(QString weatherDescription READ weatherDescription WRITE setWeatherDescription NOTIFY dataChanged) Q_PROPERTY(QString temperature READ temperature WRITE setTemperature NOTIFY dataChanged) QML_ANONYMOUS public: explicit WeatherData(QObject *parent = 0); WeatherData(const WeatherData &other); WeatherData(const WeatherInfo &other); QString dayOfWeek() const; QString weatherIcon() const; QString weatherDescription() const; QString temperature() const; void setDayOfWeek(const QString &value); void setWeatherIcon(const QString &value); void setWeatherDescription(const QString &value); void setTemperature(const QString &value); signals: void dataChanged(); };
AppModel
models the state of the entire application. At startup, we get the platform's default position source using
QGeoPositionInfoSource::createDefaultSource
().
AppModel::AppModel(QObject *parent) : QObject(parent), d(new AppModelPrivate) { d->src = QGeoPositionInfoSource::createDefaultSource(this); if (d->src) { d->useGps = true; connect(d->src, &QGeoPositionInfoSource::positionUpdated, this, &AppModel::positionUpdated); connect(d->src, &QGeoPositionInfoSource::errorOccurred, this, &AppModel::positionError); #if QT_CONFIG(permissions) QLocationPermission permission; permission.setAccuracy(QLocationPermission::Precise); permission.setAvailability(QLocationPermission::WhenInUse); switch (qApp->checkPermission(permission)) { case Qt::PermissionStatus::Undetermined: qApp->requestPermission(permission, [this] (const QPermission& permission) { if (permission.status() == Qt::PermissionStatus::Granted) d->src->startUpdates(); else positionError(QGeoPositionInfoSource::AccessError); }); break; case Qt::PermissionStatus::Denied: qWarning("Location permission is denied"); positionError(QGeoPositionInfoSource::AccessError); break; case Qt::PermissionStatus::Granted: d->src->startUpdates(); break; } #else d->src->startUpdates(); #endif } else { d->useGps = false; d->city = "Brisbane"; emit cityChanged(); requestWeatherByCity(); } QTimer *refreshTimer = new QTimer(this); connect(refreshTimer, &QTimer::timeout, this, &AppModel::refreshWeather); using namespace std::chrono; refreshTimer->start(60s); }
If no default source is available, we take a static position and fetch weather for that. If, however, we do have a position source, we connect its
positionUpdated
() signal to a slot on the
AppModel
和调用
startUpdates
(), which begins regular updates of device position.
When a position update is received, we use the longitude and latitude of the returned coordinate to retrieve weather data for the specified location.
void AppModel::positionUpdated(QGeoPositionInfo gpsPos) { d->coord = gpsPos.coordinate(); if (!d->useGps) return; requestWeatherByCoordinates(); }
To inform the UI about this process, the
cityChanged()
signal is emitted when a new city is used, and the
weatherChanged()
signal whenever a weather update occurs.
The model also uses QML_ELEMENT macro, which makes it available in QML.
class AppModel : public QObject { Q_OBJECT Q_PROPERTY(bool ready READ ready NOTIFY readyChanged) Q_PROPERTY(bool hasSource READ hasSource NOTIFY readyChanged) Q_PROPERTY(bool hasValidCity READ hasValidCity NOTIFY cityChanged) Q_PROPERTY(bool hasValidWeather READ hasValidWeather NOTIFY weatherChanged) Q_PROPERTY(bool useGps READ useGps WRITE setUseGps NOTIFY useGpsChanged) Q_PROPERTY(QString city READ city WRITE setCity NOTIFY cityChanged) Q_PROPERTY(WeatherData *weather READ weather NOTIFY weatherChanged) Q_PROPERTY(QQmlListProperty<WeatherData> forecast READ forecast NOTIFY weatherChanged) QML_ELEMENT public: explicit AppModel(QObject *parent = 0); ~AppModel(); bool ready() const; bool hasSource() const; bool useGps() const; bool hasValidCity() const; bool hasValidWeather() const; void setUseGps(bool value); QString city() const; void setCity(const QString &value); WeatherData *weather() const; QQmlListProperty<WeatherData> forecast() const; public slots: Q_INVOKABLE void refreshWeather(); signals: void readyChanged(); void useGpsChanged(); void cityChanged(); void weatherChanged(); };
使用 QQmlListProperty for the weather forecast information, which contains the weather forecast for the next days (the number of days is provider-specific). This makes it easy to access the forecast from QML.
To expose the models to the QML UI layer, we use the QML_ELEMENT and QML_ANONYMOUS macros. See the QQmlEngine class description for more details on these macros.
To make the types available in QML, we need to update our build accordingly.
For a CMake-based build, we need to add the following to the
CMakeLists.txt
:
qt_add_qml_module(weatherinfo URI Weather VERSION 1.0 SOURCES appmodel.cpp appmodel.h openmeteobackend.cpp openmeteobackend.h openweathermapbackend.cpp openweathermapbackend.h providerbackend.cpp providerbackend.h weatherapibackend.cpp weatherapibackend.h QML_FILES BigForecastIcon.qml ForecastIcon.qml WeatherIcon.qml WeatherInfo.qml RESOURCES icons/weather-few-clouds.svg icons/weather-fog.svg icons/weather-haze.svg icons/weather-icy.svg icons/weather-overcast.svg icons/weather-showers.svg icons/weather-sleet.svg icons/weather-snow.svg icons/weather-storm.svg icons/weather-sunny-very-few-clouds.svg icons/weather-sunny.svg icons/weather-thundershower.svg icons/weather-showers-scattered.svg icons/waypoint.svg )
For a qmake build, we need to modify the
weatherinfo.pro
file in the following way:
CONFIG += qmltypes QML_IMPORT_NAME = Weather QML_IMPORT_MAJOR_VERSION = 1 qml_resources.files = \ qmldir \ BigForecastIcon.qml \ ForecastIcon.qml \ WeatherIcon.qml \ WeatherInfo.qml \ icons/weather-few-clouds.svg \ icons/weather-fog.svg \ icons/weather-haze.svg \ icons/weather-icy.svg \ icons/weather-overcast.svg \ icons/weather-showers.svg \ icons/weather-showers-scattered.svg \ icons/weather-sleet.svg \ icons/weather-snow.svg \ icons/weather-storm.svg \ icons/weather-sunny-very-few-clouds.svg \ icons/weather-sunny.svg \ icons/weather-thundershower.svg \ icons/waypoint.svg qml_resources.prefix = /qt/qml/Weather RESOURCES += qml_resources
Finally, in the actual QML, we instantiate the
AppModel
:
Window { id: window AppModel { id: appModel onReadyChanged: { if (appModel.ready) statesItem.state = "ready" else statesItem.state = "loading" } } }
Once the model is instantiated like this, we can use its properties elsewhere in the QML document:
BigForecastIcon { id: current Layout.fillWidth: true Layout.fillHeight: true weatherIcon: (appModel.hasValidWeather ? appModel.weather.weatherIcon : "sunny") }
The example bundles the following images from Third-Party sources:
Tango Icons | 公共领域 |
Tango Weather Icon Pack by Darkobra | 公共领域 |