1.界面设计
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/98bc7be391964fc8b22a2a8ea03362ef.png)
2.开发
2.1 重写鼠标右键退出功能
重写鼠标右键点击事件 + QMenu:
void Widget::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::RightButton){
menuQuit->exec(QCursor::pos());
}
QWidget::mousePressEvent(event);
}
void Widget::rightMouseClicked()
{
menuQuit = new QMenu(this);
QAction *closeAct = new QAction(QIcon(":/res/close.png"), tr("退出"), this);
menuQuit->addAction(closeAct);
connect(menuQuit,&QMenu::triggered,this,[=](){
this->close();
});
}
2.2 重写鼠标左键移动窗口
如下图所示:
鼠标右键点击窗口有鼠标的点击坐标:event->globalPos();
窗口有窗口的坐标:this->Pos();
在鼠标点击窗口移动的时候,鼠标的坐标A与窗口坐标B相对的偏移量moffset不会变
QPoint moffset = event->globalPos() - this->Pos();
窗口移动到图中黑框的位置时,鼠标的坐标处在一个新的A点上,
但是A点相较于新窗口点C的偏移量与原来的A点相较于B点的偏移量没有改变
因此,在已知新坐标点A与固定偏移量的情况下C点可求
新窗口的坐标点位为:event->globalPos() - moffset
然后再对鼠标移动事件进行重写
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/e14f0b49043e43959a82d5abc801698a.png)
void Widget::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::RightButton){
menuQuit->exec(QCursor::pos());
}
if(event->button() == Qt::LeftButton){
qDebug() << event->globalPos()<< this->pos();
moffset = event->globalPos() - this->pos();
}
QWidget::mousePressEvent(event);
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
this->move(event->globalPos()-moffset);
QWidget::mouseMoveEvent(event);
}
2.3 QtHttp编程获取天气原始数据
2.3.1 发送HTTP请求
类:
QNetworkAccessManager
QNetworkRequest
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
QNetworkRequest res(QUrl("http://t.weather.itboy.net/api/weather/city/101010100"));
2.3.2 读取数据
QNetworkReply *reply = manager->get(res);
connect(reply,SIGNAL(finished()),this,SLOT(readHttpReply()));
void Widget::readHttpReply()
{
QByteArray data = reply->readAll();
qDebug() << QString::fromUtf8(data);
}
2.3.3 处理网络失败请求
http请求返回200请求成功
void Widget::readHttpReply()
{
int resCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug() <<resCode;
if(reply->error() == QNetworkReply::NoError && resCode == 200){
QByteArray data = reply->readAll();
qDebug() << QString::fromUtf8(data);
}else{
QMessageBox msgBox;
msgBox.setWindowTitle("错误");
msgBox.setText("网络请求失败");
msgBox.setStandardButtons(QMessageBox::Ok);
qDebug() << reply->errorString();
msgBox.exec();
}
}
2.4 JSON数据
2.4.1 QT生成JSON数据
键值对-- 对象[键] = "值";
类:
QJsonDocument
QJsonObject
QJsonArray
方法:
1. 创建JSON对象:使用 QJsonObject 来构建JSON对象,并使用键值对填充数据。
2. 创建JSON数组:使用 QJsonArray 来创建一个数组,并添加元素。
3. 组合JSON结构:将JSON数组添加到JSON对象中。
4. 生成JSON文档:通过 QJsonDocument 来处理JSON数据,可以选择格式化(缩进)或压缩形式。
5. 保存到文件:创建 QFile 对象,打开文件,写入JSON数据,并关闭文件。
QJsonObject rootObj;
rootObj["cityid"] = "1010100";
rootObj["date"] = "2024-4-6";
rootObj["weather"] = "雨夹雪";
rootObj["tmp"] = 3;
QJsonArray jsonArr;
jsonArr.append("data1");
jsonArr.append("data2");
jsonArr.append("data3");
jsonArr.append(100);
rootObj["testArr"] = jsonArr;
QJsonObject jsonObject;
jsonObject = rootObj;
rootObj["jsonObject"] = jsonObject;
QJsonArray jsObArr;
jsObArr.append(jsonObject);
jsObArr.append(jsonObject);
rootObj["jsObArr"] = jsObArr;
QJsonDocument jsonDoc(rootObj);
QByteArray jsonArray = jsonDoc.toJson();
QFile file("C:/Users/qzh/Desktop/test.json");
file.open(QIODevice::WriteOnly);
file.write(jsonArray);
file.close();
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/51baf0af23f441379864e422b9ef3115.png)
2.4.2 QT解析JSON数据
1.读取JSON数据
2.生成JSON文件
3.转成JSON对象
QFile file("C:/Users/qzh/Desktop/test.json");
file.open(QIODevice::ReadOnly);
QByteArray rawData = file.readAll();
file.close();
QJsonDocument jsonDoc = QJsonDocument::fromJson(rawData);
if(!jsonDoc.isNull() && jsonDoc.isObject()){
QJsonObject jsonRoot = jsonDoc.object();
QString strW = jsonRoot["weather"].toString();
QString strCityId = jsonRoot["cityid"].toString();
qDebug() << strW;
qDebug() << strCityId;
if(jsonRoot.contains("testArr") && jsonRoot["testArr"].isArray()){
QJsonArray testArray = jsonRoot["testArr"].toArray();
for(QJsonValue val : testArray){
QJsonValue::Type t = val.type();
switch(t){
case QJsonValue::Double:
qDebug() << QString::number(val.toDouble());
break;
case QJsonValue::String:
qDebug() << val.toString();
break;
case QJsonValue::Object:
break;
}
}
}
if(jsonRoot.contains("jsonObject") && jsonRoot["jsonObject"].isObject()){
QJsonObject jsonObject = jsonRoot["jsonObject"].toObject();
qDebug() << "jsonObject"<<jsonObject["cityid"].toString();
}
if(jsonRoot.contains("testArr") && jsonRoot["testArr"].isArray()){
QJsonArray jsObArr = jsonRoot["jsObArr"].toArray();
for(QJsonValue val : jsObArr){
if(val.isObject()){
QJsonObject obj = val.toObject();
qDebug() << obj["weather"].toString();
qDebug() << obj["cityid"].toString();
}
}
}
}
2.5 获取当前城市天气数据
全局变量
QNetworkReply *reply;
QString strUrl;
QNetworkAccessManager *manager;
1.创建一个QNetworkAccessManager对象manager
manager = new QNetworkAccessManager(this);
2.发送请求
strUrl = "http://v1.yiketianqi.com/api?unescape=1&version=v61&appid=87767798&appsecret=7Ujk853M";
QUrl urlTianQi(strUrl);
QNetworkRequest res(urlTianQi);
manager->get(res);
3.QNetworkReply获得相应,并将相关数据存储在replay中
4.绑定信号与槽,manager响应完成后触发数据处理
void Widget::readHttpReply(QNetworkReply *reply)
{
int resCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if(reply->error() == QNetworkReply::NoError && resCode == 200){
QByteArray data = reply->readAll();
parseWeatherJsonData(data);
qDebug() << QString::fromUtf8(data);
}else{
QMessageBox msgBox;
msgBox.setWindowTitle("错误");
msgBox.setText("网络请求失败");
msgBox.setStandardButtons(QMessageBox::Ok);
qDebug() << reply->errorString();
msgBox.exec();
}
}
void Widget::parseWeatherJsonData(QByteArray rawData)
{
QJsonDocument jsonObj = QJsonDocument::fromJson(rawData);
if(!jsonObj.isNull() && jsonObj.isObject()){
QJsonObject objRoot = jsonObj.object();
QString date = objRoot["date"].toString();
QString week = objRoot["week"].toString();
ui->labelCurrentDate->setText(date+" "+ week);
QString cityName = objRoot["city"].toString();
ui->labelCity->setText(cityName);
QString currentTmp = objRoot["tem"].toString();
ui->labelWendu->setText(currentTmp+"℃");
ui->labelWeatherRange->setText(objRoot["tem2"].toString()+"~"+objRoot["tem1"].toString());
ui->labelWeatherType->setText(objRoot["wea"].toString());
ui->labelGanmao->setText(objRoot["air_tips"].toString());
ui->labelFXtype->setText(objRoot["win"].toString());
ui->labelFXtype_2->setText(objRoot["win_speed"].toString());
ui->labelPMData->setText(objRoot["air_pm25"].toString());
ui->labelShiduData->setText(objRoot["humidity"].toString());
ui->labelAirData->setText(objRoot["air_level"].toString());
}
}
2.6 获取不同城市数据信息
1.当搜查按钮点击后触发信号与槽
2.读取当前ui->lineEdit控件的内容
3.解析json格式的城市编码并与当前控件内容进行匹配
4.刷新控件-
由于数据的读取绑定的信号是QNetworkAccessManager::finished,
所当在点击按钮后重对新的url发送请求完成后会触发槽函数进行数据解析
和刷新相关控件操作
void Widget::on_pushButton_clicked()
{
QString cityNameFromUser = ui->lineEditCity->text();
QString cityCode = getCityCodeFromJson(cityNameFromUser);
if(cityCode != NULL){
strUrl += "&cityid=" + cityCode;
manager->get(QNetworkRequest(QUrl(strUrl)));
}else{
QMessageBox msgBox;
msgBox.setWindowTitle("错误");
msgBox.setText("请输入正确城市名");
msgBox.setStandardButtons(QMessageBox::Ok);
qDebug() << reply->errorString();
msgBox.exec();
}
}
QString getCityCodeFromJson(QString name)
{
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 val : citys){
if(val.isObject()){
QString cityName = val["city_name"].toString();
if(cityName == name){
return val["city_code"].toString();
}
}
}
return "";
}
}
2.7 QMap
2.7.1 将CityName与CityCode产生联系
由于每次打开之前都需要打开文件,会很麻烦,所以引入Map容器
在打开的时候检查Map容器是否为空,为空则打开文件将文件内容存储在Map容器中,后续只需要检查Map容器是否为空即可
QMap<QString,int> myMap;
QMap<QString,QString> cityMap;
QString CityCodeUtils::getCityCodeFromName(QString cityName)
{
if(cityMap.isEmpty()){
initCityMap();
}
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 "";
}
}
return it.value();
}
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 val : citys){
if(val.isObject()){
QString cityName = val["city_name"].toString();
QString cityCode = val["city_code"].toString();
cityMap.insert(cityName,cityCode);
}
}
}
}
2.7.1 将天气图标与天气类型产生联系
void Widget::initWeatherIcon()
{
mTypeMap.insert("暴雪",":/res/type/BaoXue.png");
mTypeMap.insert("暴雨",":/res/type/BaoYu. png");
mTypeMap.insert("暴雨到大暴雨",":/res/type/BaoYuDaoDaBaoYu.png");
mTypeMap.insert("大暴雨",":/res/type/DaBaoYu.png");
mTypeMap.insert("大暴雨到特大暴雨",":/res/type/DaBaoYuDaoTeDaBaoYu.png");
mTypeMap.insert("大到暴雪",":/res/type/DaDaoBaoXue.png");
mTypeMap.insert("大雪",":/res/type/DaXue.png");
mTypeMap.insert("大雨",":/res/type/DaYu.png");
mTypeMap.insert("冻雨",":/res/type/DongYu.png");
mTypeMap.insert("多云",":/res/type/DuoYun.png");
mTypeMap.insert("浮沉",":/res/type/FuChen.png");
mTypeMap.insert("雷阵雨",":/res/type/LeiZhenYu.png");
mTypeMap.insert("雷阵雨伴有冰雹",":/res/type/LeiZhenYuBanYouBingBao.png");
mTypeMap.insert("霾",":/res/type/Mai.png");
mTypeMap.insert("强沙尘暴",":/res/type/QiangShaChenBao.png");
mTypeMap.insert("晴",":/res/type/Qing.png");
mTypeMap.insert("沙尘暴",":/res/type/ShaChenBao.png");
mTypeMap.insert("特大暴雨",":/res/type/TeDaBaoYu.png");
mTypeMap.insert("undefined",":/res/type/undefined.png");
mTypeMap.insert("雾",":/res/type/Wu.png");
mTypeMap.insert("小到中雪",":/res/type/XiaoDaoZhongXue.png");
mTypeMap.insert("小到中雨",":/res/type/XiaoDaoZhongYu.png");
mTypeMap.insert("小雪",":/res/type/XiaoXue.png");
mTypeMap.insert("小雨",":/res/type/XiaoYu.png");
mTypeMap.insert("雪",":/res/type/Xue.png");
mTypeMap.insert("扬沙",":/res/type/YangSha.png");
mTypeMap.insert("阴",":/res/type/Yin.png");
mTypeMap.insert("雨",":/res/type/Yu.png");
mTypeMap.insert("雨夹雪",":/res/type/YuJiaXue.png");
mTypeMap.insert("阵雪",":/res/type/ZhenXue.png");
mTypeMap.insert("阵雨",":/res/type/ZhenYu.png");
mTypeMap.insert("中到大雪",":/res/type/ZhongDaoDaXue.png");
mTypeMap.insert("中到大雨",":/res/type/ZhongDaoDaYu.png");
mTypeMap.insert("中雪",":/res/type/ZhongXue.png");
mTypeMap.insert("中雨",":/res/type/ZhongYu.png");
}
ui->labelWeatherIcon->setPixmap(mTypeMap[objRoot["wea"].toString()]);
2.8 获得最近七天的数据
strUrl = "http://v1.yiketianqi.com/api?unescape=1&version=v91&appid=87767798&appsecret=7Ujk853M&ext=";
1.定义一个新的类Day用于存储一天的全部信息
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;
};
2.解析新的url获取的json数据-并将数据存储在数组中
Day days[7];
void Widget::parseWeatherJsonDataNew(QByteArray rawData)
{
QJsonDocument jsonDoc = QJsonDocument::fromJson(rawData);
if( !jsonDoc.isNull() && jsonDoc.isObject()){
QJsonObject jsonRoot = jsonDoc.object();
days[0].mCity = jsonRoot["city"].toString();
days[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();
days[i].mDate = obj["date"].toString();
days[i].mWeek = obj["week"].toString();
days[i].mWeathType = obj["wea"].toString();
days[i].mTemp = obj["tem"].toString();
days[i].mTempLow = obj["tem2"].toString();
days[i].mTempHigh = obj["tem1"].toString();
days[i].mFx = obj["win"].toArray()[0].toString();
days[i].mFl = obj["win_speed"].toString();
days[i].mAirq = obj["air_level"].toString();
days[i].mTips = obj["air_tips"].toString();
days[i].mHu = obj["humidity"].toString();
}
}
}
updateUI();
}
3.设置QList泛型并初始化
QList<QLabel *> mDateList;
QList<QLabel *> mWeekList;
QList<QLabel *> mIconList;
QList<QLabel *> mWeaTypeList;
QList<QLabel *> mAirqList;
QList<QLabel *> mFxList;
QList<QLabel *> mFlList;
void Widget::initDaysElement()
{
mWeekList << ui->labelday0 << ui->labelday1
<< ui->labelday2 << ui->labelday3
<< ui->labelday4 << ui->labelday5;
mDateList << ui->labelDate0 << ui->labelDate1
<< ui->labelDate2 << ui->labelDate3
<< ui->labelDate4 << ui->labelDate5;
mIconList << ui->labelWeatherIcon0 << ui->labelWeatherIcon1
<< ui->labelWeatherIcon2 << ui->labelWeatherIcon3
<< ui->labelWeatherIcon4 << ui->labelWeatherIcon5;
mWeaTypeList << ui->lbweatherType0 << ui->lbweatherType1
<< ui->lbweatherType2 << ui->lbweatherType3
<< ui->lbweatherType4 << ui->lbweatherType5;
mAirqList << ui->labelq0 << ui->labelq1
<< ui->labelq2 << ui->labelq3
<< ui->labelq4 << ui->labelq5;
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;
}
4.通过泛型设置对应控件
void Widget::updateUI()
{
QPixmap pixMap;
ui->labelCurrentDate->setText(days[0].mDate+" "+days[0].mWeek);
ui->labelCity->setText(days[0].mCity+"市");
ui->labelWendu->setText(days[0].mTemp+"℃");
ui->labelWeatherRange->setText(days[0].mTempLow+"~"
+days[0].mTempHigh);
ui->labelWeatherType->setText(days[0].mWeathType);
ui->labelWeatherIcon->setPixmap(mTypeMap[days[0].mWeathType]);
ui->labelGanmao->setText(days[0].mTips);
ui->labelFXtype->setText(days[0].mFx);
ui->labelFXtype_2->setText(days[0].mFl);
ui->labelPMData->setText(days[0].mPm25);
ui->labelShiduData->setText(days[0].mHu);
ui->labelAirData->setText(days[0].mAirq);
for(int i = 0; i < 6; i++){
mWeekList[0]->setText("今天");
mWeekList[1]->setText("明天");
mWeekList[2]->setText("后天");
mWeekList[i]->setText(days[i].mWeek);
QStringList dayList = days[i].mDate.split("-");
mDateList[i]->setText(dayList[1] + "-" +dayList[2]);
int index = days[i].mWeathType.indexOf("转");
if(index != -1){
pixMap = mTypeMap[days[i].mWeathType.left(index)];
}else{
pixMap = mTypeMap[days[i].mWeathType];
}
pixMap = pixMap.scaled(mIconList[i]->size(),Qt::KeepAspectRatio,Qt::SmoothTransformation);
mIconList[i]->setMaximumHeight(50);
mIconList[i]->setMinimumWidth(ui->widget0402->width()/6.5);
mIconList[i]->setPixmap(pixMap);
mWeaTypeList[i]->setText(days[i].mWeathType);
QString airQ = days[i].mAirq;
mAirqList[i]->setText(days[i].mAirq);
if( airQ == "优"){
mAirqList[i]->setStyleSheet("background-color:rgb(150,213,32);border-radius: 10px;");
}else if(airQ == "良"){
mAirqList[i]->setStyleSheet("background-color:rgb(241,224,103);border-radius: 10px;");
}else if(airQ == "轻度"){
mAirqList[i]->setStyleSheet("background-color:rgb(255,199,199);border-radius: 10px;");
}else if(airQ == "中度"){
mAirqList[i]->setStyleSheet("background-color:rgb(255,17,17);border-radius: 10px;");
}else if(airQ == "重度"){
mAirqList[i]->setStyleSheet("background-color:rgb(153,0,0);border-radius: 10px;");
}
mFlList[i]->setText(days[i].mFl);
index = days[i].mFl.indexOf("转");
if(index != -1){
mFlList[i]->setText(days[i].mFl.left(index));
}else{
mFlList[i]->setText(days[i].mFl);
}
mFxList[i]->setText(days[i].mFx);
}
}
2.9 事件过滤器在子控件上画线
若直接通过重写paintEvent事件,则会被layout遮挡
因此通过事件过滤对事件过滤在子控件上画线
ui->widget0404->installEventFilter(this);
ui->widget0405->installEventFilter(this);
bool Widget::eventFilter(QObject *watched, QEvent *event)
{
if(watched == ui->widget0404 && event->type() == QEvent::Paint){
drawTempLineHight();
return true;
}
if(watched == ui->widget0405 && event->type() == QEvent::Paint){
drawTempLineLow();
return true;
}
return QWidget::eventFilter(watched,event);
}
void Widget::drawTempLineHight()
{
QPainter painter(ui->widget0404);
painter.setRenderHint(QPainter::Antialiasing,true);
painter.setBrush(Qt::yellow);
painter.setPen(Qt::yellow);
int ave;
int sum = 0;
int offset;
int middle = ui->widget0404->height()/2;
for(int i=0 ; i < 6 ;i++){
sum += days[i].mTempHigh.toInt();
qDebug() << days[i].mTempHigh;
}
ave = sum/6;
QPoint points[6];
for(int i = 0; i < 6; i++){
points[i].setX(mAirqList[i]->x() + mAirqList[i]->width()/2);
offset = days[i].mTempHigh.toInt() - ave;
points[i].setY(middle - offset*3);
painter.drawEllipse(QPoint(points[i]),3,3);
painter.drawText(points[i].x()-15,points[i].y()-15,days[i].mTempHigh + "℃");
}
painter.setBrush(Qt::NoBrush);
for(int i=0;i<5;i++){
painter.drawLine(points[i],points[i+1]);
}
}
void Widget::drawTempLineLow()
{
QPainter painter(ui->widget0405);
painter.setRenderHint(QPainter::Antialiasing,true);
painter.setBrush(Qt::blue);
painter.setPen(Qt::blue);
int ave;
int sum = 0;
int offset;
int middle = ui->widget0405->height()/2;
for(int i=0 ; i < 6 ;i++){
sum += days[i].mTempLow.toInt();
qDebug() << days[i].mTempLow;
}
ave = sum/6;
QPoint points[6];
for(int i = 0; i < 6; i++){
points[i].setX(mAirqList[i]->x() + mAirqList[i]->width()/2);
offset = days[i].mTempLow.toInt() - ave;
points[i].setY(middle - offset*3);
painter.drawEllipse(QPoint(points[i]),3,3);
painter.drawText(points[i].x()-15,points[i].y()-15,days[i].mTempLow + "℃");
}
for(int i=0;i<5;i++){
painter.drawLine(points[i],points[i+1]);
}
}
2.10 LineEdit的returnPressed()
void Widget::on_lineEditCity_returnPressed()
{
on_pushButton_clicked();
}
3.最终效果
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/869a36e076914678a3912cdcf4a72fe5.png)
链接:https:
提取码:sw2n
--来自百度网盘超级会员V5的分享