网络天气预报项目笔记(Qt)

项目简介:

1,使用Qt设计一款天气预报的程序,主要包括:

  1. 界面显示:当日天气情况、空气质量等级、温湿度等空气指数、每日祝福语以及未来几日天气预报;根据近几日的温度数据绘制温度曲线;支持搜索指定城市功能以及刷新数据功能。
  2. 数据处理:QJson的读取与解析。
  3. 学习和使用Qt的信号与槽。

2,运行效果图(天气预报数据来源于网络):

在这里插入图片描述

部分代码整理:

1,温度曲线的绘制

void PaintCurve()
{
    QPainter painter(ui->curveLb);
    //反锯齿 使绘制出的线光滑
    painter.setRenderHint(QPainter::Antialiasing,true);

    int iTempTotal=0;
    int high[6]={};
    QString strHigh;
    for(int i=0;i<6;++i)
    {
        strHigh=m_forecast[i].high;
        strHigh=strHigh.mid(3);
        strHigh=strHigh.left(2);
        high[i]=(int)(strHigh.toDouble());
        iTempTotal+=high[i];
    }
    //高温的平均值
    int iTempAverage=(int)(iTempTotal/6);

    //算出温度对应的点坐标
    int pointX[6]={45,133,221,309,397,485};//x坐标的具体计算要结合所设置的控件宽度以及要显示数据的组数
    int pointY_h[6]={};
    for(int i=0;i<6;++i)
    {
        pointY_h[i]=TEMPERATURE_STARTING_COORDINATE - ((high[i]-iTempAverage)* SPAN_INDEX);
    }

    QPen pen=painter.pen();
    pen.setWidth(1);//设置画笔的宽度

    //高温曲线绘制
    painter.save();
    //昨天到今天的数据
    pen.setColor(QColor(255,170,0));
    pen.setStyle(Qt::DotLine);
    painter.setPen(pen);
    painter.setBrush(QColor(255,170,0));
    painter.drawEllipse(QPoint(pointX[0],pointY_h[0]),ORIGIN_SIZE,ORIGIN_SIZE);
    painter.drawEllipse(QPoint(pointX[1],pointY_h[1]),ORIGIN_SIZE,ORIGIN_SIZE);
    painter.drawLine(pointX[0],pointY_h[0],pointX[1],pointY_h[1]);
    //今天到未来四天的数据
    pen.setStyle(Qt::SolidLine);
    pen.setWidth(1);
    painter.setPen(pen);
    for(int i=1;i<5;++i)
    {
        painter.drawEllipse(QPoint(pointX[i+1],pointY_h[i+1]),ORIGIN_SIZE,ORIGIN_SIZE);
        painter.drawLine(pointX[i],pointY_h[i],pointX[i+1],pointY_h[i+1]);
    }
    painter.restore();
}

其中m_forecast是封装的类Forecast的对象,主要保存天气的数据(或者说是个结构体可能更直观)。
在这里插入图片描述
QPainter的使用要保证save()与restore()成对出现,即开始会之前调用下save(),绘制完成后调用下restore()。

2,搜索与刷新

//刷新按钮
void on_refreshBt_clicked()
{
	//获取城市天气数据
    GetWeatherInfo(m_pManager);
    //得到新数据后调用下绘制曲线的控件的刷新,进行绘制
    ui->curveLb->update();
}

//搜索按钮
void on_searchBt_clicked()
{
	//保存当前城市,当搜索出现错误时,还可以继续显示当前城市
    m_strCityTemp=m_strCity;
    m_strCity=ui->cityLineEdit->text();
    GetWeatherInfo(m_pManager);
}

其中m_pManager是QNetworkAccessManager的一个指针,是网络编程部分的,与其相关的还有另外两个类,大致关系为
在这里插入图片描述
对于一个应用程序来说,一个QNetworkAccessManager已经足够了。
以刷新功能为例,通过自己实现的方法GetWeatherInfo去向服务器发送请求获取数据,通过m_pManager的信号得知服务器应答完成并处理数据,内容如下:

//初始化时绑定的信号槽
connect(m_pManager,&QNetworkAccessManager::finished,
        this,&MainWindow::replyFinished);

//GetWeatherInfo的实现
void GetWeatherInfo(QNetworkAccessManager*& manager)
{
    //根据城市获取城市邮编
    QString strCityCode=m_tool[m_strCity];
    //获取失败 或 输入为省份 或 没有覆盖到该城市
    if(strCityCode=="000000000")
    {
        QMessageBox::warning(this,u8"错误",u8"天气:指定城市不存在!",QMessageBox::Ok);
        return;
    }
    //拼好url:链接+城市邮编
    QUrl jsonUrl(m_strUrl+strCityCode);
    //发送网络请求
    manager->get(QNetworkRequest(jsonUrl));
}

//网络请求完成后的响应槽函数
void replyFinished(QNetworkReply* reply)
{
    //获取响应的消息 状态码200表示正常
    QVariant status_code=reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
    //出错
    if(reply->error()!=QNetworkReply::NoError || status_code!=200)
    {
        QMessageBox::warning(this,u8"错误",u8"天气:请求数据错误,检查网络连接!",QMessageBox::Ok);
        return;
    }
    //获取数据成功 读取全部数据
    QByteArray bytes=reply->readAll();
    //解析json数据
    ParseJson(bytes);
}

3,QJson的读取与解析

如果前面从网络获取数据成功的话,QByteArray bytes=reply->readAll();bytes就会那到如下样式的内容:
在这里插入图片描述
将这些乱乱的数据处理一下,看起来直观些:
在这里插入图片描述
篇幅有限就不截全图了,现在key和value就可以非常直观的看出来了(安利一个json解析的链接:https://www.json.cn/)

然后通过ParseJson(bytes)函数去解析数据:

void ParseJson(QByteArray &bytes)
{
    //首先将bytes数据转换成一个document对象
    QJsonParseError err;
    QJsonDocument jsonDoc = QJsonDocument::fromJson(bytes,&err);
    //转换出错
    if(err.error!=QJsonParseError::NoError)
        return;
    //
    QJsonObject jsonObj=jsonDoc.object();
    QString message=jsonObj.value("message").toString();
    //返回false说明message中没有包含success 即失败
    if(message.contains("success")==false)
    {
        QMessageBox::information(this,tr("The Information Of Json_desc"),u8"天气:城市错误!",QMessageBox::Ok);
        m_strCity=m_strCityTemp;
        return;
    }
    m_today=jsonObj;

    //解析data中的yesterday
    QJsonObject dataObj=jsonObj.value("data").toObject();
    m_forecast[0]=dataObj.value("yesterday").toObject();

    //解析data中的forecast
    QJsonArray forecastArr=dataObj.value("forecast").toArray();
    //j=0是因为从网上取到的预报数据中是不存在昨天的 即索引0处就是今天 1是明天...
    int j=0;
    for(int i=1;i<6;++i)
    {//索引0处表示的是昨天的信息 所以应继续从索引1处开始取数据
        m_forecast[i]=forecastArr.at(j).toObject();
        j++;
    }

    //更新UI界面数据
    SetLabelContent();
}

另外项目中在获取城市天气预报的服务中使用的是城市的邮政编码,所以还存在一个输入城市输出邮政编码的方法:

//构造时会去缓存城市名称-城市邮政编码的map
WeatherTool()
{
    //获取当前程序的相对路径 继而得到城市邮编文件
    QString fileName=QCoreApplication::applicationDirPath();
    fileName+="/citycode-2021-06-23.json";

    //用文本形式以只读的方式打开文件 将数据读取到json中,关闭文件
    QFile file(fileName);
    file.open(QIODevice::ReadOnly|QIODevice::Text);
    QByteArray json=file.readAll();
    file.close();

    //按照文件的内容及json的规定 可以将数据分解成一个由城市组成的数组,然后遍历该数组
    QJsonParseError err;
    QJsonDocument jsonDoc=QJsonDocument::fromJson(json,&err);
    QJsonArray citys=jsonDoc.array();
    for(int i=0;i<citys.size();++i)
    {
        //将每个城市先转成一个对象,然后访问每个对象的key值为city_code处,得到value后转成string返回,其他同理
        QString code=citys.at(i).toObject().value("city_code").toString();
        QString city=citys.at(i).toObject().value("city_name").toString();
        if(code.size()>0)
            m_mapCityToCode[city]=code;
    }
}

//传入城市 传出邮编
QString operator[](const QString& city)
{
    std::map<QString,QString>::iterator iter=m_mapCityToCode.find(city);
    if(iter==m_mapCityToCode.end())
        iter=m_mapCityToCode.find(city+u8"市");
    if(iter!=m_mapCityToCode.end())
        return iter->second;
    return "000000000";
}

配置的json文档大致如下:
在这里插入图片描述

4,信号与槽

类似于MFC的消息,但要比MFC的消息灵活很多,绑定的信号槽的语法为:
connect(信号发送者,&发了什么信号,信号接收者,&处理信号的函数)
以button为例,基础的用法为:
connect(pBtn,&QPushButton::clicked,this,&ClassName::OnBtnClicked)
信号与槽都为void类型,参数可有可无,信号只需定义无需实现;
信号可以传递,也可绑定多个槽;
信号的参数≥槽的参数,但相等的部分的参数类型必须一一对应。

5,Qt对象树的概念

Qt中的QObject会用对象树管理自己,它会用一个私有变量QList<QObject*>存储子孙后代,如创建一个QObject对象并指定父指针时,就会保存它们的父子关系(这里的父子关系与类的继承无关)。当父对象析构时,会直接析构掉其下的所有子对象。
但事物都有两面性,有好就有坏,针对于对象树,其好处也恰恰是坏处。
如下代码,运行后必崩溃:
在这里插入图片描述
在这里插入图片描述
关于崩溃的解释:
在函数内声明的临时变量是存储在栈上的,先入后出,所以当main函数执行结束后,会先释放Widget的对象w,而button在声明时已指定了父对象为w,所以当释放w的时候会直接释放掉button;当w释放完成后,栈会去释放button,就会二次析构,又因为button中有些data是new出来的,所以需要delete,所以这也就是为什么崩溃报错在heap上了。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值