Qt实现手势缩放和平移(QGestureEvent)

        最近项目中遇到一个预览图片的需求,同时能够对手势进行处理,可以处理手势的放大、缩小和平移,其中移动可以通过重写鼠标事件实现。而手势事件之前则没有处理过。查阅资料发现Qt自带了一个手势示例,如下图所示,通过阅读代码发现可以通过QGestureEvent事件实现。具体代码如下。

#pragma once

#include "ui_testGesture.h"

#include <QWidget>
#include <QGesture>
#include <QPanGesture>
#include <QPinchGesture>
#include <QGestureEvent>

class testGesture : public QWidget
{
    Q_OBJECT

public:
    testGesture(QWidget *parent = nullptr);
    ~testGesture();

    /**
     * brief:注册所关注的手势事件.
     * 
     * \param gestures:需要关注的事件
     */
    void grabGestures(const QList<Qt::GestureType>& gestures);

protected:
    /**
     * brief:重写paintevent。这是实现的核心,通过修改绘制的中心坐标实现图像根据手势的移动.
     * 
     * \param event
     */
    void paintEvent(QPaintEvent* event)override;
    /**
     * brief:尺寸变化.
     * 
     * \param event
     */
    void resizeEvent(QResizeEvent* event)override;
    /**
     * brief:双击事件,双击后所有的缩放移动等均失效,恢复到初始状态.
     * 
     * \param event
     */
    void mouseDoubleClickEvent(QMouseEvent* event)override;
    /**
     * brief:按下事件,任务需要移动了,不再处理手势.
     * 
     * \param event
     */
    void mousePressEvent(QMouseEvent* event)override;
    /**
     * brief:按下并移动的处理.
     * 
     * \param event
     */
    void mouseMoveEvent(QMouseEvent* event)override;
    /**
     * brief:.
     * 
     * \param event
     */
    void mouseReleaseEvent(QMouseEvent* event)override;
    /**
     * brief:事件过滤处理.
     * 
     * \param event
     * \return 
     */
    bool event(QEvent* event)override;

private:
    bool gestureEvent(QGestureEvent* event);
    void panTriggered(QPanGesture* gesture);
    void pinchTriggered(QPinchGesture* gesture);
    void swipeTriggered(QSwipeGesture* gesture);

private:
    Ui::testGestureClass ui;
    qreal m_horizontalOffset{ 0 };    //水平方向偏移量
    qreal m_verticalOffset{ 0 };      //垂直方向偏移量
    qreal m_rotationAngle{ 0 };       //旋转角度
    qreal m_scaleFactor{ 1 };         //缩放比例
    qreal m_currentStepScaleFactor{ 1 };   //一次手势之后缩放的比例
    QPoint m_lastPoint{ 0,0 };          //上次点击位置

    bool m_bIsPressed{ false };         //是否有过点击事件
};

这里最核心的处理便是paintevent的重写和pinchTriggered事件的处理函数。在pinchTriggered处理函数中根据手势的类型,将缩放的比例以及旋转的比例记录下来,结束后则计算此次总的比例并记录在成员变量中。而在paintevent中则调用tanslate函数,依照缩放,旋转值重新计算绘图的位置,从而实现了缩放,旋转。至于panTriggered和swipeTriggered事件因需求中不关注,便未做处理,可根据自身需要进行响应的处理。

#include "testGesture.h"

#include <QDebug>
#include <QPainter>

testGesture::testGesture(QWidget *parent)
	: QWidget(parent)
{
	ui.setupUi(this);
	setFixedSize({ 1920,1080 });
	grabGestures({});
}

testGesture::~testGesture()
{}

void testGesture::grabGestures(const QList<Qt::GestureType>&gestures)
{
	QList<Qt::GestureType> gesturess;
	//关注移动,缩放和扫动手势
	gesturess << Qt::PanGesture;
	gesturess << Qt::PinchGesture;
	gesturess << Qt::SwipeGesture;
	foreach(Qt::GestureType gesture, gesturess)
		grabGesture(gesture);
}

void testGesture::paintEvent(QPaintEvent* event)
{
	QPainter p(this);
	QPixmap pix("E:/1.jpg");

	const qreal iw = pix.width();
	const qreal ih = pix.height();
	const qreal wh = height();
	const qreal ww = width();

	//首先将坐标系移动到整个屏幕的中心
	p.translate(ww / 2, wh / 2);
	//然后依据偏移量,再计算出来新的坐标系中心
	//第二次变换是在上次的基础上进行的计算,也就是translate的所有值是累加的
	p.translate(m_horizontalOffset, m_verticalOffset);
	//qDebug() << "qqqq" << m_horizontalOffset << ":" << m_verticalOffset;
	//p.rotate(m_rotationAngle);
	//对坐标系进行一个比例放缩
	p.scale(m_currentStepScaleFactor * m_scaleFactor, m_currentStepScaleFactor * m_scaleFactor);
	//又进行了一个坐标系中心的变化,减去图片的一半一半,也就是到了图片的左上角
	//上边计算了偏移量,所以就是移动之后的图片需要绘制的位置,缩放也相应的在上边进行了设置
	p.translate(-iw / 2, -ih / 2);
	p.drawPixmap(0, 0, pix);
}

void testGesture::resizeEvent(QResizeEvent* event)
{
	update();
}

void testGesture::mouseDoubleClickEvent(QMouseEvent* event)
{
	//双击后将双击偏移量,缩放比例,旋转角度都设置为初始值
	m_rotationAngle = 0;
	m_scaleFactor = 1;
	m_currentStepScaleFactor = 1;
	m_verticalOffset = 0;
	m_horizontalOffset = 0;
	update();
}

void testGesture::mousePressEvent(QMouseEvent* event)
{
	//鼠标按下后不再处理手势,并记录鼠标按下的位置
	m_bIsPressed = true;
	m_lastPoint = event->pos();
}

void testGesture::mouseMoveEvent(QMouseEvent* event)
{
	//如果是处理移动事件
	if (m_bIsPressed)
	{
		//计算偏移量
		QPointF mouseDelta = event->pos() - m_lastPoint;
		m_lastPoint = event->pos();
		m_horizontalOffset += mouseDelta.x();
		m_verticalOffset += mouseDelta.y();
		update();
	}
}

void testGesture::mouseReleaseEvent(QMouseEvent* event)
{
	//不再处理移动
	m_bIsPressed = false;
}

bool testGesture::event(QEvent* event)
{
	//如果是手势,需要给到手势的分派函数
	if (event->type() == QEvent::Gesture)
	{
		return gestureEvent(static_cast<QGestureEvent*>(event));
	}
	return QWidget::event(event);
}

bool testGesture::gestureEvent(QGestureEvent* event)
{
	//给到对应的处理事件
	if (QGesture* swipe = event->gesture(Qt::SwipeGesture))
		swipeTriggered(static_cast<QSwipeGesture*>(swipe));
	else if (QGesture* pan = event->gesture(Qt::PanGesture))
		panTriggered(static_cast<QPanGesture*>(pan));
	if (QGesture* pinch = event->gesture(Qt::PinchGesture))
		pinchTriggered(static_cast<QPinchGesture*>(pinch));
	return true;
}

void testGesture::panTriggered(QPanGesture* gesture)
{
#ifndef QT_NO_CURSOR
	switch (gesture->state()) {
	case Qt::GestureStarted:
	case Qt::GestureUpdated:
		setCursor(Qt::SizeAllCursor);
		break;
	default:
		setCursor(Qt::ArrowCursor);
	}
#endif
	QPointF delta = gesture->delta();
	//qDebug() << "panTriggered():" << gesture;
	//m_horizontalOffset += delta.x();
	//m_verticalOffset += delta.y();
	update();
}

void testGesture::pinchTriggered(QPinchGesture* gesture)
{
	QPinchGesture::ChangeFlags changeFlags = gesture->changeFlags();
	//旋转角度的变化记录
	if (changeFlags & QPinchGesture::RotationAngleChanged) 
	{
		qreal rotationDelta = gesture->rotationAngle() - gesture->lastRotationAngle();
		m_rotationAngle += rotationDelta;
		qDebug() << "pinchTriggered(): rotate by" <<
			rotationDelta << "->" << m_rotationAngle;
	}
	//缩放比例的变化记录
	if (changeFlags & QPinchGesture::ScaleFactorChanged) 
	{
		m_currentStepScaleFactor = gesture->totalScaleFactor();
		qDebug() << "pinchTriggered(): zoom by" <<
			gesture->scaleFactor() << "->" << m_currentStepScaleFactor;
	}
	//手势结束后将锁防值累乘
	if (gesture->state() == Qt::GestureFinished) 
	{
		qDebug() << "finished::" << m_currentStepScaleFactor;
		m_scaleFactor *= m_currentStepScaleFactor;
		m_currentStepScaleFactor = 1;
	}
	update();
}

void testGesture::swipeTriggered(QSwipeGesture* gesture)
{
	if (gesture->state() == Qt::GestureFinished) {
		if (gesture->horizontalDirection() == QSwipeGesture::Left
			|| gesture->verticalDirection() == QSwipeGesture::Up) {
			//qDebug() << "swipeTriggered(): swipe to previous";
			//goPrevImage();
		}
		else {
			//qDebug() << "swipeTriggered(): swipe to next";
			//goNextImage();
		}
		update();
	}
}

### QT实现触摸屏滑动手势的功能 在 Qt 中,`QScroller` 提供了一种简单的方式来启用基于手势的滚动功能。通过设置 `QScroller` 的属性以及绑定到特定的手势类型,可以轻松实现触摸屏上的滑动效果。 以下是详细的说明示例代码: #### 使用 QScroller 启用手势滚动 为了使控件响应触摸手势并执行滚动操作,需要配置 `QScroller` 其关联的手势识别器。下面是一个完整的例子,展示如何在一个自定义窗口中启用手势滚动[^1]。 ```cpp #include <QApplication> #include <QWidget> #include <QVBoxLayout> #include <QScrollArea> #include <QLabel> #include <QScroller> class TouchScrollWidget : public QWidget { public: TouchScrollWidget(QWidget *parent = nullptr) : QWidget(parent) { QVBoxLayout *layout = new QVBoxLayout(this); QLabel *label = new QLabel("这是一个用于测试触控滑动的大标签", this); label->setAlignment(Qt::AlignCenter); QFont font = label->font(); font.setPointSize(50); // 增大字体以便更容易看到滚动效果 label->setFont(font); QScrollArea *scrollArea = new QScrollArea; scrollArea->setWidget(label); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); layout->addWidget(scrollArea); // 初始化 QScroller 并将其附加到 ScrollArea 上 QScroller::grabGesture(scrollArea, QScroller::TouchGesture); } }; int main(int argc, char *argv[]) { QApplication app(argc, argv); TouchScrollWidget window; window.resize(800, 600); window.show(); return app.exec(); } ``` 上述代码创建了一个简单的应用程序界面,在该界面上有一个可滚动区域 (`QScrollArea`),并通过调用 `QScroller::grabGesture()` 方法为其绑定了触摸手势支持。 #### 手势缩放平移的支持 如果还需要支持手势缩放平移,则可以通过监听 `QGestureEvent` 来捕获这些动作。以下是如何扩展上面的例子来加入手势缩放平移功能[^2]。 ```cpp #include <QApplication> #include <QWidget> #include <QVBoxLayout> #include <QLabel> #include <QPinchGestureRecognizer> #include <QGraphicsView> #include <QGraphicsScene> #include <QTransform> class GestureImageView : public QGraphicsView { protected: bool event(QEvent *event) override { if (event->type() == QEvent::Gesture) { return gestureEvent(static_cast<QGestureEvent *>(event)); } return QGraphicsView::event(event); } bool gestureEvent(QGestureEvent *event) { if (QGesture *gesture = event->gesture(Qt::PinchGesture)) { handlePinch(gesture); } else if (QGesture *gesture = event->gesture(Qt::PanGesture)) { handlePan(gesture); } return true; } private: void handlePinch(QGesture *gesture) { auto pinch = static_cast<QPinchGesture *>(gesture); if (pinch->state() == Qt::GestureStarted || pinch->state() == Qt::GestureUpdated) { qreal scale = pinch->scaleFactor(); setTransformationAnchor(QGraphicsView::NoAnchor); scale(scale, scale); } } void handlePan(QGesture *gesture) { auto pan = static_cast<QPanGesture *>(gesture); QPointF delta = pan->delta(); horizontalScrollBar()->setValue(horizontalScrollBar()->value() - delta.x()); verticalScrollBar()->setValue(verticalScrollBar()->value() - delta.y()); } }; int main(int argc, char *argv[]) { QApplication app(argc, argv); GestureImageView view; QGraphicsScene scene(-200, -200, 400, 400); scene.addText("支持手势缩放平移"); view.setScene(&scene); view.setAttribute(Qt::WA_AcceptTouchEvents); view.grabGesture(Qt::PinchGesture); view.grabGesture(Qt::PanGesture); view.resize(800, 600); view.show(); return app.exec(); } ``` 在这个版本中,我们使用了 `QGraphicsView` `QGraphicsScene` 组合,并抓取了 `Qt::PinchGesture` `Qt::PanGesture` 这两种手势事件分别用来控制图像的缩放平移行为。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值