#include
#include
#include
#include "qcustomplot.h"
#include
#define MAX_COUNT 1500 // x轴可显示的最大范围
#define cout qDebug() << __LINE__ << ":"
class Handle : public QObject
{
Q_OBJECT
public:
explicit Handle(QCustomPlot *P,QObject *parent = nullptr);
signals:
void finish(); // 数据解析完成时发出信号
public slots:
void getValidPort(QComboBox* p); // 获取有效串口端口
void getText(QString t);
void analyData(); // 解析数据
void setFlag(QString b = "[",QString m=",",QString e="]"){
begin = b;mid = m; end = e;
} // 自定义数据格式,分隔符
void clear(); // 数据清空
void setAuto(bool e){ // 是否设置为自动模式
isAuto = e;
}
void setRun(bool e){ // 是否设置为运行
isRun = e;
}
void setOpen(bool e){ // 是否打开串口
isPortOpen = e;
}
bool getOpen(){return isPortOpen;} // 获取串口打开状态
bool getRun(){return isRun;} // 获取串口是否在运行
bool getAuto(){return isAuto;} // 获取是否为自动模式
private:
QString begin,mid,end; // 自定义数据格式
QString text;
bool isPortOpen; // 标识串口端口是否打开
QStringList list;
QCustomPlot *Plot; // 图层
QVector <QCPItemTracer *> trace; // 曲线上的跟随点
QVector <QCPItemText * > Ptext; // 每条曲线上的显示的文字
QVector <QVector<double>> YData; // Y轴数据,可具有多个数据曲线
QVector <double> XData; // x轴数据
QVector<QPen> pen; // 曲线颜色的区分
int Clk,xMid; // x轴的刻度
int countLine; // 曲线的数量
int firstLine;
bool isAuto,isRun; // 标识是否自动,是否在运行
};
/*
* 串口数据的解析
*/
void Handle::analyData()
{
// 进行数据提取,比如begin="[",mid=",",end="]",串口数据为,30][10,20,30][...
//
int _end = text.indexOf(begin);
text.remove(0,_end+1); // 移除前面多余的数据,比如移除,30][,还剩10,20,30][...
if( _end < 0 || !isRun){
return ;
}
// 将数据分段,比如分出10,20,30]和下一段[...
QStringList _be = text.split(begin,QString::SkipEmptyParts);
for(auto _b:_be){
int _e = _b.indexOf(end); // 找出数据段里的]的位置
if(_e > 0){
QStringList _list = _b.left(_e).split(mid,QString::SkipEmptyParts); // 将数据10,20,30中的各个数据分离出来
countLine = 0; // 统计一段数据里的数据个数
for(auto i:list){ // 分离出的数据放入各个曲线数据里
YData[countLine].push_back(i.toDouble());
if(YData[countLine].count() >= MAX_COUNT){
YData[countLine].pop_front();
}
countLine ++;
}
XData.push_back(Clk++);
if(XData.count() >= MAX_COUNT){
XData.pop_front();
}
for(int _i = firstLine; _i < countLine && _i < 6; _i ++){
firstLine = countLine;
Plot->addGraph();
Plot->graph(_i)->setPen(pen.at(_i)); // 给数据曲线上色
Plot->graph(_i)->setVisible(true); // 曲线显示
trace[_i] = new QCPItemTracer(Plot); // 生成一个跟随点
trace[_i]->setPen(pen.at(_i)); // 设置点的颜色
trace[_i]->setSize(10); // 设置点的大小
trace[_i]->setBrush(QBrush(pen.at(_i).color()));
trace[_i]->setGraph(Plot->graph(_i)); // 设置在该曲线显示
trace[_i]->setStyle(QCPItemTracer::tsCircle); // 圆形点
trace[_i]->setInterpolating(true);
Ptext[_i] = new QCPItemText(Plot); // 跟随点的文本信息
Ptext[_i]->setPen(pen.at(_i));
Ptext[_i]->setText(QString("Graph:%1").arg(_i));
Ptext[_i]->setRotation(4);
Ptext[_i]->setTextAlignment(Qt::AlignRight | Qt::AlignBottom|Qt::AlignJustify);
Ptext[_i]->setPositionAlignment(Qt::AlignRight | Qt::AlignBottom|Qt::AlignJustify);
Ptext[_i]->position->setType(QCPItemPosition::ptPlotCoords);
Ptext[_i]->setFont(QFont("Helvetica [Cronyx]", 12));
Ptext[_i]->setPadding(QMargins(8, 0, 0, 0));
YData[_i].resize(MAX_COUNT);
}
for(int _i = 0; _i < countLine; _i ++){
Plot->graph(_i)->setData(XData,YData[_i]);
if(Clk <= xMid){
Plot->xAxis->setRange(0,Clk);
}
else{
Plot->xAxis->setRange(Clk-xMid,Clk);
}
Ptext[_i]->position->setCoords(Clk,YData[_i].last()); // 文本显示的位置
Ptext[_i]->setText(QString("%1:(%2,%3)").arg(_i+1).arg(Clk).arg(YData[_i].last())); // 文本内容
trace[_i]->setGraphKey(Clk); // 跟随点的位置
if(isAuto){ // 是否自动适配缩放
Plot->yAxis->rescale(true);
}
}
}
}
Plot->replot(); // 图层更新
if(text.length() > 200){ // 判断串口接收数据是否过多,过多久丢弃
text.clear();
}
emit finish();
}
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include
#include
#include
#include "qcustomplot.h"
#include
#include
#include
#include
#include
#include "handle.h"
#include
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
void goClear(); // 清除
void autoHandle(); // 没用上
signals:
void analy(QString t);
void clearT();
void getValidP(QComboBox* p);
void startA();
private slots:
void on_pushOpen_clicked();
void on_pushClear_clicked();
void on_pushRun_clicked();
void on_pushAuto_clicked();
private:
Ui::Widget *ui;
Handle *hand;
QCustomPlot *Plot;
QSerialPort *SerialPort;
QThread *thread;
};
#endif // WIDGET_H
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
SerialPort = new QSerialPort(this);
Plot = ui->widget;
Plot->setMouseTracking(true); // 使能图层鼠标跟随
// 开启图层上的图层刻度可手动调动
Plot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
connect(Plot->xAxis, SIGNAL(rangeChanged(QCPRange)), Plot->xAxis2, SLOT(setRange(QCPRange)));
connect(Plot->yAxis, SIGNAL(rangeChanged(QCPRange)), Plot->yAxis2, SLOT(setRange(QCPRange)));
connect(Plot,&QCustomPlot::mouseMove,[=](QMouseEvent *e){
double _x = Plot->xAxis->pixelToCoord(e->x()),_y = Plot->yAxis->pixelToCoord(e->y());
ui->labelWhere->setText(QString("(%1,%2)").arg(_x).arg(_y));
});
connect(Plot,&QCustomPlot::plottableClick,[=](
QCPAbstractPlottable * plottable,
int ,
QMouseEvent * ){
ui->labelGraph->setText(plottable->name());
});
ui->tabWidget->setTabText(0,"Gui");
ui->tabWidget->setTabText(1,"Data");
hand = new Handle(Plot);
thread = new QThread(this);
hand->moveToThread(thread);
// 获取自定义数据
hand->setFlag(ui->lineEditBegin->text(),ui->lineEditMid->text(),ui->lineEditEnd->text());
// 绑定清除按键的响应
connect(this,&Widget::clearT,hand,&Handle::clear,Qt::QueuedConnection);
// 有串口信息时,将数据给Handle类处理
connect(this,&Widget::analy,hand,&Handle::getText,Qt::QueuedConnection);
// 绑定获取有效串口端口信息
connect(this,&Widget::getValidP,hand,&Handle::getValidPort,Qt::QueuedConnection);
// 这个也没用上,还请注意
connect(hand,&Handle::finish,this,&Widget::autoHandle,Qt::QueuedConnection);
connect(SerialPort,&QSerialPort::readyRead,[=](){
emit analy(SerialPort->readAll());
});
connect(SerialPort,&QSerialPort::errorOccurred,[=](QSerialPort::SerialPortError e){
cout << "error";
if(e == QSerialPort::DeviceNotFoundError){
SerialPort->close();
hand->setOpen(false);
on_pushClear_clicked();
}
});
thread->start(); // 开启线程
goClear();
}
注:这个程序有个小问题,就是当窗口全屏时,就会发生数据丢失加剧的现象。当时的我本想试着再加一个线程来专门处理曲线绘画的,后来发现数据丢失的现象更严重,就没加那个线程了。数据丢失是由下位机发信息太过于频繁,比如频率大于1000HZ时,频率降低可解决。