Weather Info (C++/QML)
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 QML.
Key Qt Positioning classes used in this example:
Running the Example
To run the example from Qt Creator, open the Welcome mode and select the example from Examples. For more information, visit Building and Running an Example.
Application Data Models
The example uses weather data provided by http://www.openweathermap.org.
The key part of this example is the application's data model, contained in the WeatherData
and AppModel
classes. 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); 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->nam = new QNetworkAccessManager(this); d->src = QGeoPositionInfoSource::createDefaultSource(this); if (d->src) { d->useGps = true; connect(d->src, SIGNAL(positionUpdated(QGeoPositionInfo)), this, SLOT(positionUpdated(QGeoPositionInfo))); connect(d->src, SIGNAL(errorOccurred(QGeoPositionInfoSource::Error)), this, SLOT(positionError(QGeoPositionInfoSource::Error))); d->src->startUpdates(); } else { d->useGps = false; d->city = "Brisbane"; emit cityChanged(); this->refreshWeather(); } }
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
and call 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 the current "city" name for use in the weather lookup.
void AppModel::positionUpdated(QGeoPositionInfo gpsPos) { d->coord = gpsPos.coordinate(); if (!(d->useGps)) return; queryCity(); }
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); void hadError(bool tryAgain); 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(); };
We use a QQmlListProperty for the weather forecast information, which contains the next 4 days of forecast weather. This makes it easy to access from QML.
Expose Custom Models to 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.
CMake Build
For a CMake-based build, we need to add the following to the CMakeLists.txt
:
set_target_properties(weatherinfo PROPERTIES QT_QML_MODULE_VERSION 1.0 QT_QML_MODULE_URI WeatherInfo ) qt6_qml_type_registration(weatherinfo)
qmake Build
For a qmake build, we need to modify the weatherinfo.pro
file in the following way:
CONFIG += qmltypes QML_IMPORT_NAME = WeatherInfo QML_IMPORT_MAJOR_VERSION = 1
Instantiate the Models in QML
Finally, in the actual QML, we instantiate the AppModel
:
import WeatherInfo 1.0 Item { id: window AppModel { id: model onReadyChanged: { if (model.ready) window.state = "ready" else window.state = "loading" } } }
Once the model is instantiated like this, we can use its properties elsewhere in the QML document:
BigForecastIcon { id: current width: main.width -12 height: 2 * (main.height - 25 - 12) / 3 weatherIcon: (model.hasValidWeather ? model.weather.weatherIcon : "01d") }
Files and Attributions
The example bundles the following images from Third-Party sources:
Tango Icons | Public Domain |
Tango Weather Icon Pack by Darkobra | Public Domain |