[Qt]基于QChartView开发的图表显示控件,支持实时显示,动态更新,支持鼠标交互等操作
前言
这是一个Qt平台的基于QChartView类的图像显示控件,支持鼠标交互,支持数据实时显示,动态更新,坐标轴自适应点集的值,鼠标实时点显示。
实现平台:Windows 10 x64 + Qt 6.2.3 + MSVC 2019
先来看演示视频
控件类关键代码说明
十字线和显示坐标实现
在.h文件中定义十字线lineitem变量和坐标textitem变量
QGraphicsLineItem *m_xLine;
QGraphicsLineItem *m_yLine;
QGraphicsTextItem *m_txtPos;
在Cpp文件中初始化
m_xLine =newQGraphicsLineItem();
m_xLine->setPen(QPen(QColor(100,100,100)));
m_xLine->setZValue(2);
m_yLine =newQGraphicsLineItem();
m_yLine->setPen(QPen(QColor(100,100,100)));
m_yLine->setZValue(2);scene()->addItem(m_xLine);scene()->addItem(m_yLine);
m_txtPos =newQGraphicsTextItem();
m_txtPos->setFont(QFont("宋体",15, QFont::Bold));
m_txtPos->setVisible(false);scene()->addItem(m_txtPos);
然后定义鼠标事件,在鼠标进入时显示,移出时隐藏,移动时显示。
voidChartDrawer::mouseMoveEvent(QMouseEvent *pEvent){
QPoint point = pEvent->pos();
m_xLine->setLine(point.x(),0,point.x(),this->height());
m_yLine->setLine(0,point.y(),this->width(),point.y());
QPointF chartPoint = m_chart->mapToValue(point);
m_txtPos->setPlainText(QString("%1,%2").arg(QString::number(chartPoint.x(),'f',3),QString::number(chartPoint.y(),'f',3)));
m_txtPos->setPos(point.x(), point.y());QChartView::mouseMoveEvent(pEvent);}voidChartDrawer::enterEvent(QEnterEvent *pEvent){
m_txtPos->setVisible(true);
m_xLine->setVisible(true);
m_yLine->setVisible(true);QChartView::leaveEvent(pEvent);}voidChartDrawer::leaveEvent(QEvent *pEvent){
m_txtPos->setVisible(false);
m_xLine->setVisible(false);
m_yLine->setVisible(false);QChartView::leaveEvent(pEvent);}
其他实现请参考具体代码
控件类实现具体代码
ChartDrawer.h文件
#ifndefCHARTDRAWER_H#defineCHARTDRAWER_H#include<QtCharts>#include<QChartView>#include<QWidget>#include<QGraphicsLineItem>#include<QLabel>#include<QGraphicsTextItem>#defineUPDATE_DIS0.001#defineMINOR_TICK_COUNT10#defineTICK_COUNT_MIN5#defineTICK_COUNT_MAX20#defineTICK_DIS_DEFAULT20#defineDEFAULT_X_MIN0#defineDEFAULT_X_MAX10#defineDEFAULT_Y_MIN0#defineDEFAULT_Y_MAX10classChartDrawer:publicQChartView{
Q_OBJECT
public:explicitChartDrawer(QWidget *parent =nullptr);~ChartDrawer();enumAxisType{
AxisType_Static =0,
AxisType_Slide,
AxisType_Dynamic
};
QAbstractSeries::SeriesType getSeriesType();voidaddSeries(QAbstractSeries::SeriesType type = QAbstractSeries::SeriesTypeLine,QString seriesName ="");voidsetSeriesStyle(QString seriesName ="", Qt::GlobalColor color = Qt::red,int width =2);voidsetAxisType(AxisType xType = AxisType_Dynamic);voidsetAxisRange(double xMin,double xMax);voidsetAxisRange(double slideDis);voidsetAxisRange();doublegetXAxisRange();doublegetYAxisRange();voidsetAxisTitle(QString xTitle, QString yTitle);voidsetGridVisible(bool isShow);voidsetLegendVisible(bool isShow);voidsetOriginPointFs(QString seriesName, QList<QPointF>);voidaddPointF(QString seriesName, QPointF);voidclearAllPointF();staticboolcompareX(QPointF a, QPointF b);staticboolcompareY(QPointF a, QPointF b);virtualvoidmousePressEvent(QMouseEvent *pEvent)override;virtualvoidmouseReleaseEvent(QMouseEvent *pEvent)override;virtualvoidwheelEvent(QWheelEvent *pEvent)override;virtualvoidmouseMoveEvent(QMouseEvent *e)override;virtualvoidenterEvent(QEnterEvent *pEvent)override;virtualvoidleaveEvent(QEvent *pEvent)override;
QChart *m_chart;
QValueAxis *m_xAxis;
QValueAxis *m_yAxis;
QGraphicsLineItem *m_xLine;
QGraphicsLineItem *m_yLine;
QGraphicsTextItem *m_txtPos;
QMap<QString, QXYSeries*> m_mapSeries;
QAbstractSeries::SeriesType m_seriesType;
AxisType m_xAxisType = AxisType_Dynamic;double m_xMin, m_xMax, m_yMin, m_yMax;double m_slideDis;bool m_middleButtonPressed =false;
QPoint m_oPrePos;};#endif// CHARTDRAWER_H
ChartDrawer.cpp 文件
#include"chartdrawer.h"ChartDrawer::ChartDrawer(QWidget *parent): QChartView{parent},m_xMin(0.0),m_xMax(10),m_yMin(0.0),m_yMax(10){
m_chart =newQChart();this->setChart(m_chart);this->setRubberBand(QChartView::NoRubberBand);this->setRenderHint(QPainter::Antialiasing);this->setContentsMargins(0,0,0,0);//m_chart->setTheme(QChart::ChartThemeDark);
m_xAxis =new QValueAxis;
m_xAxis->setLabelFormat("%.3f");
m_xAxis->setRange(m_xMin,m_xMax);
m_yAxis =new QValueAxis;
m_yAxis->setLabelFormat("%.3f");
m_yAxis->setRange(m_yMin,m_yMax);
m_chart->addAxis(m_xAxis, Qt::AlignBottom);
m_chart->addAxis(m_yAxis, Qt::AlignLeft);
m_xLine =newQGraphicsLineItem();
m_xLine->setPen(QPen(QColor(100,100,100)));
m_xLine->setZValue(2);
m_yLine =newQGraphicsLineItem();
m_yLine->setPen(QPen(QColor(100,100,100)));
m_yLine->setZValue(2);scene()->addItem(m_xLine);scene()->addItem(m_yLine);
m_txtPos =newQGraphicsTextItem();
m_txtPos->setFont(QFont("宋体",15, QFont::Bold));
m_txtPos->setVisible(false);scene()->addItem(m_txtPos);}ChartDrawer::~ChartDrawer(){deleteLater();delete m_xLine;delete m_yLine;delete m_txtPos;
m_xLine =nullptr;
m_yLine =nullptr;
m_txtPos =nullptr;}
QAbstractSeries::SeriesType ChartDrawer::getSeriesType(){return m_seriesType;}voidChartDrawer::addSeries(QAbstractSeries::SeriesType type,QString seriesName){
m_seriesType = type;
QXYSeries *series;switch(type){case QAbstractSeries::SeriesType::SeriesTypeLine:
series =newQLineSeries(this);break;case QAbstractSeries::SeriesType::SeriesTypeSpline:
series =newQSplineSeries(this);break;case QAbstractSeries::SeriesType::SeriesTypeScatter:
series =newQScatterSeries(this);break;default:break;}
m_chart->addSeries(series);
series->setName(seriesName);
series->attachAxis(m_xAxis);
series->attachAxis(m_yAxis);// 隐藏点标签
series->setPointLabelsVisible(false);
m_mapSeries.insert(seriesName,series);}voidChartDrawer::setSeriesStyle(QString seriesName, Qt::GlobalColor color,int width){if(m_mapSeries.find(seriesName).value()==nullptr){qDebug()<<QStringLiteral("曲线不存在");return;}
QPen splinePen;
splinePen.setBrush(color);
splinePen.setColor(color);
splinePen.setWidth(width);
m_mapSeries[seriesName]->setPen(splinePen);}voidChartDrawer::setAxisType(AxisType xType){
m_xAxisType = xType;}inlineboolChartDrawer::compareX(QPointF a, QPointF b){return a.x()> b.x();}inlineboolChartDrawer::compareY(QPointF a, QPointF b){return a.y()> b.y();}voidChartDrawer::setAxisRange(double xMin,double xMax){
m_xMin = xMin;
m_xMax = xMax;}voidChartDrawer::setAxisRange(double slideDis){
m_slideDis = slideDis;}voidChartDrawer::setAxisRange(){
QVector<QPointF> olddata;foreach(QXYSeries *series, m_mapSeries){
olddata << series->points();}
QVector<QPointF> sortData = olddata;
std::sort(sortData.begin(),sortData.end(),compareY);
m_yMin = sortData.last().y();
m_yMax = sortData.first().y();
m_yAxis->setRange(m_yMin, m_yMax);if(m_xAxisType == AxisType_Static){}else{
std::sort(sortData.begin(),sortData.end(),compareX);
m_xMax = sortData.first().x();if(m_xAxisType == AxisType_Slide){
m_xMin = m_xMax - m_slideDis;}elseif(m_xAxisType == AxisType_Dynamic){
m_xMin = sortData.last().x();}}
m_xAxis->setRange(m_xMin, m_xMax);//qDebug() << m_xMin << m_xMax << m_yMin << m_yMax;}doubleChartDrawer::getXAxisRange(){returndouble(m_xAxis->max()- m_xAxis->min());}doubleChartDrawer::getYAxisRange(){returndouble(m_yAxis->max()- m_yAxis->min());}voidChartDrawer::setAxisTitle(QString xTitle, QString yTitle){
m_xAxis->setTitleText(xTitle);
m_yAxis->setTitleText(yTitle);}voidChartDrawer::setGridVisible(bool isShow){int count =double(m_xAxis->max()- m_xAxis->min())/20;if(count < TICK_COUNT_MIN){
count = TICK_COUNT_MIN;}elseif(count > TICK_COUNT_MAX){
count = TICK_COUNT_MAX;}
m_xAxis->setGridLineVisible(isShow);
m_xAxis->setTickCount(count);
m_xAxis->setMinorTickCount(MINOR_TICK_COUNT);
count =double(m_yAxis->max()- m_yAxis->min())/20;if(count < TICK_COUNT_MIN){
count = TICK_COUNT_MIN;}elseif(count > TICK_COUNT_MAX){
count = TICK_COUNT_MAX;}
m_yAxis->setGridLineVisible(isShow);
m_yAxis->setTickCount(count);
m_yAxis->setMinorTickCount(MINOR_TICK_COUNT);}voidChartDrawer::setLegendVisible(bool isShow){
m_chart->legend()->setVisible(isShow);/*m_chart->legend()->setLayoutDirection(Qt::LeftToRight);
m_chart->legend()->setAlignment(Qt::AlignBottom);*/}voidChartDrawer::setOriginPointFs(QString seriesName, QList<QPointF> data){if(m_mapSeries.find(seriesName).value()==nullptr){qDebug()<<QStringLiteral("曲线不存在");return;}
m_mapSeries[seriesName]->append(data);}voidChartDrawer::addPointF(QString seriesName, QPointF pointf){if(m_mapSeries.find(seriesName).value()==nullptr){qDebug()<<QStringLiteral("曲线不存在");return;}//qDebug() << pointf;
QVector<QPointF> olddata = m_mapSeries[seriesName]->points();if(olddata.size()>0){double xdis =abs(olddata[olddata.size()-1].x()- pointf.x());double ydis =abs(olddata[olddata.size()-1].y()- pointf.y());//qDebug() << "x:" << xdis << "," << "y:" << ydis;if(xdis < UPDATE_DIS && ydis < UPDATE_DIS)return;}
olddata.append(pointf);
m_mapSeries[seriesName]->replace(olddata);setAxisRange();}voidChartDrawer::clearAllPointF(){foreach(QXYSeries *series, m_mapSeries){
series->replace(QVector<QPointF>());}}voidChartDrawer::mousePressEvent(QMouseEvent *pEvent){if(pEvent->button()== Qt::MiddleButton){
m_middleButtonPressed =true;
m_oPrePos = pEvent->pos();this->setCursor(Qt::OpenHandCursor);}elseif(pEvent->button()== Qt::RightButton){setAxisRange();}QChartView::mousePressEvent(pEvent);}voidChartDrawer::mouseReleaseEvent(QMouseEvent *pEvent){if(pEvent->button()== Qt::MiddleButton){
m_middleButtonPressed =false;this->setCursor(Qt::ArrowCursor);}QChartView::mouseReleaseEvent(pEvent);}voidChartDrawer::wheelEvent(QWheelEvent *pEvent){
QPointF point = pEvent->position();double rVal = std::pow(0.999, pEvent->angleDelta().y());
QPointF chartPoint = m_chart->mapToValue(point);//计算当前点到最小点和最大点的距离,x轴和y轴double oldLeftDis = chartPoint.x()- m_xAxis->min();double oldRightDis = m_xAxis->max()- chartPoint.x();double oldDownDis = chartPoint.y()- m_yAxis->min();double oldUpDis = m_yAxis->max()- chartPoint.y();//计算当前点到缩放后的最小点和最大点的距离,x轴和y轴double newLeftDis = oldLeftDis * rVal;double newRightDis = oldRightDis * rVal;double newDownDis = oldDownDis * rVal;double newUpDis = oldUpDis * rVal;//计算新的坐标最小点和最大点double newXMin = chartPoint.x()- newLeftDis;double newXMax = chartPoint.x()+ newRightDis;double newYMin = chartPoint.y()- newDownDis;double newYMax = chartPoint.y()+ newUpDis;//重新设置坐标轴的显示范围
m_xAxis->setRange(newXMin, newXMax);
m_yAxis->setRange(newYMin, newYMax);QChartView::wheelEvent(pEvent);}voidChartDrawer::mouseMoveEvent(QMouseEvent *pEvent){
QPoint point = pEvent->pos();if(m_middleButtonPressed){
QPoint oDeltaPos = point - m_oPrePos;
m_chart->scroll(-oDeltaPos.x(), oDeltaPos.y());
m_oPrePos = point;}
m_xLine->setLine(point.x(),0,point.x(),this->height());
m_yLine->setLine(0,point.y(),this->width(),point.y());
QPointF chartPoint = m_chart->mapToValue(point);
m_txtPos->setPlainText(QString("%1,%2").arg(QString::number(chartPoint.x(),'f',3),QString::number(chartPoint.y(),'f',3)));
m_txtPos->setPos(point.x(), point.y());QChartView::mouseMoveEvent(pEvent);}voidChartDrawer::enterEvent(QEnterEvent *pEvent){
m_txtPos->setVisible(true);
m_xLine->setVisible(true);
m_yLine->setVisible(true);QChartView::leaveEvent(pEvent);}voidChartDrawer::leaveEvent(QEvent *pEvent){
m_txtPos->setVisible(false);
m_xLine->setVisible(false);
m_yLine->setVisible(false);QChartView::leaveEvent(pEvent);}
控件类的使用
具体使用代码如下
1、初始化类对象,并加入界面布局中
m_chartDrawer =newChartDrawer(this);
vLayout->addWidget(m_chartDrawer);
m_chartDrawer->setAxisTitle("时间/(s)","路程/(m)");
m_chartDrawer->setGridVisible(true);
m_chartDrawer->setLegendVisible(false);// m_chartDrawer->setAxisType(ChartDrawer::AxisType::AxisType_Slide);//m_chartDrawer->setAxisRange(0,10);
m_chartDrawer->addSeries(QAbstractSeries::SeriesTypeLine,"line0");
m_chartDrawer->setSeriesStyle("line0", Qt::red,3);
m_chartDrawer->setOriginPointFs("line0",QList<QPointF>());
m_chartDrawer->addSeries(QAbstractSeries::SeriesTypeSpline,"line1");
m_chartDrawer->setSeriesStyle("line1", Qt::green,3);
m_chartDrawer->setOriginPointFs("line1",QList<QPointF>());
2、添加点到图表控件中
我这边定义了定时器,每隔1s交换的向两个曲线内添加一个点
voidMainWindow::timerEvent(QTimerEvent *event){//qDebug() << "aa";
m_time +=1;srand(QTime(0,0,0).secsTo(QTime::currentTime()));double m_distance =QRandomGenerator::global()->bounded(0,20);if(m_preLine =="line0"){
m_chartDrawer->addPointF("line1",QPointF(m_time,m_distance));
m_preLine ="line1";}else{
m_chartDrawer->addPointF("line0",QPointF(m_time,m_distance));
m_preLine ="line0";}}
3、从控件中移除所有点
m_chartDrawer->clearAllPointF();
如果还是看不懂、建议直接下载源代码
源码链接:https://download.csdn.net/download/xiaohuihuihuige/87264001
有帮助的话请点个赞吧。关注我,获取更多自定义控件
版权归原作者 xiaohuihuihuige 所有, 如有侵权,请联系我们删除。