Qt项目天气预报(8) - 绘制温度曲线 + 回车搜索(最终篇)

news2024/11/24 4:27:08

全部内容在专栏

Qt项目 天气预报_mx_jun的博客-CSDN博客

目录

绘制温度曲线

事件过滤器在子控件上绘图

    子控件下载事件过滤器

事件过滤器进行绘图 - eventFilter

画初步高温曲线

画初步低温曲线

效果演示

 画低温曲线

 画高温曲线

效果演示

按下回车搜索:

returnPress()槽函数

整体代码

widget.h

widget.cpp

day.h

day.cpp(无内容)

citycodeutils.h

citycodeutils.cpp

整体ui预览

项目总结: 


绘制温度曲线

事件过滤器在子控件上绘图


//难点 -- 我们已经添加了很多控件和样式表,在大的this对象上绘图有难度
//我们可以调用事件过滤器去在子控件上绘图 


    子控件下载事件过滤器


    ui->widget0404->installEventFilter(this);
    ui->widget0405->installEventFilter(this);


事件过滤器进行绘图 - eventFilter

bool Widget::eventFilter(QObject *watched, QEvent *event)
{
    if(watched == ui->widget0404  && event->type() == QEvent::Paint){//在这个控件触发绘图事件,就调用api绘图
     drawTempLineHigh();
     return true; //表示事件已经被处理
    }
    if(watched == ui->widget0405  && event->type() == QEvent::Paint){//在这个控件触发绘图事件,就调用api绘图
     drawTempLineLow();
     return true; //表示事件已经被处理
    }
    return QWidget::eventFilter(watched, event);
}

画初步高温曲线

void Widget::drawTempLineHigh()
{
    //在widget0404 上绘图
  QPainter painter(ui->widget0404);
  painter.setPen(Qt::yellow);
  painter.drawLine(QPoint(10,10),QPoint(30,30));

}

画初步低温曲线


 

void Widget::drawTempLineLow()
{
    //在widget0405 上绘图
  QPainter painter(ui->widget0405);
  painter.setPen(Qt::yellow);
  painter.drawLine(QPoint(10,10),QPoint(30,30));

}

效果演示


 

 画低温曲线

void Widget::drawTempLineLow()
{
    //在widget0405 上绘图
  QPainter painter(ui->widget0405);
  painter.setPen(QColor(70, 192, 203));
  painter.setBrush(QColor(70, 192, 203));//设置画刷,让圆变成实心
  painter.setRenderHint(QPainter::Antialiasing,true);

  // 计算七天最大温度的平均值
  int ave;
  int sum = 0;
  int offset = 0; // 偏移值
  int middle = ui->widget0405->height()/2;
  for(int i=0;i<6;++i){
  sum += day[i].mTempLow.toInt();
  }
  ave = sum/6;
  //定义出6个点
  QPoint points[6];
  for(int i=0;i<6;++i){
   points[i].setX(mAirqList[i]->x() + mAirqList[i]->width()/2); // 设置在对应上方控件的中间位置
   offset = (day[i].mTempLow.toInt() - ave)*4; // 计算偏移值
   points[i].setY(middle-offset); // 记得y轴正向朝下,so我们这里是-
   // 画出6个温度点
   painter.drawEllipse(QPoint(points[i]),3,3);
   // 画出对应温度
   painter.drawText(points[i].x()-15,points[i].y()-15,day[i].mTempLow+"°");
  }

  //画出5条线将6个点相连接
  for(int i=0;i<5;++i){
      painter.drawLine(points[i],points[i+1]);
  }

}


 


 画高温曲线
 


void Widget::drawTempLineHigh()
{
    //在widget0404 上绘图
  QPainter painter(ui->widget0404);
  painter.setPen(Qt::yellow);
  painter.setBrush(Qt::yellow);//设置画刷,让圆变成实心
  painter.setRenderHint(QPainter::Antialiasing,true);

  // 计算七天最大温度的平均值
  int ave;
  int sum = 0;
  int offset = 0; // 偏移值
  int middle = ui->widget0404->height()/2;
  for(int i=0;i<6;++i){
  sum += day[i].mTempHigh.toInt();
  }
  ave = sum/6;
  //定义出6个点
  QPoint points[6];
  for(int i=0;i<6;++i){
   points[i].setX(mAirqList[i]->x() + mAirqList[i]->width()/2); // 设置在对应上方控件的中间位置
   offset = (day[i].mTempHigh.toInt() - ave)*4; // 计算偏移值
   points[i].setY(middle-offset); // 记得y轴正向朝下,so我们这里是-
   // 画出6个温度点
   painter.drawEllipse(QPoint(points[i]),3,3);
   // 画出对应温度
   painter.drawText(points[i].x()-15,points[i].y()-15,day[i].mTempHigh+"°");
  }

  //画出5条线将6个点相连接
  for(int i=0;i<5;++i){
      painter.drawLine(points[i],points[i+1]);
  }

}

效果演示



按下回车搜索:

returnPress()槽函数

//按下回车刷新
void Widget::on_lineEditCity_returnPressed()
{
  on_pushButton_clicked(); // 回车被按下,调用搜索槽函数
}

整体代码

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QLabel>
#include <QMenu>
#include <QNetworkReply>
#include <QWidget>
#include <QList>

#include "citycodeutils.h"
#include "day.h"

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Day day[7];
    // 定义7个列表去存放我们的label控件内容
    QList<QLabel *> mDateList;
    QList<QLabel *> mWeekList;
    QList<QLabel *> mIconList;
    QList<QLabel *> mWeatypeList;
    QList<QLabel *> mAirqList;
    QList<QLabel *> mFxList;
    QList<QLabel *> mFlList;


    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
bool eventFilter(QObject *watched,QEvent *event);

public slots:
 void readHttpReply(QNetworkReply* reply);

private slots:
 void on_pushButton_clicked();

 void on_lineEditCity_returnPressed();

private:
    Ui::Widget *ui;
    QMenu *menuQuit;
    QPoint mOffset; //偏移值
    QNetworkReply* reply;

    QString urlyiKe; // 定义全局的url
    QNetworkAccessManager *manager;
    CityCodeUtils cityCodeUtils;
    QMap<QString,QString> mTypeMap;

    void parseWeatherJsonData(QByteArray rawData);
    void parseWeatherJsonDataNew(QByteArray rawData);// 七天的
    void updateUI();
    void drawTempLineHigh();
    void drawTempLineLow();
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

#include <QMouseEvent>
#include <QDebug>
#include <QNetworkAccessManager>
#include <QMessageBox>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QPainter>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    setFixedSize(586,1048); // 固定大小
    setWindowFlag(Qt::FramelessWindowHint); //去除上方边框

    menuQuit = new QMenu(this);
    //设置菜单项文字颜色:白色
    menuQuit->setStyleSheet("QMenu::item{color:white}");

    //创建行为对象 -- 当调用这种行为打的时候弹出close.png这张图片, 指向这个图片的时候显示"退出"字样
    QAction *closeAct = new QAction(QIcon(":/close.png"), tr("退出"), this);
    menuQuit->addAction(closeAct); //为menu添加退出行为
    // 为menu绑定信号与槽
    connect(menuQuit,&QMenu::triggered,this,[=](){
        this->close();
    });



    //由QNetworkAccessManager 发起request请求:
    //实例化network对象
    manager = new QNetworkAccessManager(this);
    connect(manager, &QNetworkAccessManager::finished,[](){
        qDebug()<<"manager Finnish!";
    });

    // http://t.weather.itboy.net/api/weather/city/101010100
    QUrl urlItBoy("http://t.weather.itboy.net/api/weather/city/101010100");
    //http://v1.yiketianqi.com/api?unescape=1&version=v91&appid=65521391&appsecret=Dv2eKMLL&ext=&cityid=
    // 一天
    //urlyiKe ="http://v1.yiketianqi.com/api?unescape=1&version=v61&appid=65521391&appsecret=Dv2eKMLL";
    //七天
    urlyiKe ="http://v1.yiketianqi.com/api?unescape=1&version=v9&appid=65521391&appsecret=Dv2eKMLL";


    QUrl urlTianQi(urlyiKe);

    //QNetworkRequest - 指定请求的url地址
    QNetworkRequest res(urlTianQi); //根据网址实例化QUrl对象,再根据这个对象实例化QNetworkRequest对象
    reply = manager->get(res);
    //QNetworkReply网络请求后进行信号读取
    connect(manager,&QNetworkAccessManager::finished,this,&Widget::readHttpReply);



    //初始化七天数据列表
    mWeekList<<ui->labelday1<<ui->labelday2
             <<ui->labelday3<<ui->labelday4
             <<ui->labelday5<<ui->labelday6;

    mDateList<<ui->labelDate1<<ui->labelDate2
             <<ui->labelDate3<<ui->labelDate4
             <<ui->labelDate5<<ui->labelDate6;

   mIconList<<ui->labelWeahterIcon0<<ui->labelWeahterIcon1
            <<ui->labelWeahterIcon2<<ui->labelWeahterIcon3
            <<ui->labelWeahterIcon4<<ui->labelWeahterIcon5;

  mWeatypeList<<ui->labelWeatherTypeDate0<<ui->labelWeatherTypeDate1
              <<ui->labelWeatherTypeDate2<<ui->labelWeatherTypeDate3
              <<ui->labelWeatherTypeDate4<<ui->labelWeatherTypeDate5;

 mAirqList<<ui->labelairq0<<ui->labelairq1
          <<ui->labelairq2<<ui->labelairq3
          <<ui->labelairq4<<ui->labelairq5;

 mFxList<<ui->labelFX0<<ui->labelFX1
        <<ui->labelFX2<<ui->labelFX3
        <<ui->labelFX4<<ui->labelFX5;

 mFlList<<ui->labelFL0<<ui->labelFL1
        <<ui->labelFL2<<ui->labelFL3
        <<ui->labelFL4<<ui->labelFL5;

    // 根据天气插入对应图标
    //根据keys,设置icon的路径

    mTypeMap.insert("暴雪",":/type/BaoXue.png");
    mTypeMap.insert("暴雨",":/type/BaoYu. png");
    mTypeMap.insert("暴雨到大暴雨",":/type/BaoYuDaoDaBaoYu.png");
    mTypeMap.insert("大暴雨",":/type/DaBaoYu.png");
    mTypeMap.insert("大暴雨到特大暴雨",":/type/DaBaoYuDaoTeDaBaoYu.png");
    mTypeMap.insert("大到暴雪",":/type/DaDaoBaoXue.png");
    mTypeMap.insert("大雪",":/type/DaXue.png");
    mTypeMap.insert("大雨",":/type/DaYu.png");
    mTypeMap.insert("冻雨",":/type/DongYu.png");
    mTypeMap.insert("多云",":/type/DuoYun.png");
    mTypeMap.insert("浮沉",":/type/FuChen.png");
    mTypeMap.insert("雷阵雨",":/type/LeiZhenYu.png");
    mTypeMap.insert("雷阵雨伴有冰雹",":/type/LeiZhenYuBanYouBingBao.png");
    mTypeMap.insert("霾",":/type/Mai.png");
    mTypeMap.insert("强沙尘暴",":/type/QiangShaChenBao.png");
    mTypeMap.insert("晴",":/type/Qing.png");
    mTypeMap.insert("沙尘暴",":/type/ShaChenBao.png");
    mTypeMap.insert("特大暴雨",":/type/TeDaBaoYu.png");
    mTypeMap.insert("undefined",":/type/undefined.png");
    mTypeMap.insert("雾",":/type/Wu.png");
    mTypeMap.insert("小到中雪",":/type/XiaoDaoZhongXue.png");
    mTypeMap.insert("小到中雨",":/type/XiaoDaoZhongYu.png");
    mTypeMap.insert("小雪",":/type/XiaoXue.png");
    mTypeMap.insert("小雨",":/type/XiaoYu.png");
    mTypeMap.insert("雪",":/type/Xue.png");
    mTypeMap.insert("扬沙",":/type/YangSha.png");
    mTypeMap.insert("阴",":/type/Yin.png");
    mTypeMap.insert("雨",":/type/Yu.png");
    mTypeMap.insert("雨夹雪",":/type/YuJiaXue.png");
    mTypeMap.insert("阵雪",":/type/ZhenXue.png");
    mTypeMap.insert("阵雨",":/type/ZhenYu.png");
    mTypeMap.insert("中到大雪",":/type/ZhongDaoDaXue.png");
    mTypeMap.insert("中到大雨",":/type/ZhongDaoDaYu.png");
    mTypeMap.insert("中雪",":/type/ZhongXue.png");
    mTypeMap.insert("中雨",":/type/ZhongYu.png");


    //下载事件过滤器
    ui->widget0404->installEventFilter(this);
    ui->widget0405->installEventFilter(this);

}

/*
 QNetworkAccessManager *manager = new QNetworkAccessManager(this);
 connect(manager, &QNetworkAccessManager::finished,
         this, &MyClass::replyFinished);

 manager->get(QNetworkRequest(QUrl("http://qt-project.org")));
*/



Widget::~Widget()
{
    delete ui;
}
/*
 QMenu menu;
 QAction *at = actions[0]; // Assumes actions is not empty
 foreach (QAction *a, actions)
     menu.addAction(a);
 menu.exec(pos, at);
*/

//鼠标按下事件
void Widget::mousePressEvent(QMouseEvent *event)
{
    //重写鼠标右键事件
    if(event->button() == Qt::RightButton){
        qDebug()<<"右键";
        menuQuit->exec(QCursor::pos()); // 弹出位置为鼠标当前的位置
    }


    //重写鼠标左键事件 --  实现拖动窗口
    //鼠标当前位置: event->globalPos()
    //窗口当前位置:this->pos()

    //新窗口位置:
    //1.当鼠标左键被按下的时候记录鼠标/窗口当前值,还有鼠标与窗口的相对值 event->pos() --鼠标相对窗口的坐标
    //2.鼠标左键释放的时候获得鼠标当前值,对应的窗口位置也根据相对距离进行偏移

    if(event->button() == Qt::LeftButton){
        qDebug()<<"左键";
        //pos() 是相对整个widget页面的坐标  globalpos() 才是相对真个电脑窗口的坐标
        //以下两种方式均可得到偏移值
        //mOffset = event->globalPos()-this->pos();
        mOffset = event->pos();
        //qDebug()<<mOffset.x()<<"  "<<mOffset.y();
    }

}

//鼠标移动事件
void Widget::mouseMoveEvent(QMouseEvent *event)
{
    // 将窗口根据鼠标当前位置和偏移值进行移动(这个值是固定的,作为窗口移动的桥梁)
    this->move(event->globalPos()-mOffset);
}

// 事件过滤器进行绘图
bool Widget::eventFilter(QObject *watched, QEvent *event)
{
    if(watched == ui->widget0404  && event->type() == QEvent::Paint){//在这个控件触发绘图事件,就调用api绘图
     drawTempLineHigh();
     return true; //表示事件已经被处理
    }
    if(watched == ui->widget0405  && event->type() == QEvent::Paint){//在这个控件触发绘图事件,就调用api绘图
     drawTempLineLow();
     return true; //表示事件已经被处理
    }
    return QWidget::eventFilter(watched, event);
}


// 解析获取到的json数据
void Widget::parseWeatherJsonData(QByteArray rawData)
{
    QJsonDocument jsonObj = QJsonDocument::fromJson(rawData); // 先把原始数据转为JSON类型

    if(!jsonObj.isNull() && jsonObj.isObject()){ // jsonObj不为空,并且是Obeject类型
        QJsonObject objRoot = jsonObj.object();
        //解析我们获得的json数据, 填入到相关 控件中

        //解析日期
        QString date = objRoot["date"].toString();
        QString week = objRoot["week"].toString();
        ui->labelCurrentData->setText(date + " " + week); // 把读取到的日期和星期加入到右上角的控件  labelCurrentData 中

        //解析城市名称
        QString cityName =objRoot["city"].toString();
        ui->labelCity->setText(cityName+"市");

        //解析当前温度
        QString curTem = objRoot["tem"].toString();
        ui->labelTemp->setText(curTem+"℃"); // 显示当前温度
        QString lowTem = objRoot["tem2"].toString();
        QString highTem = objRoot["tem1"].toString();
        ui->labelTempRange->setText(lowTem + "~" +highTem +"℃"); // 显示温度范围

        //解析天气类型
        ui->labelWeatherType->setText(objRoot["wea"].toString());
        ui->labelWeatherIcon->setPixmap(mTypeMap[objRoot["wea"].toString()]); // 根据键值对匹配对应图片

        //解析感冒指数
        ui->labelGanMao->setText(objRoot["air_tips"].toString());


        //解析风向
        ui->labelFengXiang->setText(objRoot["win"].toString()); //风向
        ui->labelFengXiangData->setText(objRoot["win_speed"].toString());//风力

        //解析PM2.5
        ui->labelPM25Data ->setText(objRoot["air_pm25"].toString());
        //湿度
        ui->labelShiDuData->setText(objRoot["humidity"].toString());
        //空气质量
        ui->labelAriData->setText(objRoot["air_level"].toString());
    }
}

//七天json的数据解析
void Widget::parseWeatherJsonDataNew(QByteArray rawData)
{
    QJsonDocument jsonDoc = QJsonDocument::fromJson(rawData);
    if(!jsonDoc.isNull() && jsonDoc.isObject()){
     QJsonObject jsonRoot = jsonDoc.object();
     //解析城市
     day[0].mCity = jsonRoot["city"].toString();
     day[0].mPm25 = jsonRoot["aqi"].toObject()["pm25"].toString();
     if(jsonRoot.contains("data") && jsonRoot["data"].isArray()){
       QJsonArray weaArray =jsonRoot["data"].toArray();
       for(int i=0;i<weaArray.size();++i){ //输出七天情况
        QJsonObject obj = weaArray[i].toObject();
        qDebug()<<obj["date"].toString()<<obj["wea"].toString();
        // 将解析到这七年每天的数据解析到我们的数组中
        day[i].mDate = obj["date"].toString();
        day[i].mWeek = obj["week"].toString();
        day[i].mWeathType = obj["wea"].toString();
        day[i].mTemp = obj["tem"].toString();
        day[i].mTempLow = obj["tem2"].toString();
        day[i].mTempHigh = obj["tem1"].toString();
        day[i].mFx = obj["win"].toArray()[0].toString();
        day[i].mFl = obj["win_speed"].toString();
        day[i].mAirq = obj["air_level"].toString();
        // 给出穿建议
        //day[i].mTips = obj["air_tips"].toString();
        day[i].mTips = obj["index"].toArray()[3].toObject()["desc"].toString();

        day[i].mHu = obj["humidity"].toString();
       }
     }
    }
// 定义一个函数去给 ui更新数据
    updateUI();

}

// 更新ui界面上的信息
void Widget::updateUI()
{
    //解析我们获得的json数据, 填入到相关 控件中

    QPixmap pixmap;

    //解析日期

    ui->labelCurrentData->setText(day[0].mDate + " " + day[0].mWeek); // 把读取到的日期和星期加入到右上角的控件  labelCurrentData 中

    //解析城市名称

    ui->labelCity->setText(day[0].mCity+"市");

    //解析当前温度
    ui->labelTemp->setText(day[0].mTemp+"℃"); // 显示当前温度
    ui->labelTempRange->setText(day[0].mTempLow + "~" +day[0].mTempHigh +"℃"); // 显示温度范围

    //解析天气类型
    ui->labelWeatherType->setText(day[0].mWeathType);
    ui->labelWeatherIcon->setPixmap(mTypeMap[day[0].mWeathType]); // 根据键值对匹配对应图片

    //解析感冒指数
    ui->labelGanMao->setText(day[0].mTips);


    //解析风向
    ui->labelFengXiang->setText(day[0].mFx); //风向
    ui->labelFengXiangData->setText(day[0].mFl);//风力

    //解析PM2.5
    ui->labelPM25Data ->setText(day[0].mPm25);
    //湿度
    ui->labelShiDuData->setText(day[0].mHu);
    //空气质量
    ui->labelAriData->setText(day[0].mAirq);
    // 更新七个QList的数据
    for(int i=0;i<6;++i){
     mWeekList[i]->setText(day[i].mWeek);


     QStringList   daylist  = day[i].mDate.split('-'); //分割字符串 -
     mDateList[i]->setText(daylist.at(1) + "-" + daylist.at(2)); // 原本是2024-6-24 ,我们只要后两位

     // 缩放图标的大小让他能够匹配上

     int index =day[i].mWeathType.indexOf("转"); //包含'转'的天气
     if(index != -1){//包含'转'
            pixmap  = mTypeMap[day[i].mWeathType.left(index)]; // 拿到'转'左边的天气图片,例:晴转多云,拿到晴
     }
     else{
     pixmap  = mTypeMap[day[i].mWeathType];
     }
     pixmap  = pixmap.scaled(mIconList[i]->size(),Qt::KeepAspectRatio); //缩放图标的大小为图片大小,并用KeepAspectRatio 参数,不让图片被拉伸

     mIconList[i]->setPixmap(pixmap);
     mWeatypeList[i]->setText(day[i].mWeathType);
     // 根据空气质量情况设置对应的颜色
     QString  airQ = day[i].mAirq;
     mAirqList[i]->setText(airQ);
     //设置对应样式表
     if(airQ == "优"){
      mAirqList[i]->setStyleSheet(
              "color: rgb(230, 230, 230); background-color: rgb(130, 213, 32); border-radius: 7px");
     }
     if(airQ == "良"){
      mAirqList[i]->setStyleSheet(
              "color: rgb(230, 230, 230); background-color: rgb(255, 187, 69); border-radius: 7px");
     }
     if(airQ == "轻度污染"){
      mAirqList[i]->setStyleSheet(
              "color: rgb(230, 230, 230);background-color: rgb(239, 121, 24);border-radius: 7px");
     }
     if(airQ == "中度污染"){
      mAirqList[i]->setStyleSheet(
              "color: rgb(230, 230, 230); background-color: rgb(255, 17, 17); border-radius: 7px");
     }
     if(airQ == "重度污染"){
      mAirqList[i]->setStyleSheet(
              "color: rgb(230, 230, 230); background-color: rgb(153, 0, 0); border-radius: 7px");
     }



     mFxList[i]->setText(day[i].mFx);
     // 风力也会涉及"转",我们设置为左边的即可
     index = day[i].mFl.indexOf("转");
     if(index != -1){
     mFlList[i]->setText(day[i].mFl.left(index));
     }
     else{
     mFlList[i]->setText(day[i].mFl);
     }
    }
    mWeekList[0]->setText("今天");
    mWeekList[1]->setText("明天");
    mWeekList[2]->setText("后天");

    update(); // 调用我们的绘图事件
}

// 画高温曲线
void Widget::drawTempLineHigh()
{
    //在widget0404 上绘图
  QPainter painter(ui->widget0404);
  painter.setPen(Qt::yellow);
  painter.setBrush(Qt::yellow);//设置画刷,让圆变成实心
  painter.setRenderHint(QPainter::Antialiasing,true);

  // 计算七天最大温度的平均值
  int ave;
  int sum = 0;
  int offset = 0; // 偏移值
  int middle = ui->widget0404->height()/2;
  for(int i=0;i<6;++i){
  sum += day[i].mTempHigh.toInt();
  }
  ave = sum/6;
  //定义出6个点
  QPoint points[6];
  for(int i=0;i<6;++i){
   points[i].setX(mAirqList[i]->x() + mAirqList[i]->width()/2); // 设置在对应上方控件的中间位置
   offset = (day[i].mTempHigh.toInt() - ave)*4; // 计算偏移值
   points[i].setY(middle-offset); // 记得y轴正向朝下,so我们这里是-
   // 画出6个温度点
   painter.drawEllipse(QPoint(points[i]),3,3);
   // 画出对应温度
   painter.drawText(points[i].x()-15,points[i].y()-15,day[i].mTempHigh+"°");
  }

  //画出5条线将6个点相连接
  for(int i=0;i<5;++i){
      painter.drawLine(points[i],points[i+1]);
  }

}

// 画低温曲线
void Widget::drawTempLineLow()
{
    //在widget0405 上绘图
  QPainter painter(ui->widget0405);
  painter.setPen(QColor(70, 192, 203));
  painter.setBrush(QColor(70, 192, 203));//设置画刷,让圆变成实心
  painter.setRenderHint(QPainter::Antialiasing,true);

  // 计算七天最大温度的平均值
  int ave;
  int sum = 0;
  int offset = 0; // 偏移值
  int middle = ui->widget0405->height()/2;
  for(int i=0;i<6;++i){
  sum += day[i].mTempLow.toInt();
  }
  ave = sum/6;
  //定义出6个点
  QPoint points[6];
  for(int i=0;i<6;++i){
   points[i].setX(mAirqList[i]->x() + mAirqList[i]->width()/2); // 设置在对应上方控件的中间位置
   offset = (day[i].mTempLow.toInt() - ave)*4; // 计算偏移值
   points[i].setY(middle-offset); // 记得y轴正向朝下,so我们这里是-
   // 画出6个温度点
   painter.drawEllipse(QPoint(points[i]),3,3);
   // 画出对应温度
   painter.drawText(points[i].x()-15,points[i].y()-15,day[i].mTempLow+"°");
  }

  //画出5条线将6个点相连接
  for(int i=0;i<5;++i){
      painter.drawLine(points[i],points[i+1]);
  }

}




//http网络请求后进行读取槽函数
void Widget::readHttpReply(QNetworkReply* reply)
{

    int resCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();//404
    qDebug()<<resCode;
    if(reply->error() == QNetworkReply::NoError && resCode == 200){ // 没出错的情况下执行
        //大多数服务器网址返回的编码格式是 utf-8,需要进行格式转化
        QByteArray data = reply->readAll();
        //parseWeatherJsonData(data);
        parseWeatherJsonDataNew(data);
        //qDebug()<<QString::fromUtf8(data); // 将读到utf-8格式的数据转为QString类型进行输出
    }
    else{
        //qDebug()<<"请求失败"<<reply->errorString();
        QMessageBox msg;
        msg.setWindowTitle("错误");
        msg.setText("网络请求失败!");
        msg.setStyleSheet("QPushButton{color:red};");//设置按钮样式
        msg.setStandardButtons(QMessageBox::Ok); //添加Ok按钮
        msg.exec();// 调用QMessageBox对象
    }
}



//根据用户输入城市名字获取数据
void Widget::on_pushButton_clicked()
{
    QString cityNameFromUser = ui->lineEditCity->text();
    QString cityCode = cityCodeUtils.getCityCodeFromName(cityNameFromUser);
    if(cityCode != ""){ // 能匹配到根据城市id 匹配 url
        urlyiKe += "&cityid=" +cityCode;
        qDebug()<<urlyiKe;
        manager->get(QNetworkRequest(QUrl(urlyiKe)));
    }
    else{ // 没找到

        QMessageBox msg;
        msg.setWindowTitle("错误");
        msg.setText("请输入正确的城市名称!");
        msg.setStyleSheet("QPushButton{color:red};");//设置按钮样式
        msg.setStandardButtons(QMessageBox::Ok); //添加Ok按钮
        msg.exec();// 调用QMessageBox对象
    }

}

//按下回车刷新
void Widget::on_lineEditCity_returnPressed()
{
  on_pushButton_clicked(); // 回车被按下,调用搜索槽函数
}

day.h

#ifndef DAY_H
#define DAY_H

#include <QString>



class Day
{
public:
    Day();

QString mDate;
QString mWeek;
QString mCity;
QString mTemp;
QString mWeathType;
QString mTempLow;
QString mTempHigh;

QString mTips;
QString mFx;
QString mFl;
QString mPm25;
QString mHu;
QString mAirq;

};

#endif // DAY_H

day.cpp(无内容)

#include "day.h"

Day::Day()
{

}

citycodeutils.h

#ifndef CITYCODEUTILS_H
#define CITYCODEUTILS_H

#include <QMap>



class CityCodeUtils
{
public:
    CityCodeUtils();

    QMap<QString,QString> cityMap={};
    QString getCityCodeFromName(QString cityName);

    void initCityMap();


};

#endif // CITYCODEUTILS_H

citycodeutils.cpp

#include "citycodeutils.h"

#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>

CityCodeUtils::CityCodeUtils()
{

}

//初始化QMap 并寻找城市名
QString CityCodeUtils::getCityCodeFromName(QString cityName)
{

    if(cityMap.isEmpty()){ // 这个map为空就初始化
    initCityMap(); // 初始化QMap,把json文件内容中的city_name city_code-以键值对的形式添加到QMap对象cityMao中
    }
    //根据城市名寻找对应的QMap对象
    QMap<QString, QString>::iterator it = cityMap.find(cityName);
    if(it == cityMap.end()){
        it=cityMap.find(cityName+"市");
    if(it == cityMap.end())
          it=cityMap.find(cityName+"县");
    if(it == cityMap.end())
          it=cityMap.find(cityName+"区");
      if(it == cityMap.end())
        return ""; // 四种判断都找不到就返回"" --> 会给出对应QMessageBox的报错
    }

   return it.value();
}

// 初始化QMap,把json文件内容中的city_name city_code-以键值对的形式添加到QMap对象cityMao中
void CityCodeUtils::initCityMap()
{
    QFile file(":/citycode.json");
    file.open(QIODevice::ReadOnly);
    QByteArray rawData = file.readAll();
    file.close();

    QJsonDocument jsonDoc = QJsonDocument::fromJson(rawData);

    if(jsonDoc.isArray()){
        QJsonArray citys = jsonDoc.array();
        for(QJsonValue value:citys){
            if(value.isObject()){
                //读取对应键值对
                QString cityName = value["city_name"].toString();
                QString cityCode = value["city_code"].toString();
                //添加到QMap对象中进行初始化
                cityMap.insert(cityName,cityCode);
            }

        }

    }
}

整体ui预览

更详细的ui界面请看第一篇:  Qt项目天气预报(1) - ui界面搭建-CSDN博客

项目总结: 

本次项目包含了qt的很多内容:

光是ui界面搭建就需要花上半天,通过stylelsheet  样式表 的编写 让我们控件更加美观

通过http协议 获得天气API 的json数据

通过json几个常用类(

QJsonDocument  - 表示JSON数据结构,可以包含一个对象(QJsonObject)或数组(QJsonArray)
QJsonObject - 表示JSON对象,即键值对的集合 {}

QJsonArray- 表示JSON数组,即有序的值集合[]

)进行json数据的解析,并把解析到的数据与控件关联起来更新控件数据

同时通过QList去管理多个控件组

通过QPainter 配合事件过滤器去进行温度曲线的绘制 ... 

喜欢的朋友别忘了点赞,收藏,转发,谢谢啦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1879793.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LDM论文解读

论文名称&#xff1a;High-Resolution Image Synthesis with Latent Diffusion Models 发表时间&#xff1a;CVPR2022 作者及组织&#xff1a;Robin Rombach, Andreas Blattmann, Dominik Lorenz,Patrick Esser和 Bjorn Ommer, 来自Ludwig Maximilian University of Munich &a…

AI奏响未来乐章:音乐界的革命性变革

AI在创造还是毁掉音乐 引言 随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;正在逐渐渗透到我们生活的每一个角落&#xff0c;音乐领域也不例外。AI技术的引入&#xff0c;不仅为音乐创作、教育、体验带来了革命性的变革&#xff0c;更为整个音乐产业注入了…

顺序表应用——通讯录

在本篇之前的顺序表专题我们已经学习的顺序表的实现&#xff0c;了解了如何实现顺序表的插入和删除等功能&#xff0c;那么在本篇当中就要学习基于顺序表来实现通讯录&#xff0c;在通讯录当中能实现联系人的增、删、查改等功能&#xff0c;接下来就让我们一起来实现通讯录吧&a…

民用无人机企业招标投标需要资质证书详解

一、基础资质 在民用无人机企业的招标投标过程中&#xff0c;基础资质是首要考虑的因素。这些资质通常包括企业注册资质、税务登记证、组织机构代码证等。 1.1 企业注册资质 企业应具备合法的注册资质&#xff0c;即营业执照。该执照应包含企业名称、注册地址、法定代表人、…

Java [数据结构] Deque与Queue

&#x1f93a;深入理解 Java 中的 Deque 和 Queue&#x1f93a; 在现代软件开发中&#xff0c;数据结构是构建高效、可维护代码的基础。 Java 作为一门广泛应用的编程语言&#xff0c;其丰富的集合框架&#xff08;Collections Framework&#xff09;为开发者提供了多种强大的…

Zabbix 排坑版 Centos7

systemctl stop firewalld;systemctl disable firewalld;setenforce 0sed -i s/SELINUXenforcing/SELINUXdisabled/ /etc/selinux/configzabbix源地址,可以自己选版本&#xff0c;安装都大差不差 rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5…

【车载开发系列】S32 Design Studio工具安装步骤

【车载开发系列】S32 Design Studio工具安装步骤 S32 Design Studio工具安装步骤 【车载开发系列】S32 Design Studio工具安装步骤※关键字提炼※一. 准备工作二. 下载安装包三. 获取License许可四. 开始预安装五. 开始正式安装六. 启动软件七. 安装插件八. 卸载插件九. 确认安…

【操作系统】进程管理——进程控制和进程通信(个人笔记)

学习日期&#xff1a;2024.6.30 内容摘要&#xff1a;进程控制的概念&#xff0c;进程控制相关的“原语”&#xff0c;进程通信 进程控制 原语 进程控制用“原语”实现。原语是一种特殊的程序&#xff0c;它的执行具有原子性&#xff0c;也就是说&#xff0c;这段程序的执行…

vs code python开发笔记

目录 选择python 解析器 安装插件 不全&#xff1a; 调试启动目录问题&#xff1a; 2.选择python解释器 选择python 解析器 ctrl shift P select interpreter 安装插件 不全&#xff1a; remote ssh python debuger 左下角&#xff0c;点击左右左右箭头&#xff0c;远程…

后端之路第三站(Mybatis)——JDBC跟Mybatis、lombok

一、什么是JDBC JDBC就是sun公司研发的一套通过java来操控数据库的工具&#xff0c;对应不同的数据库系统有不同的JDBC&#xff0c;而他们统称【驱动】&#xff0c;这就是上一篇我们提到创建Mybatis项目时要引入的依赖、以及连接数据库四要素里的第一要素。 JDBC有自己一套原始…

Redis 7.x 系列【8】数据类型之哈希(Hash)

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Redis 版本 7.2.5 源码地址&#xff1a;https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 概述2. 常用命令2.1 HSET2.2 HGET2.3 HDEL2.4 HEXISTS2.5 HGETALL2.6 HKEYS2.7 HLE…

grpc学习golang版( 五、多proto文件示例 )

系列文章目录 第一章 grpc基本概念与安装 第二章 grpc入门示例 第三章 proto文件数据类型 第四章 多服务示例 第五章 多proto文件示例 第六章 服务器流式传输 第七章 客户端流式传输 第八章 双向流示例 文章目录 一、前言二、定义proto文件2.1 公共proto文件2.2 语音唤醒proto文…

探索MySQL核心技术:理解索引和主键的关系

在数据密集型应用中&#xff0c;数据库的性能往往是决定一个应用成败的重要因素之一。其中&#xff0c;MySQL作为一种开源关系型数据库管理系统&#xff0c;以其卓越的性能和丰富的功能被广泛应用。而在MySQL数据库优化的众多技巧中&#xff0c;索引和主键扮演着极其重要的角色…

5、Python之rich:GUI之外,终端呈现也能玩出花

引言 在Python系列文章的上一篇中&#xff0c;我们从print的定义出发&#xff0c;进一步探索了print()函数更多的用法&#xff0c;尤其是一些哪怕是Python老手也可能忽略的用法。没有阅读的或者需要回顾print()及输出格式化的扩展用法&#xff0c;可以查看上一篇文章。 虽然pr…

2024/6/30周报

文章目录 摘要ABSTRACT文献阅读题目问题本文贡献方法LSTMTCN模型总体架构 实验实验结果 深度学习TCN-LSTM代码运行结果 总结 摘要 本周阅读了一篇关于TCN和LSTM进行光伏功率预测的文章&#xff0c;本文提出了一种利用LSTM-TCN预测光伏功率的新模型。它由长短期记忆和时间卷积网…

可编程定时计数器8253/8254 - 8253控制字

8253控制字 概述 图7-45中左下角的是控制字寄存器&#xff0c;其操作端口是0x43,它是8位大小的寄存器 控制字寄存器也称为模式控制器&#xff0c;在控制字寄存器中保存的内容称为控制字&#xff0c;控制字用来设置所指定的计数器(通道)的工作方式、读写格式及数制&#xff0c…

emptyDir + initContainer实现ConfigMap的动态更新(K8s相关)

1. 絮絮叨叨 K8s部署服务时&#xff0c;一般都需要使用ConfigMap定义一些配置文件例如&#xff0c;部署分布式SQL引擎Presto&#xff0c;会在ConfigMap中定义coordinator、worker所需的配置文件以node.properties为例&#xff0c;node.environment和node.data-dir的值将由Helm…

48 - 按日期分组销售产品(高频 SQL 50 题基础版)

48 - 按日期分组销售产品 -- group_concat 分组拼接selectsell_date,count(distinct product) num_sold,group_concat(distinct product order by product separator ,) products fromActivities group bysell_date;

监控电脑的软件有哪些?精选8大监控电脑的软件

根据当前市场反馈和功能评价&#xff0c;以下是八款备受推崇的电脑监控软件推荐&#xff0c;适合不同企业和组织的监控与管理需求&#xff1a; 1.安企神监控软件 特点&#xff1a;全面的局域网监控工具&#xff0c;擅长网络设备监控、网络性能管理和故障诊断。提供员工电脑屏幕…

C++操作系列(二):VSCode安装和配置C++开发环境

1. VSCode下载 进入VSCode的官网网页&#xff1a;Download Visual Studio Code - Mac, Linux, Windows 下载相应的版本&#xff1a; 2. 安装VSCode 安装到指定位置&#xff1a; 一路下一步&#xff0c;直至安装完成&#xff1a; 3. 安装C插件 3.1. 安装C/C 点击扩展图标&…