本页内容

天气信息

“天气信息”示例演示了如何在针对 Qt Quick,并使用 Qt Positioning。

本示例中使用的 Qt Positioning 类:

天气信息示例界面

运行示例

您可以从以下位置运行该示例:

气象数据提供商

该示例使用了几个互不相关的天气数据提供商:

将在运行时自动选择要使用的提供商,如果所选提供商不可用,则可以进行更改。但是,无法手动指定。

注意: 所有服务提供商均使用免费 套餐,这意味着天气查询次数会受到一定限制。若超过限制,相关服务提供商将暂时不可用。当所有服务提供商均不可用时,应用程序将无法显示任何天气信息。此时需等待,直至至少有一家服务提供商恢复可用。

应用程序数据模型

本示例的核心部分是应用程序的数据模型,它包含在WeatherData 和AppModel 类中。WeatherData 代表从HTTP服务获取的天气信息。虽然这是一个简单的数据类,但我们使用Q_PROPERTY 使其在后续的QML中能够被良好地呈现。它还使用了QML_ANONYMOUS 宏,从而使其在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 该类描述了整个应用程序的状态。在启动时,我们通过QGeoPositionInfoSource::createDefaultSource() 获取平台的默认位置源。

AppModel::AppModel(QObject*parent) :
        QObject(parent),
        d(newAppModelPrivate)
{
    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)) {
        caseQt::PermissionStatus::Undetermined:
            qApp->requestPermission(permission, [this](constQPermission&permission) {
                if(permission.status()==Qt::PermissionStatus::Granted)
                    d->src->startUpdates();
                else
                    positionError(QGeoPositionInfoSource::AccessError);
            });
            break;
        caseQt::PermissionStatus::Denied:
            qWarning("Location permission is denied");
            positionError(QGeoPositionInfoSource::AccessError);
            break;
        caseQt::PermissionStatus::Granted:
            d->src->startUpdates();
            break;
        }
#else
        d->src->startUpdates();
#endif
    }else{
        d->useGps= false;
        d->city= "布里斯班";
        emitcityChanged();
        requestWeatherByCity();
    }

    QTimer*refreshTimer = newQTimer(this);
    connect(refreshTimer, &QTimer::timeout, this, &AppModel::refreshWeather);
    using namespacestd::chrono;
    refreshTimer->start(60s);
}

如果没有可用的默认来源,我们将采用静态位置并据此获取天气信息。但如果确实有位置来源,我们会将其positionUpdated()信号连接到AppModel 上的一个插槽,并调用startUpdates(),从而开始定期更新设备位置。

收到位置更新时,我们会使用返回坐标的经度和纬度来获取指定位置的天气数据。

void AppModel::positionUpdated(QGeoPositionInfo gpsPos)
{
    d->coord = gpsPos.coordinate();

    if (!d->useGps)
        return;

    requestWeatherByCoordinates();
}

为了向用户界面反馈这一过程,当使用新城市时会发出cityChanged() 信号,而每当天气数据更新时则会发出weatherChanged() 信号。

该模型还使用了QML_ELEMENT 宏,从而使其可在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 来获取天气预报信息,其中包含未来几天的天气预报(具体天数取决于服务提供商)。这使得从 QML 中访问预报信息变得非常方便。

将自定义模型暴露给 QML

为了将模型暴露给 QML UI 层,我们使用QML_ELEMENT 和QML_ANONYMOUS 宏。有关这些宏的更多详细信息,请参阅QQmlEngine 类的描述。

为了使这些类型在 QML 中可用,我们需要相应地更新构建配置。

CMake 构建

对于基于 CMake 的构建,我们需要在 `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
)
qmake 构建

对于 qmake 构建,我们需要按以下方式修改weatherinfo.pro 文件:

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

在 QML 中实例化模型

最后,在实际的 QML 文件中,我们实例化 `AppModel`:

Window {
    id: window
    AppModel {
        id: appModel
        onReadyChanged: {
            if (appModel.ready)
                statesItem.state = "ready"
            else
                statesItem.state = "loading"
        }
    }
}

一旦模型以这种方式实例化,我们就可以在 QML 文档的其他位置使用其属性:

    BigForecastIcon {
        id: current
        Layout.fillWidth: true
        Layout.fillHeight: true

        weatherIcon: (appModel.hasValidWeather
                      ? appModel.weather.weatherIcon
                      : "sunny")
    }

示例项目 @ code.qt.io

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.