Weather Info (C++/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.
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 give it Q_PROPERTies to expose it nicely to QML, later.
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)
public
:
explicit
WeatherData(QObject *
parent =
0
);
WeatherData(const
WeatherData &
amp;other);
QString dayOfWeek() const
;
QString weatherIcon() const
;
QString weatherDescription() const
;
QString temperature() const
;
void
setDayOfWeek(const
QString &
amp;value);
void
setWeatherIcon(const
QString &
amp;value);
void
setWeatherDescription(const
QString &
amp;value);
void
setTemperature(const
QString &
amp;value);
signals
:
void
dataChanged();
}
;
AppModel models the state of the entire application. At startup, the application first begins by waiting for network connectivity. We do this using the QNetworkConfigurationManager and QNetworkSession family of C++ APIs.
AppModel::
AppModel(QObject *
parent) :
QObject(parent),
d(new
AppModelPrivate)
{
// make sure we have an active network session
d-&
gt;nam =
new
QNetworkAccessManager(this
);
QNetworkConfigurationManager ncm;
d-&
gt;ns =
new
QNetworkSession(ncm.defaultConfiguration(), this
);
connect(d-&
gt;ns, SIGNAL(opened()), this
, SLOT(networkSessionOpened()));
// the session may be already open. if it is, run the slot directly
if
(d-&
gt;ns-&
gt;isOpen())
this
-&
gt;networkSessionOpened();
// tell the system we want network
d-&
gt;ns-&
gt;open();
}
Once the network session is open, we proceed to get the platform's default position source using QGeoPositionInfo::createDefaultSource()
void
AppModel::
networkSessionOpened()
{
d-&
gt;src =
QGeoPositionInfoSource::
createDefaultSource(this
);
if
(d-&
gt;src) {
d-&
gt;useGps =
true
;
connect(d-&
gt;src, SIGNAL(positionUpdated(QGeoPositionInfo)),
this
, SLOT(positionUpdated(QGeoPositionInfo)));
connect(d-&
gt;src, SIGNAL(error(QGeoPositionInfoSource::
Error)),
this
, SLOT(positionError(QGeoPositionInfoSource::
Error)));
d-&
gt;src-&
gt;startUpdates();
}
else
{
d-&
gt;useGps =
false
;
d-&
gt;city =
"Brisbane"
;
emit cityChanged();
this
-&
gt;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-&
gt;coord =
gpsPos.coordinate();
if
(!
(d-&
gt;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.
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&
lt;WeatherData&
gt; forecast
READ forecast
NOTIFY weatherChanged)
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 &
amp;value);
WeatherData *
weather() const
;
QQmlListProperty&
lt;WeatherData&
gt; 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.
To expose these to the QML UI layer, we use the qmlRegisterType() function. We call this once for each type we wish to register, before loading the actual QML file.
#include
"appmodel.h"
int
main(int
argc, char
*
argv[])
{
QLoggingCategory::
setFilterRules("wapp.*.debug=false"
);
QGuiApplication application(argc, argv);
qmlRegisterType&
lt;WeatherData&
gt;("WeatherInfo"
, 1
, 0
, "WeatherData"
);
qmlRegisterType&
lt;AppModel&
gt;("WeatherInfo"
, 1
, 0
, "AppModel"
);
const
QString mainQmlApp =
QStringLiteral("qrc:///weatherinfo.qml"
);
QQuickView view;
view.setSource(QUrl(mainQmlApp));
view.setResizeMode(QQuickView::
SizeRootObjectToView);
QObject::
connect(view.engine(), SIGNAL(quit()), qApp, SLOT(quit()));
view.setGeometry(QRect(100
, 100