一、原因
QCustomPlot 默认主要支持基于鼠标的交互操作(如滚轮缩放、拖拽平移等),但本身并不直接支持原生的触摸屏多点触控手势缩放。
二、实现思路
可以利用Qt的QPinchGesture(缩放手势)来检测双指缩放动作,并动态调整坐标轴范围。
三、完整代码示例
#pragma once
#include "qcustomplot.h"
#include <QTimer>
class QCustomPlotEx : public QCustomPlot
{
Q_OBJECT
public:
QCustomPlotEx(QWidget* parent);
~QCustomPlotEx();
protected:
//处理事件
bool event(QEvent* event);
//重写QCustomPlot的鼠标移动事件(当缩放事件触发时,屏蔽当前事件,有效防抖)
void mouseMoveEvent(QMouseEvent* event);
private slots:
//坐标轴变换触发
void onRangeChange(const QCPRange& newRange);
//缩放手势结束触发
void handleZoomEnd();
private:
//处理手势事件
void handlePinchGesture(QPinchGesture* gesture);
//坐标轴缩放
void scaleAxisRanges(qreal factor, const QPointF& center);
private:
QTimer m_zoomEndTimer;
int m_nPichCount;
bool isGesture;
};
#include "QCustomPlotEx.h"
#include <QGesture>
#include <QPinchGesture>
QCustomPlotEx::QCustomPlotEx(QWidget* parent)
: QCustomPlot(parent)
{
grabGesture(Qt::PinchGesture); //启用手势识别
setAttribute(Qt::WA_AcceptTouchEvents); //启用触摸事件
isGesture = false;
m_zoomEndTimer.setSingleShot(true);
connect(&m_zoomEndTimer, &QTimer::timeout, this, &QCustomPlotEx::handleZoomEnd);
connect(xAxis, SIGNAL(rangeChanged(const QCPRange&)), this, SLOT(onRangeChange
(const QCPRange&)));
connect(yAxis, SIGNAL(rangeChanged(const QCPRange&)), this, SLOT(onRangeChange
(const QCPRange&)));
}
QCustomPlotEx::~QCustomPlotEx()
{
}
//处理事件
bool QCustomPlotEx::event(QEvent* event)
{
if (event->type() == QEvent::Gesture)
{
//检查是否允许用户通过鼠标滚轮缩放
if (interactions().testFlag(QCP::iRangeZoom))
{
QGestureEvent* gestureEvent = static_cast<QGestureEvent*>(event);
QGesture* pinch = gestureEvent->gesture(Qt::PinchGesture);
if (pinch)
{
handlePinchGesture(static_cast<QPinchGesture*>(pinch));
return true;
}
}
}
return QCustomPlot::event(event);
}
//重写QCustomPlot的鼠标移动事件
void QCustomPlotEx::mouseMoveEvent(QMouseEvent* event)
{
//当双指缩放时,屏蔽鼠标移动事件
if (isGesture)
{
return;
}
QCustomPlot::mouseMoveEvent(event);
}
//坐标轴变换触发
void QCustomPlotEx::onRangeChange(const QCPRange& newRange)
{
m_zoomEndTimer.start(200); //重置定时器
}
//缩放手势结束触发
void QCustomPlotEx::handleZoomEnd()
{
isGesture = false;
}
//处理手势事件
void QCustomPlotEx::handlePinchGesture(QPinchGesture* gesture)
{
if (!gesture)
{
return;
}
QPinchGesture::ChangeFlags changeFlags = gesture->changeFlags();
if (changeFlags & QPinchGesture::ScaleFactorChanged)
{
//获取缩放中心和缩放因子
QPointF center = gesture->centerPoint();
qreal scaleFactor = gesture->totalScaleFactor();
//防抖操作,接受超过5次缩放事件,再缩放一次
if (gesture->state() == Qt::GestureStarted)
{
m_nPichCount = 0;
isGesture = true;
}
else if (gesture->state() == Qt::GestureUpdated)
{
if (scaleFactor > 1)
m_nPichCount++;
else
m_nPichCount--;
}
else
{
isGesture = false;
}
if (m_nPichCount >= 5 || m_nPichCount <= -5)
{
//将屏幕坐标转换为QCustomPlot坐标
QPointF plotCenter = mapFromGlobal(center.toPoint());
//坐标轴范围缩放
scaleAxisRanges(scaleFactor, plotCenter);
m_nPichCount = 0;
}
}
}
//坐标轴缩放
void QCustomPlotEx::scaleAxisRanges(qreal factor, const QPointF& center)
{
factor = 1.0 / factor;
double axisCenter_x = xAxis->pixelToCoord(center.x());
xAxis->scaleRange(factor, axisCenter_x);
double axisCenter_y = yAxis->pixelToCoord(center.y());
yAxis->scaleRange(factor, axisCenter_y);
replot();
}
四、关键参数
(1)scaleFactor
大于1.0 表示双指张开(放大)。
小于1.0 表示双指捏合(缩小)。
(2)center:
手势中心点的屏幕坐标,需转换为QCustomPlot的轴坐标,使缩放围绕该点进行。