资源描述
嵌入式图形界面设计报告
班级:计算机科学与技术15-2班
小组成员:刘航征、安宝、曲晗羽
杨祎涵、张振、李雪辰
2017年12月
一、前言
天气预报(测)或气象预报(测)是使用现代科学技术对未来某一地点地球大气层的状态进行预测。从史前人类就已经开始对天气进行预测来相应地安排其工作与生活(比如农业生产、军事行动等等)。今天的天气预报主要是使用收集大量的数据(气温、湿度、风向和风速、气压等等),然后使用目前对大气过程的认识(气象学)来确定未来空气变化。由于大气过程的混乱以及今天科学并没有最终透彻地了解大气过程,因此天气预报总是有一定误差的。
最传统的数据是在地面或海面上通过专业人员、爱好者、自动气象站或者浮标收集的气压、气温、风速、风向、湿度等数据。世界气象组织协调这些数据采集的时间,并制定标准。这些测量分每小时一次(METAR)或者每六小时一次(SYNOP)。
该项目为一款天气预报软件,基于Qt5开发,具有查询指定城市天气、显示当天天气状况以及未来四天天气大致状况的主要功能,次要功能为更换软件皮肤,显示当天感冒指数等功能。
软件预览图:
日期显示栏
搜索栏
湿度、风向栏
实时气温栏
日出、日落时间显示栏
感冒指数信息栏
换肤按钮、最小化、关闭按钮
今日以及未来四日天气
二、选用的技术基本说明
1、Qt JSON操作
QJsonDocument
QJsonDocument 类用于读和写 JSON 文档。
一个 JSON 文档可以使用 QJsonDocument::fromJson() 从基于文本的表示转化为 QJsonDocument, toJson() 则可以反向转化为文本。解析器非常快且高效,并将 JSON 转换为 Qt 使用的二进制表示。
已解析文档的有效性,可以使用 !isNull() 进行查询。
如果要查询一个 JSON 文档是否包含一个数组或一个对象,使用 isArray() 和 isObject()。包含在文档中的数组或对象可以使用 array() 或 object() 检索,然后读取或操作。
也可以使用 fromBinaryData() 或 fromRawData() 从存储的二进制表示创建来 JSON 文档。
QJsonArray
QJsonArray 类封装了一个 JSON 数组。
JSON 数组是值的列表。列表可以被操作,通过从数组中插入和删除 QJsonValue 。
一个 QJsonArray 可以和一个 QVariantList 相互转换。可以使用 size() 来查询条目的数量,通过 insert() 在指定索引处插入值,removeAt() 来删除指定索引的值。
QJsonObject
QJsonObject 类封装了一个 JSON 对象。
一个 JSON 对象是一个“key/value 对”列表,key 是独一无二的字符串,value 由一个 QJsonValue 表示。
一个 QJsonObject 可以和一个 QVariantMap 相互转换。可以使用 size() 来查询“key/value 对”的数量,通过 insert() 插入“key/value 对”, remove() 删除指定的 key。
QJsonValue
QJsonValue 类封装了一个值。
JSON 中的值有 6 种基本数据类型:
· bool(QJsonValue::Bool)
· double(QJsonValue::Double)
· string(QJsonValue::String)
· array(QJsonValue::Array)
· object(QJsonValue::Object)
· null(QJsonValue::Null)
一个值可以由任何上述数据类型表示。此外,QJsonValue 有一个特殊的标记来表示未定义的值,可以使用 isUndefined() 查询。
值的类型可以通过 type() 或 isBool()、isString() 等访问函数查询。同样地,值可以通过 toBool()、toString() 等函数转化成相应的存储类型。
QJsonParseError
QJsonParseError 类用于在 JSON 解析中报告错误。
枚举 QJsonParseError::ParseError:
该枚举描述 JSON 文档在解析过程中所发生的错误类型。
2、Qt XML 操作
QXmlStreamReader
用于读取格式良好的XML文档的快速解析器,该类最快且最易于使用,并提供了与其他Qt兼容的应用程序编程接口,很适用于编写单通道解析器;
3、QNetworkAccessManager
QNetworkAccessManager类允许应用程序发送网络请求和接收网络应答。
三、方案设计
1、初始化UI界面
2、获取当前地区时间
3、从文件中读取每个城市名称以及其代码,将其组合分别存入QMap类对象citykeys中
4、用预设的url发送http请求,获取本地城市名称
5,、用获取的本地城市名称获取citykeys中对应的城市代码发出查询未来五天天气的http请求
6、收到的回复为JSON文档,用QJsonDocument类解析JSON文档,将得到的未来五天的天气信息分别存入Forecast结构体数组forecast的每一个元素中,将这些信息显示在主窗口中对应的控件上,然后发出查询今天天气的http请求
7、收到的回复为XML格式文档,用QXmlStreamReader类读取其信息,将当天的天气信息存入Today结构体today中,将这些信息显示在主窗口中对应的控件上。
8、设置搜索按钮以及换肤按钮的信号与槽的关联。
流程图
程序启动
初始化UI界面
获取当前本地时间
读取城市及其代码
Y
获取本地城市名称
点击搜索城市天气
点击换肤按钮
查询未来五天天气信息
重绘UI界面
查询当天天气信息
点击关闭按钮
Y
退出程序
四、详细代码解析
ui设计界面:
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include<QNetworkAccessManager>
#include<QNetworkReply>
#include<QNetworkRequest>
#include<QPixmap>
#include<QPaintEvent>
#include<QPainter>
#include<QPoint>
#include<QMouseEvent>
#include<QLabel>
namespace Ui {
class MainWindow;
}
struct Forecast
{
QString fengxiang;
QString fengli;
QString high;
QString type;
QString low;
QString date;
};
struct Today
{
QString ganmao;
QString city;
QString updatetime;
QString wendu;
QString fengli;
QString fengxiang;
QString sunrise;
QString sunset;
QString shidu;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
void paintEvent(QPaintEvent *);
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *);
protected slots:
void replayFinished(QNetworkReply *reply);
private slots:
void on_getButton_clicked();
void on_updateButton_clicked();
private:
Ui::MainWindow *ui;
QNetworkAccessManager *manager;
QString URL_1;
QString URL_2;
Forecast forecast[5];
Today today;
void parseJson(QString Json);
void parseXml(QString Xml);
void parseCity(QString City);
void loadCitykeys();//加载城市代码文件
QMap<QString,QString> citykeys;//存储城市代码
int choose;//0.查询IP 1.今天天气+指数 2.未来5天天气
QPixmap pixmap;
QPoint move_point;
bool mouse_press;
QList<QLabel *> forecast_date_list;
QList<QLabel *> forecast_temp_list;
QList<QLabel *> forecast_type_list;
QString city;
int uid;
};
#endif // MAINWINDOW_H
mainwindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QVariant>
#include<QByteArray>
#include<QJsonParseError>
#include<QJsonArray>
#include<QJsonObject>
#include<QJsonDocument>
#include<QJsonValue>
#include<QXmlStreamReader>
#include<QMessageBox>
//#include<QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setWindowFlags(Qt::FramelessWindowHint);
setAttribute(Qt::WA_TranslucentBackground);
pixmap.load(":/images/UI0");
resize(pixmap.size());
uid = 0;
mouse_press = false;
forecast_date_list<<ui->forecast_0_date<<ui->forecast_1_date<<ui->forecast_2_date<<ui->forecast_3_date<<ui->forecast_4_date;
forecast_temp_list<<ui->forecast_0_temp<<ui->forecast_1_temp<<ui->forecast_2_temp<<ui->forecast_3_temp<<ui->forecast_4_temp;
forecast_type_list<<ui->forecast_0_type<<ui->forecast_1_type<<ui->forecast_2_type<<ui->forecast_3_type<<ui->forecast_4_type;
manager = new QNetworkAccessManager(this);
//天气API
URL_1 = "";
URL_2 = "";
connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(replayFinished(QNetworkReply*)));
//设置组件样式
ui->cityEdit->setTextMargins(0, 0, ui->getButton->width(), 0);
ui->cityEdit->setStyleSheet("QLineEdit{border: 1px solid gray;border-radius: 3px;background:rgb(200, 231, 232);} QLineEdit:hover{border-color:transparent; }");
ui->getButton ->setStyleSheet("background:transparent;");
ui->closeButton->setStyleSheet("QPushButton {border-image:url(:/images/close);}QPushButton:hover{border-image:url(:/images/close_on);}QPushButton:hover:pressed{border-image:url(:/images/close);}");
ui->minButton->setStyleSheet("QPushButton {border-image:url(:/images/min);}QPushButton:hover{border-image:url(:/images/min_on);}QPushButton:hover:pressed{border-image:url(:/images/min);}");
ui->updateButton->setStyleSheet("QPushButton {border-image:url(:/images/update);}QPushButton:hover{border-image:url(:/images/update_on);}QPushButton:hover:pressed{border-image:url(:/images/update);}");
QDateTime time = QDateTime::currentDateTime();
ui->date->setText(tr("%1").arg(time.toString("yyyy-MM-dd")));
//加载城市代码
loadCitykeys();
//启动程序后先查询ip定位默认城市
choose = 0;
manager->get(QNetworkRequest(QUrl("")));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.drawPixmap(0, 0, pixmap);//绘制UI
}
void MainWindow::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
//鼠标相对于窗体的位置)
move_point = event->pos();
mouse_press = true;
}
}
void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
if(mouse_press)
{
//鼠标相对于屏幕的位置
QPoint move_pos = event->globalPos();
//移动主窗体
this->move(move_pos - move_point);
}
}
void MainWindow::mouseReleaseEvent(QMouseEvent *)
{
mouse_press = false;
}
void MainWindow::loadCitykeys() {
QFile file(":/citykeys/citykeys");
if ( !file.open(QIODevice::ReadOnly | QIODevice::Text) )
return ;
QString tmp;
while (file.atEnd() == 0) {
tmp = file.readAll();
}
QStringList list = tmp.split(",");
for(int i = 0; i < list.length(); i++) {
QString s = list.at(i);
QString citys = s.split(":").at(1);
QString city = citys.replace("\"", "");
QString codes = s.split(":").at(0);
QString code = codes.replace("\"", "");
citykeys.insert(city, code);
}
}
void MainWindow::replayFinished(QNetworkReply *reply)
{
QVariant status_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
if(reply->error() == QNetworkReply::NoError)
{
QByteArray bytes = reply->readAll();
if(choose == 0)//查询IP
{
QString result = QString::fromLocal8Bit(bytes);
parseCity(result);
}
else if(choose == 1)//今天天气+指数
{
QString result(bytes);
parseXml(result);
}
else if(choose == 2)//未来5天天气
{
QString result(bytes);
parseJson(result);
}
}
else
QMessageBox::information(this,tr("出错啦"),tr("网络错误,请检查网络连接"),QMessageBox::Ok,QMessageBox::Ok);
}
//解析城市信息
void MainWindow::parseCity(QString City)
{
city = City.split(tr(" ")).at(5);
if(city == "" || citykeys[city]=="")
{
QMessageBox::information(this,tr("提示"),tr("无法定位城市,请手动查询"),QMessageBox::Ok,QMessageBox::Ok);
return;
}
QString url = URL_1 + citykeys[city];
choose = 2;
manager->get(QNetworkRequest(QUrl(url)));
}
//解析json格式的未来天气
void MainWindow::parseJson(QString Json)
{
QByteArray byte_array;
QJsonParseError json_error;
QJsonDocument parse_doucment = QJsonDocument::fromJson(byte_array.append(Json),&json_error);
if(json_error.error == QJsonParseError::NoError)
{
QJsonObject obj = parse_doucment.object();
QJsonValue desc = obj.take("desc");
if(desc.toString() != "OK")
{
QMessageBox::information(this,tr("抱歉"),tr("暂无此城市的天气情况"),QMessageBox::Ok,QMessageBox::Ok);
return;
}
QJsonValue data = obj.take("data");
today.ganmao = data.toObject().take("ganmao").toString();
QJsonValue forecast1 = data.toObject().take("forecast");
QJsonArray forecast2 = forecast1.toArray();
for(int i=0; i<5; i++)
{
QJsonValue value = forecast2.at(i);
QJsonObject object = value.toObject();
forecast[i].fengxiang = object.take("fengxiang").toString();
forecast[i].date = object.take("date").toString();
forecast[i].fengli = object.take("fengli").toString();
forecast[i].high = object.take("high").toString();
forecast[i].low = object.take("low").toString();
forecast[i].type = object.take("type").toString();
}
for(int i=0;i<5;i++)
{
forecast_date_list[i]->setText(tr("%1").arg(forecast[i].date));
forecast_temp_list[i]->setText(tr("%1 - %2").arg(forecast[i].low.split(" ").at(1)).arg(forecast[i].high.split(" ").at(1)));
forecast_type_list[i]->setPixmap(QPixmap(tr(":/images/%1").arg(forecast[i].type)));
forecast_type_list[i]->setToolTip(tr("%1 : %2 - %3").arg(forecast[i].type).arg(forecast[i].fengli).arg(forecast[i].fengxiang));
}
ui->forecast_0_date->setText(tr("今天"));
QString url = URL_2 + citykeys[city];
choose = 1;
manager->get(QNetworkRequest(QUrl(url)));
}
else
{
QMessageBox::information(this,tr("出错啦"),tr("数据出错,请重试 "),QMessageBox::Ok,QMessageBox::Ok);
return;
}
}
//解析XML格式的今天天气
void MainWindow::parseXml(QString Xml)
{
QXmlStreamReader xml(Xml);
while(!xml.atEnd())
{
if(xml.hasError())
{
QMessageBox::information(this,tr("出错啦"),tr("数据出错,请重试"),QMessageBox::Ok,QMessageBox::Ok);
return;
}
else if(xml.isStartElement())
{
if(xml.name()=="city")
{
today.city = xml.readElementText();
}
else if(xml.name()=="updatetime")
{
today.updatetime = xml.readElementText();
}
else if(xml.name()=="wendu")
{
today.wendu = xml.readElementText();
}
else if(xml.name()=="fengli")
{
today.fengli = xml.readElementText();
}
else if(xml.name()=="shidu")
{
today.shidu = xml.readElementText();
}
else if(xml.name()=="fengxiang")
{
today.fengxiang = xml.readElementText();
}
else if(xml.name()=="sunrise_1")
{
today.sunrise = xml.readElementText();
}
else if(xml.name()=="sunset_1")
{
today.sunset = xml.readElementText();
xml.clear();
ui->city->setText(tr("%1").arg(today.city));
ui->temp->setText(tr("%1℃").arg(today.wendu));
ui->sunrise->setText(tr("%1").arg(today.sunrise));
ui->sunset->setText(tr("%1").arg(today.sunset));
ui->label->setText(tr("日出"));
ui->label_2->setText(tr("日落"));
ui->label_3->setText(tr("湿度"));
ui->shidu->setText(tr("%1").arg(today.shidu));
ui->fengli->setText(tr("%1").arg(today.fengli));
ui->fengxiang->setText(tr("%1").arg(today.fengxiang));
ui->label_4->setText(tr("感\n冒\n指\n数"));
ui->ganmao->setText(tr("%1").arg(today.ganmao));
return;
}
else
xml.readNext();
}
else
xml.readNext();
}
xml.clear();
}
//搜索框查询天气
void MainWindow::on_getButton_clicked()
{
if(ui->cityEdit->text().isEmpty())
return;
city = ui->cityEdit->text();
if(citykeys[city] == "") {
QMessageBox::information(this,tr("抱歉"),tr("暂无此城市的天气情况"),QMessageBox::Ok,QMessageBox::Ok);
return;
}
QString url = URL_1 + citykeys[city];
choose = 2;
manager->get(QNetworkRequest(QUrl(url)));
}
//简单换肤
void MainWindow::on_updateButton_clicked()
{
//选择UI的id
if(uid == 3)
uid = 0;
else
uid = uid + 1;
//拼凑成UI路径
QString UIpath = tr(":/images/UI%1").arg(uid);
//qDebug()<<UIpath;
//加载UI
pixmap.load(UIpath);
//产生paintEvent重绘UI
update();
}
main.cpp:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
五、测试数据及分析
打开程序:
换肤:
搜索指定城市天气信息:
六、结论:
本次我们小组对设计实验所涉及到知识进行讨论,共同研究,查证资料,掌握将要用到的Qt知识并且对我们小组的实验进行主要设计,对小组实验进行分工,本次设计实验结束后我们脑海里已经对QT的知识面得到了延伸!
七、参考文献
[Qt之JSON生成与解析]
展开阅读全文