最近项目中需要一个显示传感器相关数据的集成化上位机,分别显示硬件中的雷达、油污监测管、温湿度传感器、雨量传感器等传回的数据,数据是通过串口传送的,后期的话要考虑4G模组通过TCP协议进行读入,因此先搭建一个串口调试助手的界面,可以实时显示串口传入的数据值,并用折线图显示出来,同时实现了天气的API查询。具体代码如下所示。
//mainwindow.h头文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "qcustomplot.h"
#include "weather.h"
#include "widget.h"
#include <QHBoxLayout>
#include <QDebug>
#include <QSerialPort>//提供访问串口的功能
#include <QSerialPortInfo>//提供系统中存在的串口的信息
#include <QTime>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
QByteArray buf;
double buffer;
double tempnum[30];
double n;
double newdata;
private slots:
void datetimeShow();
void weatherShow();
void on_openButton_clicked();
void on_sendButton_clicked();
void ReadData();
void on_plotButton_clicked();
void SerialData();
void UpdateData(QCustomPlot *customPlot,double tempnum[30],double n);
void on_weatherButton_clicked();
signals:
void plotButton_clicked();
private:
Ui::MainWindow *ui;
Weather *weather;
QSerialPort *serial;
};
#endif // MAINWINDOW_H
//mainwindow.cpp 源文件
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent):
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
{//寻找可用串口,将串口名添加到ui->serialnameBox中
QSerialPort serial;//创建串口对象
serial.setPort(info);//读取串口参数
if(serial.open(QIODevice::ReadWrite))
{
ui->serialnameBox->addItem(serial.portName());//串口名添加
serial.close();
}
}
ui->bautBox->setCurrentIndex(3);//初始化9600比特率
ui->dataBox->setCurrentIndex(3);
ui->stopBox->setCurrentIndex(0);
ui->checkBox->setCurrentIndex(0);//初始化8-0-1
this->setWindowTitle("智慧河道监测系统");
this->setWindowIcon(QIcon("E:\\Qt\\source\\icons\\earth.ico"));
ui->tabWidget->setTabIcon(0,QIcon("E:\\Qt\\source\\icons\\setting.ico"));
ui->tabWidget->setTabIcon(1,QIcon("E:\\Qt\\source\\icons\\show.ico"));
ui->tabWidget->setTabIcon(2,QIcon("E:\\Qt\\source\\icons\\record.ico"));
ui->tabWidget->setIconSize(QSize(50,25));
ui->tabWidget->setAttribute(Qt::WA_StyledBackground);
weather=new Weather(QStringLiteral("西安"));
QTimer *dataTimer= new QTimer();
connect(dataTimer,SIGNAL(timeout()),this,SLOT(SerialData()));
connect(dataTimer,SIGNAL(timeout()),this,SLOT(datetimeShow()));
connect(dataTimer,SIGNAL(timeout()),this,SLOT(weatherShow()));
connect(weather,&Weather::getDataSuccessedSignal,this,&MainWindow::weatherShow);
dataTimer->start(1000);//每一秒(1000ms)刷新一次
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::datetimeShow()
{
QDateTime nowdatetime = QDateTime::currentDateTime();
QString nowdate=nowdatetime.toString("yyyy-MM-dd");
QString nowtime=nowdatetime.toString("hh:mm:ss");
ui->nowdateLabel->setText(nowdate);
ui->nowtimeLabel->setText(nowtime);
}
void MainWindow::weatherShow()
{
ui->temperatureLabel->setText(weather->wendu);
ui->windLabel->setText(weather->fengli);
}
void MainWindow::on_openButton_clicked()
{//点击openButton时的SLOT,进行串口的打开与关闭
if(ui->openButton->text()==tr("打开串口"))
{
serial=new QSerialPort;
serial->setPortName(ui->serialnameBox->currentText());
serial->open(QIODevice::ReadWrite);
switch (ui->bautBox->currentIndex())
{
case 0:
serial->setBaudRate(QSerialPort::Baud1200);
break;
case 1:
serial->setBaudRate(QSerialPort::Baud2400);
break;
case 2:
serial->setBaudRate(QSerialPort::Baud4800);
break;
case 3:
serial->setBaudRate(QSerialPort::Baud9600);
break;
case 4:
serial->setBaudRate(QSerialPort::Baud19200);
break;
case 5:
serial->setBaudRate(QSerialPort::Baud38400);
break;
case 6:
serial->setBaudRate(QSerialPort::Baud57600);
break;
case 7:
serial->setBaudRate(QSerialPort::Baud115200);
break;
}
switch (ui->dataBox->currentIndex())
{
case 0:
serial->setDataBits(QSerialPort::Data5);
break;
case 1:
serial->setBaudRate(QSerialPort::Data6);
break;
case 2:
serial->setBaudRate(QSerialPort::Data7);
break;
case 3:
serial->setBaudRate(QSerialPort::Data8);
break;
}
switch (ui->checkBox->currentIndex())
{
case 0:
serial->setParity(QSerialPort::NoParity);
break;
case 1:
serial->setParity(QSerialPort::OddParity);
break;
case 2:
serial->setParity(QSerialPort::EvenParity);
break;
}
switch (ui->stopBox->currentIndex())
{
case 0:
serial->setStopBits(QSerialPort::OneStop);
break;
case 1:
serial->setStopBits(QSerialPort::OneAndHalfStop);
break;
case 2:
serial->setStopBits(QSerialPort::TwoStop);
break;
}
serial->setFlowControl(QSerialPort::NoFlowControl);
ui->serialnameBox->setEnabled(false);
ui->dataBox->setEnabled(false);
ui->checkBox->setEnabled(false);
ui->bautBox->setEnabled(false);
ui->stopBox->setEnabled(false);
ui->openButton->setText("关闭串口");
QObject::connect(serial,&QSerialPort::readyRead,this,&MainWindow::ReadData);
}
else {
serial->clear();
serial->close();
serial->deleteLater();
ui->serialnameBox->setEnabled(true);
ui->dataBox->setEnabled(true);
ui->checkBox->setEnabled(true);
ui->bautBox->setEnabled(true);
ui->stopBox->setEnabled(true);
ui->openButton->setText("打开串口");
}
}
void MainWindow::on_sendButton_clicked()
{
if(ui->openButton->text()==tr("打开串口"))
{
ui->receiveTextEdit->appendPlainText("请先选择串口");
}
else {
serial->write(ui->sendTextEdit->toPlainText().toLatin1());
}
}
void MainWindow::ReadData()
{
buf=serial->readAll();//得到串口读入数据,是QByteArray型
if(!buf.isEmpty())
{
QString str=buf;
ui->receiveTextEdit->appendPlainText(str);
qDebug()<<buf.toHex();
}
buffer=buf.toFloat()*50;
buf.clear();
}
void MainWindow::on_plotButton_clicked()
{
emit plotButton_clicked();
}
void MainWindow::SerialData()
{
UpdateData(ui->dataplot,tempnum,buffer);//每过一秒,更新串口接收转换数据,是float型
}
void MainWindow::UpdateData(QCustomPlot *customPlot,double tempnum[30],double n)
{
QVector <double> key(30);
QVector <double> value1(30);
double now = QDateTime::currentDateTime().toTime_t();
for(int i=0; i<29; i++){
tempnum[i] =tempnum[i+1];//后9个点左移一位到前9个点
}
tempnum[29] = n;//更新新点
for(int i = 0; i<30; i++)
{
key[i] = now-30+i;//横轴的时间显示
value1[i] = tempnum[i];//纵轴的数值
}
ui->dataplot->addGraph();
ui->dataplot->graph(0)->setPen(QPen(Qt::blue));
ui->dataplot->graph(0)->setData(key,value1);
ui->dataplot->xAxis->setLabel("time");
ui->dataplot->yAxis->setLabel("throughput/Mbps");//横纵轴
QSharedPointer<QCPAxisTickerDateTime> dateTicker(new QCPAxisTickerDateTime);
//dateTicker->setDateTimeFormat("hh:mm:ss"); // yyyy-MM-dd hh:mm:ss
dateTicker->setDateTimeFormat("hh:mm:ss\nyyyy-MM-dd");
ui->dataplot->xAxis->setTicker(dateTicker);
//ui->dataplot->xAxis->setTickLabelRotation(30); //旋转30度
ui->dataplot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);//鼠标可移动缩放
ui->dataplot->xAxis->setRange(now-30,now);//确定框体范围
ui->dataplot->yAxis->setRange(0,1000);//确定框体范围
ui->dataplot->replot();
}
void MainWindow::on_weatherButton_clicked()
{
Widget *wg=new Widget;
wg->show();
}
//weather.h
/*
* 天气获取类
*/
#ifndef WEATHER_H
#define WEATHER_H
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QMap>
#include <QMultiMap>
class Weather : public QObject
{
Q_OBJECT
public:
explicit Weather(QObject *parent = nullptr);
explicit Weather(QString cityName = "");
public:
void setCityName(QString cityName); //设置城市名
QString getCityName(); //获取城市名
QString getDate(); //获取当前日期
QString getFengLi(); //获取风向风力
QString getWenDu(); //获取温度范围
QString getTianQiType(); //获取天气类型
QString getCurrentWendu(); //获取当前温度
QString getGanMaoInfo(); //获取感冒提示
QString getAllInfo(); //获取原始的所有字段
bool isGetDataSuccessd(); //是否成功获取数据
void refresh(); //刷新
QMap<QString, QMap<QString, QString> > getDataMap(bool *ok=nullptr); //获取昨天以及未来5天的天气预测
void print_Debug_allinfoMap(); //调试打印所有信息
QString fengli; //风力
QString wendu; //温度
signals:
void getDataFinisedSignal();//获取数据结束的信号
void getDataSuccessedSignal();//获取数据成功的信号
void getDataFailedSignal();//获取数据失败的信号
public slots:
void replyFinished(QNetworkReply *reply);//刷新的槽
private:
void queryWeather();//查询
private:
QString cityName;
QNetworkAccessManager *manager; //请求句柄
QString allinfo; //所有信息
//以下皆是当天,未来几天的数据框通过获取日期的数据列表
QString date;//当前日期
QString currentwendu;//当前温度
QString weather_type; //天气类型
QString ganmao;//对于感冒提示
bool isGetData=false;//是否成功获取数据
QMap<QString,QMap<QString,QString>> dataMap;
};
#endif // WEATHER_H
//weather.cpp
/*
* 天气获取类
*/
#include "weather.h"
Weather::Weather(QObject *parent) : QObject(parent)
{
manager = new QNetworkAccessManager(this); //新建QNetworkAccessManager对象
connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(replyFinished(QNetworkReply*)));//关联信号和槽
}
Weather::Weather(QString cityName)
{
manager = new QNetworkAccessManager(this); //新建QNetworkAccessManager对象
connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(replyFinished(QNetworkReply*)));//关联信号和槽
this->cityName=cityName;
refresh();
}
void Weather::setCityName(QString cityName)
{
this->cityName=cityName;
}
QString Weather::getCityName()
{
return this->cityName;
}
/*
* 获取当前日期
*/
QString Weather::getDate()
{
return this->date;
}
QString Weather::getFengLi()
{
return this->fengli;
}
QString Weather::getWenDu()
{
return this->wendu;
}
QString Weather::getTianQiType()
{
return this->weather_type;
}
QString Weather::getCurrentWendu()
{
return this->currentwendu;
}
QString Weather::getGanMaoInfo()
{
return this->ganmao;
}
QString Weather::getAllInfo()
{
return this->allinfo;
}
bool Weather::isGetDataSuccessd()
{
return this->isGetData;
}
/*
* 刷新天气
*/
void Weather::refresh()
{
this->currentwendu.clear();
this->wendu.clear();
this->ganmao.clear();
this->fengli.clear();
this->weather_type.clear();
this->allinfo.clear();
queryWeather();
dataMap.clear();//刷新的清空,待获取时在加载
}
QMap<QString, QMap<QString, QString> > Weather::getDataMap(bool *ok)
{
bool Oktemp;
if(ok==nullptr)
ok=&Oktemp;
if(!this->dataMap.isEmpty())
{
*ok=true;
return this->dataMap;
}
*ok=false;
if(!this->isGetData)
return this->dataMap;
QJsonParseError err;
QJsonDocument json_recv = QJsonDocument::fromJson(allinfo.toUtf8(),&err);//解析json对象
qDebug() <<"Json-Error:"<< err.error;
if(!json_recv.isNull())
{
QJsonObject object = json_recv.object();
if(object.contains("data"))
{
QJsonValue value = object.value("data"); // 获取指定 key 对应的 value
if(value.isObject())
{
QJsonObject object_data = value.toObject();
if(object_data.contains("yesterday")&&object_data.contains("forecast"))//若存在昨天及预测天气则加载所有数据
{
QJsonValue value=object_data.value("yesterday");
if(value.isObject())
{
QMap<QString,QString>mapvalue;
mapvalue["high"]=value.toObject().value("high").toString();
mapvalue["low"]=value.toObject().value("low").toString();
mapvalue["fengxiang"]=value.toObject().value("fx").toString();
mapvalue["fengli"]=value.toObject().value("fl").toString();
mapvalue["type"]=value.toObject().value("type").toString();
dataMap[value.toObject().value("date").toString()]=mapvalue;
}
value = object_data.value("forecast");
if(value.isArray())
{
QJsonArray valueArray=value.toArray();
qDebug()<<"WeatherData count:"<<valueArray.count();
for(int i=0;i<valueArray.count();i++)
{
QJsonObject object = valueArray.at(i).toObject();
QMap<QString,QString>mapvalue;
mapvalue["high"]=object.value("high").toString();
mapvalue["low"]=object.value("low").toString();
mapvalue["fengxiang"]=object.value("fengxiang").toString();
mapvalue["fengli"]=object.value("fengli").toString();
mapvalue["type"]=object.value("type").toString();
dataMap[object.value("date").toString()]=mapvalue;
}
// QString low = today_weather.value("low").toString();
// QString high = today_weather.value("high").toString();
// QString strength = today_weather.value("fengli").toString();
// strength.remove(0,8);
// strength.remove(strength.length()-2,2);
/*
{
"data":
{
"yesterday":
{
"date":"17日星期六","high":"高温 32℃","fx":"北风","low":"低温 19℃","fl":"<![CDATA[3-4级]]>","type":"晴"
},
"city":"北京",
"forecast":[
{
"date":"18日星期天","high":"高温 32℃","fengli":"<![CDATA[<3级]]>","low":"低温 21℃","fengxiang":"北风","type":"晴"
},
{
"date":"19日星期一","high":"高温 31℃","fengli":"<![CDATA[<3级]]>","low":"低温 22℃","fengxiang":"南风","type":"多云"
},
{
"date":"20日星期二","high":"高温 25℃","fengli":"<![CDATA[<3级]]>","low":"低温 20℃","fengxiang":"南风","type":"小雨"
},
{
"date":"21日星期三","high":"高温 31℃","fengli":"<![CDATA[<3级]]>","low":"低温 21℃","fengxiang":"北风","type":"多云"
},
{
"date":"22日星期四","high":"高温 30℃","fengli":"<![CDATA[<3级]]>","low":"低温 22℃","fengxiang":"北风","type":"晴"
}
],
"ganmao":"各项气象条件适宜,发生感冒机率较低。但请避免长期处于空调房间中,以防感冒。",
"wendu":"24"
},
"status":1000,
"desc":"OK"
}
*/
qDebug()<<QString::fromLocal8Bit("获取天气成功");
*ok=true;
return dataMap;
}
}
}
}
}else
{
qDebug()<<"json_recv is NULL or is not a object !!";
}
return dataMap;
}
/*
* qDebug打印Map数据
*/
void Weather::print_Debug_allinfoMap()
{
getDataMap();
qDebug()<<endl;
qDebug()<<"city:"<<this->cityName;
qDebug()<<"wendu:"<<this->wendu;
qDebug()<<"currentwendu:"<<this->currentwendu;
qDebug()<<"fengli:"<<this->fengli;
qDebug()<<"weather_type:"<<this->weather_type;
qDebug()<<"ganmao:"<<this->ganmao;
QString str;
foreach (QString key, dataMap.keys()) {
str="date"+key+"[";
foreach (QString key1, dataMap.value(key).keys()) {
// qDebug()<<key1<<dataMap.value(key).value(key1);
str+=key1+':'+dataMap.value(key).value(key1)+' ';
}
str+=']';
qDebug()<<str;
}
dataMap.clear();
}
void Weather::replyFinished(QNetworkReply *reply)
{
this->isGetData=false;
qDebug()<<"recv weather data!!";
allinfo = reply->readAll();
// ui->textEdit->setText(all); //将接收到的数据显示出来
QJsonParseError err;
QJsonDocument json_recv = QJsonDocument::fromJson(allinfo.toUtf8(),&err);//解析json对象
qDebug() <<"Json-Error:"<< err.error;
if(!json_recv.isNull())
{
QJsonObject object = json_recv.object();
if(object.contains("data"))
{
QJsonValue value = object.value("data"); // 获取指定 key 对应的 value
if(value.isObject())
{
QJsonObject object_data = value.toObject();
this->cityName=object_data.value("city").toString();
this->currentwendu=object_data.value("wendu").toString();
this->ganmao=object_data.value("ganmao").toString();
if(object_data.contains("forecast"))
{
QJsonValue value = object_data.value("forecast");
if(value.isArray())
{
QJsonObject today_weather = value.toArray().at(0).toObject();
weather_type = today_weather.value("type").toString();
date = today_weather.value("date").toString();
QString low = today_weather.value("low").toString();
QString high = today_weather.value("high").toString();
wendu = low.mid(low.length()-3,4) +"-"+ high.mid(high.length()-3,4);
QString strength = today_weather.value("fengli").toString();
strength.remove(0,8);
strength.remove(strength.length()-2,2);
fengli = today_weather.value("fengxiang").toString() + strength;
// ui->type->setText(weather_type); //显示天气类型
// ui->wendu->setText(wendu); //显示温度
// ui->fengli->setText(fengli); //显示风力
this->isGetData=true;
}
}
}
}
}else
{
qDebug()<<"json_recv is NULL or is not a object !!";
}
reply->deleteLater(); //销毁请求对象
if(isGetData)
{
qDebug()<<QString::fromLocal8Bit("获取天气成功");
emit this->getDataSuccessedSignal();
}
else
{
qDebug()<<QString::fromLocal8Bit("获取天气失败");
emit this->getDataFailedSignal();
}
emit this->getDataFinisedSignal();
}
/*
* 查询天气
*/
void Weather::queryWeather()
{
// QString local_city = ui->lineEdit->text().trimmed(); //获得需要查询天气的城市名称
char quest_array[256]="http://wthrcdn.etouch.cn/weather_mini?city=";
QNetworkRequest quest;
sprintf(quest_array,"%s%s",quest_array,cityName.toUtf8().data());
quest.setUrl(QUrl(quest_array));
quest.setHeader(QNetworkRequest::UserAgentHeader,"RT-Thread ART");
/*发送get网络请求*/
manager->get(quest);
}
//widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "weather.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void getWeatherFailedSlot();
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
void on_pushButton_4_clicked();
private:
Ui::Widget *ui;
Weather *weather;
bool isGetData=false;
};
#endif // WIDGET_H
//widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QMessageBox>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
weather=new Weather(QStringLiteral("西安"));
connect(weather,&Weather::getDataSuccessedSignal,this,&Widget::on_pushButton_clicked);
connect(weather,SIGNAL(getDataFailedSignal()),this,SLOT(getWeatherFailedSlot()));
}
Widget::~Widget()
{
delete ui;
}
void Widget::getWeatherFailedSlot()
{
QMessageBox::warning(this,QString::fromLocal8Bit("获取天气提示"),QString::fromLocal8Bit("获取天气失败,请确认网络是否连接,或城市名的输入"));
}
void Widget::on_pushButton_clicked()
{
ui->lineEdit_cityName->setText(weather->getCityName());
ui->lineEdit_date->setText(weather->getDate());
ui->lineEdit_currentWendu->setText(weather->getCurrentWendu()+QString::fromLocal8Bit("℃"));
ui->lineEdit_Fengli->setText(weather->getFengLi());
ui->lineEdit_type->setText(weather->getTianQiType());
ui->lineEdit_Wendu->setText(weather->getWenDu());
ui->textBrowser_ganmao->setText(weather->getGanMaoInfo());
ui->tableWidget->clearContents();
ui->tableWidget->setRowCount(0);
QMap<QString,QMap<QString,QString>> map=weather->getDataMap();
int n=0;
foreach (QString date, map.keys()) {
ui->tableWidget->insertRow(ui->tableWidget->rowCount());
QMap<QString,QString> mapvalue=map.value(date);
ui->tableWidget->setItem(n,0,new QTableWidgetItem(date));
ui->tableWidget->setItem(n,1,new QTableWidgetItem(mapvalue.value("type")));
QString low = mapvalue.value("low");
QString high = mapvalue.value("high");
ui->tableWidget->setItem(n,2,new QTableWidgetItem(low.mid(low.length()-3,4) +"-"+ high.mid(high.length()-3,4)));
QString strength = mapvalue.value("fengli");
strength.remove(0,8);
strength.remove(strength.length()-2,2);
ui->tableWidget->setItem(n,3,new QTableWidgetItem(mapvalue.value("fengxiang") + strength));
n++;
}
qDebug()<<weather->getCityName();
qDebug()<<QStringLiteral("风力风向")<<weather->getFengLi();
qDebug()<<QStringLiteral("天气类型")<<weather->getTianQiType();
qDebug()<<QStringLiteral("温度")<<weather->getWenDu();
// ui->textBrowser->setText(weather->getAllInfo());
weather->getDataMap();
}
void Widget::on_pushButton_2_clicked()
{
weather->refresh();
}
void Widget::on_pushButton_3_clicked()
{
if(ui->lineEdit->text().isEmpty())
return;
weather->setCityName(ui->lineEdit->text());
weather->refresh();
}
void Widget::on_pushButton_4_clicked()
{
weather->print_Debug_allinfoMap();
}
//main.cpp
#include "mainwindow.h"
#include "curvewidget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow mw;
curvewidget cw;
mw.show();
QObject::connect(&mw,SIGNAL(plotButton_clicked()),&cw,SLOT(show()));
return a.exec();
}
?mainwindow.ui文件
?weather.ui文件
?
?
代码中有一些本地图片可能需要去掉,不然会出错,其他应该可以调通没问题,天气查询代码仿照了其他博主的内容,直接调用了这个窗口,最终实现网络连接查询的效果。
最终效果?
?
项目还属于雏形,有许多需要改进的地方,和大家一起学习进步。?
|