窗口设置
天气预报的界面一般是没有退出窗口的,所以要设置窗口无边距和固定窗口大小,如下:
setWindowFlag(Qt::FramelessWindowHint); //设置窗口无边距
setFixedSize(width(),height()); //设置窗口固定大小
右键退出
由于窗口没有退出按钮,所以要通过右键弹出菜单来退出程序。
首先要创建菜单和动作,如下:
menu = new QMenu(this); //构建菜单
action = new QAction("退出"); //构建动作
menu->addAction(action); //给菜单添加动作
要右键弹出菜单,就要重写void contextMenuEvent(QContextMenuEvent* event)函数
void MainWindow::contextMenuEvent(QContextMenuEvent *event)
{
menu->exec(QCursor::pos()); //使菜单在鼠标右击位置弹出
event->accept(); //使event不再传递
}
触发动作槽函数
connect(action,&QAction::triggered,this,[=]{
qApp->exit(); //退出程序
});
拖动窗口
因为要用到鼠标左键拖拽窗口,所以要重写三个鼠标事件函数
void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void MainWindow::mousePressEvent(QMouseEvent *event)
{
//获取鼠标当前位置与窗口左上角的差值
if(event->button() == Qt::LeftButton){
_isDrag = true;
_point = event->globalPosition().toPoint() - this->pos();
}
}
void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
if(_isDrag){
//移动窗口
move(event->globalPosition().toPoint() - _point);
}
}
void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton){
_isDrag = false;
}
}
需要的类
程序中需要的类有3个,分别是:
存放天气数据的类
//存放今天天气数据类
class Today
{
public:
Today()
{
date = "2023-2-27";
city = "广州";
ganmao = "感冒指数";
wendu = 0;
shidu = "0%";
pm25 = 0;
quality = "无数据";
type = "多云";
fl = "2级";
fx = "南风";
high = 30;
low = 18;
}
QString date;
QString city;
QString ganmao;
QString wendu;
QString shidu;
int pm25;
QString quality;
QString type;
QString fx;
QString fl;
int high;
int low;
};
//存放五天天气数据类
class Date
{
public:
Date()
{
date = "2023-2-27";
week = "周一";
type = "多云";
high = 0;
low = 0;
fx = "南风";
fl = "2级";
aqi = 0;
}
QString date;
QString week;
QString type;
int high;
int low;
QString fx;
QString fl;
int aqi;
};
解析城市编码的类
//城市编码类,因为每个城市都有唯一且固定的编码,所以采用单例设计模式中的饿汉模式
class WeatherTool
{
public:
//禁用该类的拷贝构造函数和拷贝赋值操作符重载函数
WeatherTool(const WeatherTool& obj) = delete;
WeatherTool& operator = (const WeatherTool& obj) = delete;
//返回该单例对象
static WeatherTool* getInstance()
{
return _weathertool;
}
//根据城市获取对应编码
static QString getCityCode(QString cityname);
private:
WeatherTool() = default; //设为默认构造函数
static QMap<QString,QString> _map; //存放城市及对应编码
static WeatherTool* _weathertool; //单例对象
//初始化map
static void initCityMap();
};
将json文件中的城市及对应编码解析出来后,存放到一个map中,map键值对是城市名称及其对应编码
//根据城市获取对应编码
QString WeatherTool::getCityCode(QString cityname)
{
if(_map.isEmpty()) initCityMap(); //初始化map
QMap<QString,QString>::iterator it = _map.find(cityname);
if(it == _map.end()){
//搜索城市后戴市字
it = _map.find(cityname + "市");
}
if(it == _map.end()){
//搜索城市后戴市字
it = _map.find(cityname + "县");
}
//找到了城市对应编码
if(it != _map.end()){
return it.value();
}
return "";
}
//初始化map
void WeatherTool::initCityMap()
{
QString filename = QCoreApplication::applicationDirPath();//获取当前程序允许的路径
filename += "/city.json"; //json文件路径
QFile file(filename); //创建文件
file.open(QIODevice::ReadOnly | QIODevice::Text); //以文本形式读取
QByteArray json = file.readAll(); //获取所有数据
file.close(); //关闭文件
QJsonParseError err; //存放错误信息
QJsonDocument doc = QJsonDocument::fromJson(json,&err);
QJsonArray citys = doc.array(); //转换为数组类型
//获取json中的城市及编码
for(int i = 0;i < citys.size();++i){
QString city_name = citys[i].toObject().value("city_name").toString();
QString city_code = citys[i].toObject().value("city_code").toString();
if(city_code.size() > 0){
//将城市及编码放入map
_map.insert(city_name,city_code);
}
}
}
解析天气数据的类
//解析天气JSON的类
class WeatherJson : public QObject
{
Q_OBJECT
public:
WeatherJson();
//发送http请求获取天气数据
void getCityInfo(QString cityname);
//解析JSON数据
void parseJson(QByteArray& array);
QNetworkAccessManager* getNetManeger();
Today getToday();
Date* getDate();
public slots:
//发送请求结束后的槽函数
void onReply(QNetworkReply* reply);
signals:
void changeUI();
private:
//HTTP
QNetworkAccessManager* networkaccessmaneger;
//天气数据类
Today today;
Date date[5];
};
首先要发送http请求,然后解析json文件
//发送http请求获取天气数据
void WeatherJson::getCityInfo(QString cityname)
{
QString citycode = WeatherTool::getInstance()->getCityCode(cityname);//获取城市对应编码
if(citycode.isEmpty()){
QMessageBox::warning(0,"天气","请检查输入是否正确!",QMessageBox::Ok);
return;
}
QUrl url("http://t.weather.sojson.com/api/weather/city/" + citycode);
networkaccessmaneger->get(QNetworkRequest(url));
}
//解析JSON数据
void WeatherJson::parseJson(QByteArray &array)
{
//存放错误信息
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson(array,&err);
if(err.error != QJsonParseError::NoError) return;
QJsonObject rootobj = doc.object();
//解析日期和城市
today.date = rootobj.value("date").toString();
today.city = rootobj.value("cityInfo").toObject().value("city").toString();
//解析yesterday
QJsonObject objData = rootobj.value("data").toObject();
QJsonObject objyester = objData.value("yesterday").toObject();
date[0].date = objyester.value("ymd").toString();
date[0].week = objyester.value("week").toString();
date[0].type = objyester.value("type").toString();
//最高温
QString s = objyester.value("high").toString().split(" ").at(1);
date[0].high = s.left(s.length()-1).toInt();
//最低温
s = objyester.value("low").toString().split(" ").at(1);
date[0].low = s.left(s.length()-1).toInt();
//风力及等级
date[0].fx = objyester.value("fx").toString();
date[0].fl = objyester.value("fl").toString();
//污染指数
date[0].aqi = objyester.value("aqi").toInt();
//解析forcast中5天的数据
QJsonArray forecastArr = objData.value("forecast").toArray();
for(int i = 0;i < 4;i++){
QJsonObject objForecast = forecastArr[i].toObject();
date[i+1].date = objForecast.value("ymd").toString();
date[i+1].week = objForecast.value("week").toString();
date[i+1].type = objForecast.value("type").toString();
//最高温
QString s = objForecast.value("high").toString().split(" ").at(1);
date[i+1].high = s.left(s.length()-1).toInt();
//最低温
s = objForecast.value("low").toString().split(" ").at(1);
date[i+1].low = s.left(s.length()-1).toInt();
//风力及等级
date[i+1].fx = objForecast.value("fx").toString();
date[i+1].fl = objForecast.value("fl").toString();
//污染指数
date[i+1].aqi = objForecast.value("aqi").toInt();
}
//解析今天的数据
today.ganmao = objData.value("ganmao").toString();
today.wendu = objData.value("wendu").toString();
today.shidu = objData.value("shidu").toString();
today.pm25 = objData.value("pm25").toInt();
today.quality = objData.value("quality").toString();
today.type = date[1].type;
today.fx = date[1].fx;
today.fl = date[1].fl;
today.high = date[1].high;
today.low = date[1].low;
}
QNetworkAccessManager* WeatherJson::getNetManeger()
{
return networkaccessmaneger;
}
Today WeatherJson::getToday()
{
return today;
}
Date* WeatherJson::getDate()
{
return date;
}
mainwindow.cpp
发送http请求结束后,会触发槽函数
//发送请求结束后的槽函数
void WeatherJson::onReply(QNetworkReply *reply)
{
int status_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
//如果请求失败
if(reply->error() != QNetworkReply::NoError || status_code != 200){
//输出错误信息
qDebug() << reply->errorString().toLatin1().data();
QMessageBox::warning(0,"天气","请求数据失败!",QMessageBox::Ok);
}else{
QByteArray array = reply->readAll(); //读取数据
parseJson(array);
emit changeUI();
}
reply->deleteLater(); //析构
}
在该槽函数中发生信号changeUI(),主函数中接收到后会更新UI界面显示天气数据
绘制温度曲线
绘制最高最低温曲线用到了事件过滤器
//给绘图控件添加事件过滤器
ui->highLabel->installEventFilter(this);
ui->lowLabel->installEventFilter(this);
//绘制温度曲线
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
//最高温
if(watched == ui->highLabel && event->type() == QEvent::Paint){
paintHigh();
}
//最低温
if(watched == ui->lowLabel && event->type() == QEvent::Paint){
paintLow();
}
return QWidget::eventFilter(watched,event);
}
void MainWindow::paintHigh()
{
QPainter painter(ui->highLabel);
//抗锯齿
painter.setRenderHint(QPainter::Antialiasing,true);
//获取x坐标
int pointx[5] = {0};
for(int i = 0;i < 5;i++){
pointx[i] = weeklist[i]->pos().x() + weeklist[i]->width()/2;
}
//获取y坐标
int value = 0;
for(int i = 0;i < 5;i++){
value += date[i].high;
}
value = value/5; //最高温平均值
int pointy[5] = {0};
for(int i = 0;i < 5;i++){
pointy[i] = ui->highLabel->height()/2 - ((date[i].high - value) * 3);
}
QPen pen = painter.pen();
pen.setWidth(1);
pen.setColor(QColor(255,170,0));
painter.setPen(pen);
painter.setBrush(QColor(255,170,0));
//开始绘制
for(int i = 0;i < 5;i++){
//显示点
painter.drawEllipse(QPoint(pointx[i],pointy[i]),3,3);
//显示温度
painter.drawText(pointx[i]-10,pointy[i]-10,QString::number(date[i].high) + "°C");
}
//绘制曲线
for(int i = 0;i < 4;i++){
if(i == 0){
pen.setStyle(Qt::DotLine); //虚线
painter.setPen(pen);
}else{
pen.setStyle(Qt::SolidLine); //实线
painter.setPen(pen);
}
painter.drawLine(pointx[i],pointy[i],pointx[i+1],pointy[i+1]);
}
}