【[Qt]基于QChartView开发的图表显示控件,支持实时显示,动态更新,支持鼠标交互等操作】

[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 = new QGraphicsLineItem();
    m_xLine->setPen(QPen(QColor( 100, 100, 100 )));
    m_xLine->setZValue(2);

    m_yLine = new QGraphicsLineItem();
    m_yLine->setPen(QPen(QColor( 100, 100, 100 )));
    m_yLine->setZValue(2);

    scene()->addItem(m_xLine);
    scene()->addItem(m_yLine);

    m_txtPos = new QGraphicsTextItem();
    m_txtPos->setFont(QFont("宋体", 15, QFont::Bold));
    m_txtPos->setVisible(false);
    scene()->addItem(m_txtPos);

然后定义鼠标事件,在鼠标进入时显示,移出时隐藏,移动时显示。

void ChartDrawer::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);
}
void ChartDrawer::enterEvent(QEnterEvent *pEvent)
{
    
    
    m_txtPos->setVisible(true);
    m_xLine->setVisible(true);
    m_yLine->setVisible(true);
    QChartView::leaveEvent(pEvent);
}

void ChartDrawer::leaveEvent(QEvent *pEvent)
{
    
    
    m_txtPos->setVisible(false);
    m_xLine->setVisible(false);
    m_yLine->setVisible(false);
    QChartView::leaveEvent(pEvent);
}

其他实现请参考具体代码

控件类实现具体代码

ChartDrawer.h文件

#ifndef CHARTDRAWER_H
#define CHARTDRAWER_H

#include <QtCharts>
#include <QChartView>
#include <QWidget>
#include <QGraphicsLineItem>
#include <QLabel>
#include <QGraphicsTextItem>

#define UPDATE_DIS 0.001
#define MINOR_TICK_COUNT 10
#define TICK_COUNT_MIN 5
#define TICK_COUNT_MAX 20
#define TICK_DIS_DEFAULT 20
#define DEFAULT_X_MIN 0
#define DEFAULT_X_MAX 10
#define DEFAULT_Y_MIN 0
#define DEFAULT_Y_MAX 10

class ChartDrawer : public QChartView
{
    
    
    Q_OBJECT
public:
    explicit ChartDrawer(QWidget *parent = nullptr);
    ~ChartDrawer();

    enum AxisType{
    
    
        AxisType_Static = 0,
        AxisType_Slide,
        AxisType_Dynamic
    };

    QAbstractSeries::SeriesType getSeriesType();
    void addSeries(QAbstractSeries::SeriesType type = QAbstractSeries::SeriesTypeLine,QString seriesName = "");
    void setSeriesStyle(QString seriesName = "", Qt::GlobalColor color = Qt::red, int width = 2);
    void setAxisType(AxisType xType = AxisType_Dynamic);
    void setAxisRange(double xMin, double xMax);
    void setAxisRange(double slideDis);
    void setAxisRange();
    double getXAxisRange();
    double getYAxisRange();
    void setAxisTitle(QString xTitle, QString yTitle);
    void setGridVisible(bool isShow);
    void setLegendVisible(bool isShow);
    void setOriginPointFs(QString seriesName, QList<QPointF>);
    void addPointF(QString seriesName, QPointF);
    void clearAllPointF();
    static bool compareX(QPointF a, QPointF b);
    static bool compareY(QPointF a, QPointF b);


    virtual void mousePressEvent(QMouseEvent *pEvent) override;
    virtual void mouseReleaseEvent(QMouseEvent *pEvent) override;
    virtual void wheelEvent(QWheelEvent *pEvent) override;
    virtual void mouseMoveEvent(QMouseEvent *e) override;
    virtual void enterEvent(QEnterEvent *pEvent) override;
    virtual void leaveEvent(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 = new QChart();
    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 = new QGraphicsLineItem();
    m_xLine->setPen(QPen(QColor( 100, 100, 100 )));
    m_xLine->setZValue(2);

    m_yLine = new QGraphicsLineItem();
    m_yLine->setPen(QPen(QColor( 100, 100, 100 )));
    m_yLine->setZValue(2);

    scene()->addItem(m_xLine);
    scene()->addItem(m_yLine);

    m_txtPos = new QGraphicsTextItem();
    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;
}
void ChartDrawer::addSeries(QAbstractSeries::SeriesType type,QString seriesName)
{
    
    
    m_seriesType = type;
    QXYSeries *series;
    switch(type)
    {
    
    
    case QAbstractSeries::SeriesType::SeriesTypeLine:
        series = new QLineSeries(this);
        break;
    case QAbstractSeries::SeriesType::SeriesTypeSpline:
        series = new QSplineSeries(this);
        break;
    case QAbstractSeries::SeriesType::SeriesTypeScatter:
        series = new QScatterSeries(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);
}
void ChartDrawer::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);
}

void ChartDrawer::setAxisType(AxisType xType)
{
    
    
    m_xAxisType = xType;
}

inline bool ChartDrawer::compareX(QPointF a, QPointF b)
{
    
    
    return a.x() > b.x();
}
inline bool ChartDrawer::compareY(QPointF a, QPointF b)
{
    
    
    return a.y() > b.y();
}

void ChartDrawer::setAxisRange(double xMin, double xMax)
{
    
    
    m_xMin = xMin;
    m_xMax = xMax;
}
void ChartDrawer::setAxisRange(double slideDis)
{
    
    
    m_slideDis = slideDis;
}
void ChartDrawer::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;
        } else if(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;
}
double ChartDrawer::getXAxisRange()
{
    
    
    return double(m_xAxis->max() - m_xAxis->min());
}
double ChartDrawer::getYAxisRange()
{
    
    
    return double(m_yAxis->max() - m_yAxis->min());
}
void ChartDrawer::setAxisTitle(QString xTitle, QString yTitle)
{
    
    
    m_xAxis->setTitleText(xTitle);
    m_yAxis->setTitleText(yTitle);
}

void ChartDrawer::setGridVisible(bool isShow)
{
    
    
    int count = double(m_xAxis->max() - m_xAxis->min()) / 20;
    if(count < TICK_COUNT_MIN){
    
    
        count = TICK_COUNT_MIN;
    } else if(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;
    } else if(count > TICK_COUNT_MAX){
    
    
        count = TICK_COUNT_MAX;
    }
    m_yAxis->setGridLineVisible(isShow);
    m_yAxis->setTickCount(count);
    m_yAxis->setMinorTickCount(MINOR_TICK_COUNT);
}

void ChartDrawer::setLegendVisible(bool isShow)
{
    
    
    m_chart->legend()->setVisible(isShow);
    /*m_chart->legend()->setLayoutDirection(Qt::LeftToRight);
    m_chart->legend()->setAlignment(Qt::AlignBottom);*/
}
void ChartDrawer::setOriginPointFs(QString seriesName, QList<QPointF> data)
{
    
    
    if(m_mapSeries.find(seriesName).value() == nullptr){
    
    
        qDebug() << QStringLiteral("曲线不存在");
        return;
    }
    m_mapSeries[seriesName]->append(data);
}
void ChartDrawer::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();
}
void ChartDrawer::clearAllPointF()
{
    
    
    foreach(QXYSeries *series, m_mapSeries){
    
    
        series->replace(QVector<QPointF>());
    }
}
void ChartDrawer::mousePressEvent(QMouseEvent *pEvent)
{
    
    
    if (pEvent->button() == Qt::MiddleButton) {
    
    
        m_middleButtonPressed = true;
        m_oPrePos = pEvent->pos();
        this->setCursor(Qt::OpenHandCursor);
    } else if(pEvent->button() == Qt::RightButton) {
    
    
        setAxisRange();
    }
    QChartView::mousePressEvent(pEvent);
}
void ChartDrawer::mouseReleaseEvent(QMouseEvent *pEvent)
{
    
    
    if (pEvent->button() == Qt::MiddleButton) {
    
    
        m_middleButtonPressed = false;
        this->setCursor(Qt::ArrowCursor);
    }
    QChartView::mouseReleaseEvent(pEvent);
}
void ChartDrawer::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);
}
void ChartDrawer::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);
}
void ChartDrawer::enterEvent(QEnterEvent *pEvent)
{
    
    
    m_txtPos->setVisible(true);
    m_xLine->setVisible(true);
    m_yLine->setVisible(true);
    QChartView::leaveEvent(pEvent);
}

void ChartDrawer::leaveEvent(QEvent *pEvent)
{
    
    
    m_txtPos->setVisible(false);
    m_xLine->setVisible(false);
    m_yLine->setVisible(false);
    QChartView::leaveEvent(pEvent);
}

控件类的使用

具体使用代码如下
1、初始化类对象,并加入界面布局中

	m_chartDrawer = new ChartDrawer(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交换的向两个曲线内添加一个点

扫描二维码关注公众号,回复: 15787565 查看本文章
void MainWindow::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

有帮助的话请点个赞吧。关注我,获取更多自定义控件

猜你喜欢

转载自blog.csdn.net/xiaohuihuihuige/article/details/128265056