QCustomPlot在触摸屏上实现双指触控手势缩放

一、原因

        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的轴坐标,使缩放围绕该点进行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大笨象、小笨熊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值