最近项目中遇到一个预览图片的需求,同时能够对手势进行处理,可以处理手势的放大、缩小和平移,其中移动可以通过重写鼠标事件实现。而手势事件之前则没有处理过。查阅资料发现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();
}
}