警告

本节包含自动从C++翻译到Python的代码片段,可能含有错误。

图像手势示例#

展示了在窗口部件中使用简单手势。

此示例展示了如何为窗口部件启用手势并使用手势输入执行操作。

../_images/imagegestures-example.png

我们使用两个类来创建应用程序的用户界面:MainWidgetImageWidgetMainWidget 类仅用作 ImageWidget 类的容器,我们将配置它以接受手势输入。由于我们对手势的使用方式感兴趣,我们将集中在 ImageWidget 类的实现上。

ImageWidget 类定义#

ImageWidget 类是一个简单的 QWidget 子类,它除了重写一些具体的处理函数外,还重写了常见的 event() 处理函数。

class ImageWidget(QWidget):

    Q_OBJECT
# public
    ImageWidget(QWidget parent = None)
    def openDirectory(url):
    def grabGestures(gestures):
# protected
    bool event(QEvent event) override
    def paintEvent(event):
    def resizeEvent(event):
    def mouseDoubleClickEvent(event):
# private
    gestureEvent = bool(QGestureEvent event)
    def panTriggered(QPanGesture*):
    def pinchTriggered(QPinchGesture*):
    def swipeTriggered(QSwipeGesture*):            ...

我们还实现了一个私有的辅助函数 gestureEvent(),用于帮助管理发送到窗口部件的手势事件,以及三个基于手势执行动作的函数:panTriggered()pinchTriggered()swipeTriggered()

ImageWidget 类实现#

在窗口部件的构造函数中,我们首先设置了各种参数,这些参数将用于控制图像的显示方式。

def __init__(self, parent):
    super().__init__(parent)
    self.position = 0
    self.horizontalOffset = 0
    self.verticalOffset = 0
    , rotationAngle(0), scaleFactor(1), currentStepScaleFactor(1)

    setMinimumSize(QSize(100, 100))

通过调用 grabGesture() 并提供所需的手势类型,我们为窗口部件启用了三种标准手势。这些手势将由应用程序的默认手势识别器识别,并发送事件到我们的窗口部件。

由于 QWidget 未定义特定于手势的事件处理器,窗口部件需要重写常见的 event() 来接收手势事件。

def event(self, QEvent event):

    if event.type() == QEvent.Gesture:
        return gestureEvent(QGestureEvent(event))
    return QWidget.event(event)

我们的处理函数将手势事件委托给专门为此任务编写的私有函数,并将所有其他事件传递给 QWidget 的实现。

gestureHandler() 函数检查由新交付的 QGestureEvent 提供的手势。由于在任何特定时刻,一个小部件上只能使用同一种类型的一个手势,因此我们可以使用 gesture() 函数检查每种手势类型

def gestureEvent(self, QGestureEvent event):

    qCDebug(lcExample) << "gestureEvent():" << event
    if QGesture swipe = event.gesture(Qt.SwipeGesture):
        swipeTriggered(QSwipeGesture(swipe))
    elif QGesture pan = event.gesture(Qt.PanGesture):
        panTriggered(QPanGesture(pan))
    if QGesture pinch = event.gesture(Qt.PinchGesture):
        pinchTriggered(QPinchGesture(pinch))
    return True

如果为某种类型的手势提供了一个 QGesture 对象,我们将调用一个专门的功能来处理它,将手势对象转换为适当的 QGesture 子类。

为了说明标准手势如何被应用程序解释,我们展示了 pinchTriggered() 函数的实现,该函数处理用户在显示屏或输入设备上用两个手指移动时的捏合手势。

def pinchTriggered(self, gesture):

    QPinchGesture.ChangeFlags changeFlags = gesture.changeFlags()
    if changeFlags  QPinchGesture.RotationAngleChanged:
        rotationDelta = gesture.rotationAngle() - gesture.lastRotationAngle()
        rotationAngle += rotationDelta
        qCDebug(lcExample) << "pinchTriggered(): rotate by" <<
            rotationDelta << "." << rotationAngle

    if changeFlags  QPinchGesture.ScaleFactorChanged:
        currentStepScaleFactor = gesture.totalScaleFactor()
        qCDebug(lcExample) << "pinchTriggered(): zoom by" <<
            gesture.scaleFactor() << "." << currentStepScaleFactor

    if gesture.state() == Qt.GestureFinished:
         = currentStepScaleFactor
        currentStepScaleFactor = 1

    update()

QPinchGesture 类提供了属性,可以将两个触摸点之间的变化距离解释为缩放因子,以及角度变化作为应用于图像的旋转。触摸点之间的中心点可以用来拖动图像,但在本例中我们使用平移手势来达到这个目的。

scaleFactor() 是一个相对值,表示从一次事件到下一次事件应该改变多少比例,而 totalScaleFactor() 提供了自从手势开始以来所表达的缩放量。当触摸点释放并且另一个手势开始时,totalScaleFactor() 将从 1.0 重新开始。在这种情况下,我们将 totalScaleFactor() 存储到 currentStepScaleFactor 变量中,以便可以在 paintEvent() 中使用它来缩放图像。或者,在捏合处理程序中,可以直接将存储的总缩放因子乘以 scaleFactor()

相比之下,rotationAngle() 代表自从捏合手势开始以来的旋转量,而 lastRotationAngle() 提供了先前的值。因此,需要进行减法以获得增量偏差。当用户开始一个新的捏合手势时,rotationAngle() 将从零开始,我们希望图像从当前的旋转角度开始旋转。这是通过将增量添加到存储的 rotationAngle(在 paintEvent() 中应用)实现的。如果我们简单地将 totalRotationAngle() 赋值给存储的 rotationAngle,则新的手势会导致图像在再次旋转之前重置为竖直方向。但是,可以在 paintEvent() 中存储开始手势的旋转角度,并将其添加到 rotationAngle,就像我们存储开始手势的缩放量一样。

在本示例中,旋转和滑动手势也由单独的函数处理,这些函数使用传递给它们的 QGesture 对象的属性值。

def paintEvent(self, QPaintEvent*):

    p = QPainter(self)
    if files.isEmpty() and not path.isEmpty():
        p.drawText(rect(), Qt.AlignCenter|Qt.TextWordWrap,
                         tr("No supported image formats found"))
        return

    iw = currentImage.width()
    ih = currentImage.height()
    wh = height()
    ww = width()
    p.translate(ww / 2, wh / 2)
    p.translate(horizontalOffset, verticalOffset)
    p.rotate(rotationAngle)
    p.scale(currentStepScaleFactor * scaleFactor, currentStepScaleFactor * scaleFactor)
    p.translate(-iw / 2, -ih / 2)
    p.drawImage(0, 0, currentImage)

paintEvent() 中,scaleFactor 表示在捏合手势开始之前的缩放级别,而 currentStepScaleFactor 表示捏合手势进行过程中的额外缩放因子。但旋转仅存储当前的旋转角度。水平和垂直偏移表示图片通过滑动手势被拖动的距离。

示例项目 @ code.qt.io