本页内容

小部件和图形视图中的手势

Qt 包含一个用于手势编程的框架,该框架能够根据一系列事件构建手势,且与所使用的输入方式无关。手势可以是鼠标的特定移动、触摸屏操作,或是来自其他来源的一系列事件。 输入的性质、手势的解释以及采取的操作均由开发者自行决定。

概述

QGesture 是 Qt 手势框架的核心类,为用户执行的手势信息提供了一个容器。QGesture 公开了提供所有手势共有的通用信息的属性,这些属性可以被扩展以提供额外的手势特定信息。常见的平移、捏合和滑动手势由专门的类表示:QPanGesture 、QPinchGesture 和QSwipeGesture 。

开发者还可以通过继承并扩展 `QGestureRecognizer ` 类来实现新的手势。添加对新手势的支持需要编写代码,以便从输入事件中识别该手势。相关内容在“创建您自己的手势识别器”一节中有详细说明。

在小部件中使用标准手势

可以为QWidget 和QGraphicsObject 子类的实例启用手势功能。在本文档中,接受手势输入的对象统称为目标对象。

要为目标对象启用手势,请调用其QWidget::grabGesture() 或QGraphicsObject::grabGesture() 函数,并传入描述所需手势类型的参数。标准类型由Qt::GestureType 枚举定义,其中包含许多常用手势。

for (Qt::GestureType gesture : gestures)
    grabGesture(gesture);

在上述代码中,手势是在目标对象本身的构造函数中设置的。

处理事件

当用户执行手势时,QGestureEvent 事件将传递给目标对象,可以通过重写控件的QWidget::event()处理函数或图形对象的QGraphicsItem::sceneEvent()处理函数来处理这些事件。

由于一个目标对象可以订阅多种手势类型,因此QGestureEvent 中可能包含多个QGesture ,这表明可能有多种手势同时处于活动状态。此时,控件需要自行决定如何处理这些多重手势,并选择是否应取消其中某些手势以优先处理其他手势。

QGestureEvent 对象中包含的每个QGesture 都可以单独通过 accept() 或 ignore() 处理,也可以统一处理。此外,您还可以使用多个获取器查询各个QGesture 数据对象(即状态)。

事件处理的标准流程

当QGesture 到达您的控件时,默认会被接受。但是,始终显式地接受或拒绝手势是一种良好的编程实践。 一般规则是:若接受一个手势,则表示您正在使用它;若忽略它,则表示您对此不感兴趣。忽略手势可能意味着它会被传递给另一个目标对象,或者被取消。

每个QGesture 都会经历多个状态;状态的切换有明确的规则,通常由用户输入引发状态变化(例如通过开始和停止交互),但控件本身也可以引发状态变化。

当某个特定的QGesture 首次被传递给小部件或图形项时,它将处于Qt::GestureStarted 状态。此时你对该手势的处理方式,将影响你后续能否与其进行交互。

  • 接受该手势意味着控件将对此手势采取行动,随后将出现处于 Qt::GestureUpdated 状态的手势。
  • 忽略该手势则意味着该手势将不再提供给您。该手势也会提供给父级小部件或图形项。
  • 当手势处于起始状态且已被接受时,若调用 setGestureCancelPolicy(),可能会导致其他手势被取消。

使用QGesture::CancelAllInContext 取消一个手势将导致所有处于任何状态的手势被取消,除非它们已被显式接受。这意味着子控件上的活动手势将被取消。这也意味着,如果在同一QGestureEvent 中传递的手势被小部件忽略,这些手势也将被取消。这是一种有用的方法,可以过滤掉除你感兴趣的手势以外的所有手势。

事件处理示例

为方便起见,“图像手势示例”(Image Gestures Example)重新实现了通用的event()处理函数,并将手势事件委托给专门的gestureEvent()函数:

bool ImageWidget::event(QEvent *event)
{
    if (event->type() == QEvent::Gesture)
        return gestureEvent(static_cast<QGestureEvent*>(event));
    return QWidget::event(event);
}

传递到目标对象的手势事件可以逐个检查并进行适当处理:

bool ImageWidget::gestureEvent(QGestureEvent *event)
{
    qCDebug(lcExample) << "gestureEvent():" << 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;
}

响应手势只需获取发送到目标对象的QGestureEvent 中传递的QGesture 对象,并检查其中包含的信息即可。

void ImageWidget::swipeTriggered(QSwipeGesture *gesture)
{
    if (gesture->state() == Qt::GestureFinished) {
        if (gesture->swipeAngle() < 45 || gesture->swipeAngle() > 225) {
            // swipe direction right or down
            qCDebug(lcExample) << "swipeTriggered(): angle"
                               << gesture->swipeAngle() << "; swipe to next";
            goNextImage();
        } else {
            // swipe direction left or up
            qCDebug(lcExample) << "swipeTriggered(): angle"
                               << gesture->swipeAngle() << "; swipe to previous";
            goPrevImage();
        }
        update();
    }
}

在此,我们检查用户滑动小部件的方向,并据此修改其内容。

创建您自己的手势识别器

要添加对新手势的支持,需要创建并注册一个新的手势识别器。根据手势的识别过程,可能还需要创建一个新的手势对象。

要创建新的识别器,您需要继承 `QGestureRecognizer ` 类来创建自定义识别器类。其中有一个虚拟函数必须重写,另外两个函数可根据需要重写。

过滤输入事件

必须重写recognize() 函数。该函数负责处理和过滤目标对象接收到的输入事件,并判断这些事件是否与识别器所寻找的手势相对应。

虽然手势识别的逻辑在此函数中实现(可能基于Qt::GestureState 枚举的状态机),但您可以将识别过程状态的持久化信息存储在提供的QGesture 对象中。

您的 `recognize()` 函数必须返回一个 `QGestureRecognizer::Result ` 类型的值,该值表示针对特定手势和目标对象的识别状态。这将决定手势事件是否会被传递给目标对象。

自定义手势

如果您选择通过自定义的QGesture 子类来表示手势,则需要重写create()函数,以构造您手势类的实例,而不是标准的QGesture 实例。或者,您也可以使用标准的QGesture 实例,但需为其添加额外的动态属性,以表达您要处理的手势的具体细节。

重置手势

如果您使用的是自定义手势对象,且在手势被取消时需要重置或进行其他特殊处理,则需要重写reset() 函数以执行这些特殊任务。

请注意,对于每个目标对象和手势类型的组合,QGesture 对象仅创建一次,并且当用户尝试在目标对象上执行相同类型的手势时,该对象可能会被重复使用。因此,重写reset()函数以在每次手势识别尝试后进行清理会很有帮助。

使用新的手势识别器

要使用手势识别器,请构建QGestureRecognizer 子类的实例,并通过QGestureRecognizer::registerRecognizer()将其注册到应用程序中。可通过QGestureRecognizer::unregisterRecognizer()移除特定类型手势的识别器。

进一步阅读

“图像手势示例”演示了如何在简单的图像查看器应用程序中为小部件启用手势功能。

在Qt Quick

Qt Quick 中没有通用的全局手势识别器;相反,各个组件可以以各自的方式响应触摸事件。例如,PinchArea 处理双指手势,Flickable 用于单指轻扫内容,而MultiPointTouchArea 可以处理任意数量的触摸点,并允许应用程序开发人员编写自定义的手势识别代码。

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.