【Qt】拖拽曲线

   许多时候,我们都会用图表显示数据,便于清晰直观的呈现数据走势和变化。某些环境下,更希望能够在图表上拖拽曲线,达到修改数据的目的。之前,使用海思PQ调试工具时,发现gamma模块的曲线拖拽功能做的很好,便用Qt做了一个曲线拖拽的demo(如图1)。一来可以了解海思工具中拖拽曲线的实现方式,二来可以加深对Qt图表操作的理解。
Alt

图1 整体效果示意图

该demo具备以下功能:

  • 鼠标在图表中移动时,呈现十字光标状态,并显示鼠标当前位置的坐标值。
  • 鼠标为十字光标时,按下左键,选择矩形区域进行放大。单击鼠标右键恢复图表大小(如图2所示)。
  • 鼠标移动到数据点附近,光标切换为垂直调整光标,并将数据点的坐标显示在右上角的X,Y编辑框中。
  • 鼠标为垂直调整光标时,按下左键,可拖动数据点,调整数据点的纵坐标(如图3所示)。
  • 捕获到数据点时,可在Y编辑框中编辑该数据点的纵坐标,点击set按钮后生效(如图4所示)。
  • 选中“Force Correctness on Drag”时,当前点的纵坐标yn会被限制在[yn-1,yn+1]范围内。
  • 改变Curve Type的类型,在图表上显示不同类型的曲线(如图5所示)。

   为了能够实现鼠标拖动曲线的功能,需要在QChartView组件中对鼠标事件进行处理。因此需要自定义一个QChartView继承的类,实现鼠标拖动功能。在文中通过自定义CurveChartView类处理鼠标操作,其定义如下。

#ifndef _CURVECHARTVIEW_H
#define _CURVECHARTVIEW_H

#include <QtCharts/QChartView>
#include <QtCharts/QScatterSeries>
#include <QtCharts/QSplineSeries>
#include <QtCore/QtMath>

QT_CHARTS_USE_NAMESPACE

class CurveChartView : public QChartView
{
	Q_OBJECT

public:
	CurveChartView(QWidget *parent = Q_NULLPTR);
	~CurveChartView();

	void setForceCorrectnessOnDrag(bool status);			// 限制拖动点范围
	void setSeriesIndex(int index);							// 更改curve type类型
	void setCurrentPointValue(const QPointF &value);		// 修改当前坐标数据
	void setSeriesList(QList<QScatterSeries *> sList, 
		QList<QSplineSeries *> lList);						// 设置曲线数据
	void setYRange(int minVal, int maxVal);					// Y轴范围

protected:
	void mousePressEvent(QMouseEvent *event);
	void mouseMoveEvent(QMouseEvent *event);
	void mouseReleaseEvent(QMouseEvent *event);

private:
	bool forceCorrectnessFlag;		// 拖动范围限制标志
	bool dragPointFlag;				// 拖动数据点标志
	QPoint beginPoint;				// 选择矩形区域的起点
	QPoint endPoint;				// 选择矩形区域的重点
	int pointIndex;					// 当前数据点序号
	int seriesIndex;				// 当前曲线类型
	int dataCount;					// 数据点个数
	int minValue;					// Y轴最小值
	int maxValue;					// Y轴最大值

	QList<QScatterSeries *> scatterList;	// 散点序列集合
	QList<QSplineSeries *> lineList;		// 曲线序列集合

	qreal distance(const QPointF &p1, const QPointF &p2);	// 计算距离
	void detectDragPoint(const QPointF &point);				// 捕获待拖动的数据点
	void updateSeriesData(const QPointF &point);			// 更新数据点数据

signals:
	void signalMouseMovePoint(QPoint point);		// 鼠标移动信号
	void signalCurrentDragPoint(QPointF point);		// 数据点拖动信号

};

#endif

   在设计UI界面时,按照图1所示效果进行布局,并将用于显示图表的Widget控件提升为CurveChartView类。
   在图表范围内,鼠标默认采用十字光标。此状态按下鼠标,会将拖拽模式设置为橡皮筋模式,并记录坐标;释放鼠标,则会记录鼠标释放坐标,放大预览。当点击鼠标右键时,可将预览恢复到初始状态。
   在十字光标状态下,移动鼠标靠近数据点时,鼠标会被数据点捕获,由十字光标切换为垂直调整光标状态。在垂直调整光标状态下,拖动数据点,即可改变当前数据点的值。

/* 按下鼠标 */
void CurveChartView::mousePressEvent(QMouseEvent *event)
{
	QCursor currentCusor = this->cursor();
	if (event->button() == Qt::LeftButton)			// 左键按下
	{
		// 十字光标状态
		if (currentCusor.shape() == Qt::CrossCursor)
		{
			this->setDragMode(QGraphicsView::RubberBandDrag);
			beginPoint = event->pos();
		}
		else		// 垂直调整光标
		{
			this->setDragMode(QGraphicsView::NoDrag);
			dragPointFlag = true;
		}
	}
	QChartView::mousePressEvent(event);
}

/* 移动鼠标 */
void CurveChartView::mouseMoveEvent(QMouseEvent *event)
{
	QPoint point = event->pos();
	emit signalMouseMovePoint(point);
	if (dragPointFlag)		// 检测是否正在拖点
	{
		this->setCursor(Qt::SizeVerCursor);
		updateSeriesData(point);	// 更新拖动点数值
	}
	else
	{
		this->setCursor(Qt::CrossCursor);
		detectDragPoint(point);		// 捕获拖动点
	}

	QChartView::mouseMoveEvent(event);
}

/* 释放鼠标 */
void CurveChartView::mouseReleaseEvent(QMouseEvent *event)
{
	QCursor currentCusor = this->cursor();
	if (event->button() == Qt::LeftButton)
	{
		if (currentCusor.shape() == Qt::CrossCursor)
		{	// 左键释放,十字光标,获取选择矩形终点,缩放图像
			endPoint = event->pos();
			QRectF rectF;
			rectF.setTopLeft(this->beginPoint);
			rectF.setBottomRight(this->endPoint);
			this->chart()->zoomIn(rectF);
		}
		else
		{
			dragPointFlag = false;
		}
	}
	else if (event->button() == Qt::RightButton)
	{	// 右键释放,恢复图表大小
		this->chart()->zoomReset();
	}
	QChartView::mouseReleaseEvent(event);
}

   鼠标移动时,会计算鼠标所在位置与曲线数据之间的距离,当某个数据点与鼠标所在位置的距离小于设定的阈值时,便会将距离最近的数据点记为可拖动的数据点。

/*
	描述: 捕获拖动点
*/
void CurveChartView::detectDragPoint(const QPointF &point)
{
	// check data
	if (scatterList.isEmpty() || lineList.isEmpty())
		return;

	// detect drag point
	QPointF curPoint = this->chart()->mapToValue(point);
	QScatterSeries *curSeries = scatterList.at(seriesIndex);
	QVector<QPointF> seriesData = curSeries->pointsVector();
	for (int i = 0; i < seriesData.count(); i++)
	{
		if (distance(curPoint, seriesData.at(i)) <= 1)		// 距离检测
		{
			pointIndex = i;
			emit signalCurrentDragPoint(seriesData.at(i));	// 标记拖动点
			this->setCursor(Qt::SizeVerCursor);				// 切换光标状态
			break;
		}
	}
}

效果示意:

Alt

图2 选择放大示意图

Alt

图3 拖拽数据点示意图

Alt

图4 修改数据示意图

Aly

图5 更改曲线类型示意图

说明:
   上述功能,采用VS2013+Qt5.8环境编译,且验证通过。由于代码较多,在博客中贴全部源码会显得冗余,且占篇幅。故将完整代码上传GitHub,其地址为:https://github.com/ShrekLi/Programing/tree/master/Qt_OperateCurve

参考文献:
   [1] 王维波,2018. Qt 5.9 C++开发指南[M]. 北京:人民邮电出版社

个人声明:
   以上内容,纯属个人观点,不喜勿喷。未经本人同意,不得私自转载。博客中出现的代码仅供学习参考,不得有其他用途。若文中存在纰漏,或读者有更好的建议,欢迎留言探讨。也可邮箱联系:yxyx_0212@163.com

  • 15
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: Qt QChart是一个用于绘制图表的模块,可以用于绘制各种类型的图表,包括曲线图。要实现曲线动,可以按照以下步骤进行操作: 1. 创建一个QChart对象,并添加一个QLineSeries对象作为曲线的数据源。通过添加点坐标的方式,将数据添加到QLineSeries中。 2. 创建一个QChartView对象,并将QChart对象设置为其图表。这样可以将图表显示在窗口上。 3. 为QChartView对象添加一个QScatterSeries对象作为鼠标动的曲线。该QScatterSeries对象中的数据为空。 4. 重写QChartView对象的mousePressEvent和mouseMoveEvent事件处理函数,在鼠标按下和鼠标移动时获取鼠标的坐标。 5. 在mousePressEvent事件处理函数中,判断鼠标是否在曲线上。可以通过调用QChart对象的mapToValue函数将鼠标坐标转换为图表上的坐标,然后判断该坐标是否在QLineSeries中。 6. 在mouseMoveEvent事件处理函数中,判断之前鼠标是否按下,若是,则将动的鼠标坐标同样转换为图表上的坐标,并更新QScatterSeries对象的数据。这样可以实现曲线动效果。 7. 最后,将QChartView对象添加到窗口中,并显示窗口。 通过以上步骤,可以实现Qt QChart曲线动效果。 ### 回答2: 在Qt QChart中,要实现曲线动效果,可以通过以下步骤进行操作: 首先,创建一个QT图表视图,并将其设置为可动模式。可以通过设置setRubberBand()函数来实现这一点,例如: chartView->setRubberBand(QChartView::RectangleRubberBand); 其次,创建曲线数据并将其添加到图表中。可以使用QLineSeries类来表示曲线,例如: QLineSeries *series = new QLineSeries(); series->append(0, 0); series->append(1, 1); series->append(2, 2); ... chart->addSeries(series); 然后,将图表视图与图表进行绑定,以实现动效果: chartView->setChart(chart); 最后,通过实现图表视图的mousePressEvent()、mouseMoveEvent()和mouseReleaseEvent()函数,可以跟踪鼠标事件并更新曲线的位置。例如,在mouseMoveEvent()函数中,可以根据鼠标的移动距离来计算并更新曲线的坐标,实现曲线动效果: void ChartView::mouseMoveEvent(QMouseEvent *event) { if (event->buttons() & Qt::LeftButton) { QPoint diff = event->pos() - m_lastMousePos; chart()->scroll(diff.x(), diff.y()); } m_lastMousePos = event->pos(); } 这样,当鼠标在图表视图上按下并动时,就可以实现曲线动效果了。注意,在此过程中,m_lastMousePos用于记录鼠标上一次的位置,以便计算鼠标移动的距离。 以上是使用Qt QChart实现曲线动的基本步骤,希望对你有帮助。如需更多详细信息,请参考Qt官方文档或相关教程。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值