这是一个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
#include
#include
#include
#include
#include
#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交换的向两个曲线内添加一个点
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
有帮助的话请点个赞吧。关注我,获取更多自定义控件