本文介绍的QGraphicsView的双指缩放,QWidget更简单,可以参考当前内容。
方法一:(QTouchEvent事件实现)
使用场景:适用于paintevent绘制下的界面。
优点:不需要代码设置中心锚点(锚点:视图变化期间通过此点定位场景)。
缺点:界面上所有其它操作无法响应,需单独做处理才能做相应。(无法响应原因是使用了”return true“打断了”触摸点击“转化为”鼠标点击“。但是不使用”retuen true“又会造成”触摸点击“默认转化成了”鼠标点击“,触摸操作无法过度到TouchUpdate中来捕获到多点触摸了。)
使用步骤:
1、首先需要打开触摸屏功能。
this->setAttribute(Qt::WA_AcceptTouchEvents);
2、在event事件管理器中接收触摸屏的三个事件,TouchBegin、TouchUpdate和TouchEnd。
3、判断单点触摸还是多点触摸。
4、如果多点触摸,通过比较前后两次两点间触摸位置来判断是放大还是缩小。
5、多点触摸时,会存在抖动情况,需要做防抖处理。
6、如果是单点触摸,通过比较手指放上去的位置和手指拖动时的位置来设置界面滚动条的位置。
代码:
属性设置
this->setAttribute(Qt::WA_AcceptTouchEvents);
缩放和移动逻辑
bool MGraaphicsView::event(QEvent *e)
{
static int index = 0;
switch (e->type()) {
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
{
qDebug() <<"CProjectionPicture::event"<<e->type();
QTouchEvent *touchEvent = static_cast<QTouchEvent *>(e);
QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
if (touchPoints.count() == 2) {
//缩放
const QTouchEvent::TouchPoint &touchPoint0 = touchPoints.first();
const QTouchEvent::TouchPoint &touchPoint1 = touchPoints.last();
qreal currentScaleFactor = QLineF(touchPoint0.pos(), touchPoint1.pos()).length()
/ QLineF(touchPoint0.startPos(), touchPoint1.startPos()).length();
if (currentScaleFactor > _lastScaleFactor)
index++;
else if (currentScaleFactor < _lastScaleFactor)
index--;
if (index == 5)//超过5次放大,则认为有效.防抖操作
{
index = 0;
zoomOnce(true);
}
else if (index == -5)
{
index = 0;
zoomOnce(false);
}
qDebug()<<index<<currentScaleFactor<<_lastScaleFactor;
_lastScaleFactor = currentScaleFactor;
update();
}
else if (touchPoints.count() == 1)
{//移动
const QTouchEvent::TouchPoint &touchPoint = touchPoints.first();
if (e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchEnd)
_startScenePos = mapToScene(touchPoint.pos().toPoint());
if (e->type() == QEvent::TouchUpdate)
{
QPointF endScenePos = mapToScene(touchPoint.pos().toPoint());
QPointF delta = endScenePos - _startScenePos;
int oposx = this->horizontalScrollBar()->value();
int oposy = this->verticalScrollBar()->value();
int nposx = oposx - delta.x();
int nposy = oposy - delta.y();
this->horizontalScrollBar()->setValue(nposx);
this->verticalScrollBar()->setValue(nposy);
}
qDebug()<<"====="<<_startScenePos<<touchPoint.pos();
}
if (touchEvent->touchPointStates() & Qt::TouchPointReleased)
{
qDebug()<<"Qt::TouchPointReleased";
}
return true;//一定不要调QGraphicsView::event(e);否则手指触摸会经常失效
}
default:
break;
}
return QGraphicsView::event(e);
}
//缩放
void MGraaphicsView::zoomOnce(bool increase)
{
if (increase)
setZoom(1);
else
setZoom(-1);
}
void MGraaphicsView::setZoom(int val)
{
if (val > 0)
{
m_zoom++;
auto scaleValue = qPow(2, m_zoom);
setTransform(QTransform::fromScale(scaleValue, scaleValue));
}
else
{
m_zoom--;
auto scaleValue = qPow(2, m_zoom);
setTransform(QTransform::fromScale(scaleValue, scaleValue));
}
}
方法二:(QGesture事件实现)
使用场景:适用所有场景。
优点:界面上界面上所有操作都不受影响。
缺点:需要代码定位视图锚点。(QGraphicsView需要定位视图锚点,如果是QWidget则不需要定位锚点这一步,缩放大小和位置代码设置即可。)
使用步骤:
1、首先需要打开触摸屏功能和注册缩放手势。
grabGesture(Qt::PinchGesture);//”捏“手势
this->setAttribute(Qt::WA_AcceptTouchEvents);
所有手势介绍: enum GestureType { TapGesture = 1, //轻拍手势。(1个手指单击) TapAndHoldGesture = 2, //轻触并保持(长按)手势。(1个手指单击并长按) PanGesture = 3, //平移手势。(1个手指拖动) PinchGesture = 4, //捏合缩放及缩放(2个手指捏合或转动) SwipeGesture = 5, //滑动手势。(3个手指平移) CustomGesture = 0x0100, //可用于测试手势是否为用户定义的手势ID的标志。 };
自定义手势
2、在event事件管理器中接收QEvent::Gesture事件,并转化为QGestureEvent事件。
3、获取”捏“手势,并转化为QPinchGesture事件。
4、获取”捏“手势变化状态。
5、判断捏手势变化状态,根据QPinchGesture::ScaleFactorChanged状态变化计算缩放比例。
6、在GestureUpdated的变化下,根据缩放比例来处理缩小和扩大。
代码:
bool InteractiveMap::event(QEvent *e)
{
static int index = 0;
#if 1
if (e->type() == QEvent::Gesture)
{
QGestureEvent* gEvent = static_cast<QGestureEvent *>(e);
if (QGesture* pinch = gEvent->gesture(Qt::PinchGesture))
{
QPinchGesture* pEvent = static_cast<QPinchGesture *>(pinch);
QPinchGesture::ChangeFlags changeFlags = pEvent->changeFlags();
//旋转角度的变化记录
if (changeFlags & QPinchGesture::RotationAngleChanged)
{
// qreal rotationDelta = pEvent->rotationAngle() - pEvent->lastRotationAngle();
// qDebug() << "pinchTriggered(): rotate by" << rotationDelta;
}
static qreal s_factor = 1.;
//缩放比例的变化记录
if (changeFlags & QPinchGesture::ScaleFactorChanged)
{
s_factor *= pEvent->totalScaleFactor();
}
int id = (int)pEvent->state();
switch (id)
{
case Qt::GestureStarted:
case Qt::GestureUpdated:
{
//视图在变换期间应如何定位场景。
//QGraphicsView使用此属性来决定在变换矩阵发生变化以及视图的坐标系发生变换时如何在视口中定位场景。
//默认行为AnchorViewCenter可确保视图中心的场景点在变换期间保持不变(例如,旋转时,场景将显示为围绕视图中心旋转)。
setTransformationAnchor(QGraphicsView::AnchorViewCenter); //视图中心的场景点用作锚点
if (s_factor > 1)
index++;
else
index--;
if (index == 5) //超过5次放大,则认为有效.防抖操作
{
index = 0;
zoomOnce(true);
}
else if (index == -5)
{
index = 0;
zoomOnce(false);
}
setTransformationAnchor(QGraphicsView::AnchorUnderMouse); //鼠标下方的点用作锚点。
}
break;
case Qt::GestureFinished:
default:
{
s_factor = 1.;
}
break;
}
}
}
return QGraphicsView::event(e);
}
void MGraaphicsView::zoomOnce(bool increase)
{
if (increase)
setZoom(1);
else
setZoom(-1);
}
void MGraaphicsView::setZoom(int val)
{
if (val > 0)
{
m_zoom++;
auto scaleValue = qPow(2, m_zoom);
setTransform(QTransform::fromScale(scaleValue, scaleValue));
}
else
{
m_zoom--;
auto scaleValue = qPow(2, m_zoom);
setTransform(QTransform::fromScale(scaleValue, scaleValue));
}
}