Widget 和图形视图中的手势

Qt 包括手势编程框架,拥有从独立于所用输方法的一系列事件形成手势的能力。手势可以是特定鼠标运动,触摸屏动作,或来自某些其它源的一系列事件。输入的性质、手势的解释及获取动作由开发者抉择。

概述

QGesture 是 Qt 手势框架中心类,提供用户履行手势有关信息的容器。 QGesture 暴露了赋予所有手势一般公共信息的特性,且可以扩展这些以提供特定额外手势信息。常见的平移、捏合和轻扫手势由专用类表示: QPanGesture , QPinchGesture and QSwipeGesture .

开发者还可以实现新手势通过子类化并扩展 QGestureRecognizer 类。 添加支持新手势涉及实现从输入事件识别手势的代码。这的描述在 创建自己的手势识别器 章节。

在 Widget 中使用标准手势

可以启用手势为实例化的 QWidget and QGraphicsObject 子类。接受手势输入的对象在整个文档编制被称为 目标对象 .

要启用目标对象手势,调用其 QWidget::grabGesture () 或 QGraphicsObject::grabGesture () 函数采用自变量描述要求的手势类型。标准类型的定义是通过 Qt::GestureType 枚举且包括很多常用手势。

foreach (Qt::GestureType gesture, gestures)
    grabGesture(gesture);
					

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

处理事件

当用户履行手势时, QGestureEvent 事件会被交付给目标对象,且可以处理这些通过重实现 QWidget::event () 处理程序函数为 Widget 或 QGraphicsItem::sceneEvent () 为图形对象。

由于一个目标对象可以订阅多种手势类型, QGestureEvent 可以包含多个 QGesture ,指示几个手势同时活动是可能的。然后由 Widget 确定如何处理这些手势并选择,若取消一些手势代之其它手势。

每个 QGesture 包含于 QGestureEvent 对象可以单独或全部一起 accepted() 或 ignored()。此外,可以查询单个 QGesture 数据对象 (状态) 使用几个 Getter。

事件处理的标准程序

A QGesture 默认是接受的,当它到达 Widget 时。不管怎样,好的实践是始终明确接受 (或拒绝) 手势。一般规则是,若接受手势,就会使用它。若忽略,就是对它不感兴趣。忽略手势可能意味着,会将它提供给另一目标对象 (或者它将被取消)。

每个 QGesture 会经历几种状态;有改变状态的良好定义方式,通常,用户输入是状态变化的原因 (例如:通过启动和停止交互),但 Widget 也可能导致状态改变。

首先,特定 QGesture 被交付给 Widget 或图形项,它会在 Qt::GestureStarted 状态。 此时处理手势的方式,会影响稍后是否可以与之交互。

  • 接受手势意味着 Widget 作用于手势,且手势随后具有 Qt::GestureUpdatedstate。
  • 忽略手势将意味着不会再提供手势。还会将它提供给父级 Widget 或项。
  • 调用手势的 setGestureCancelPolicy() 当其处于启动状态时,且接受还会导致其它手势被取消。

使用 QGesture::CancelAllInContext 来取消手势将导致所有手势被取消,在任何状态下都会被取消,除非明确接受它们。这意味着,将取消子级的活动手势。也意味着,手势的交付是在同一 QGestureEvent 会被取消若 Widget 忽略它们。这是可以过滤掉所有手势的有用方式,除感兴趣的手势外。

事件处理范例

为方便起见, 图像手势范例 重实现一般 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;
}
					

手势响应问题只需获得 QGesture 交付对象在 QGestureEvent 被发送给目标对象并审查它包含的信息。

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

这里,我们审查用户轻扫 Widget 的方向并相应修改其内容。

创建自己的手势识别器

添加支持新手势涉及创建并注册新手势识别器。从属手势识别过程,还可能涉及创建新手势对象。

要创建新识别器,需要子类化 QGestureRecognizer 以创建自定义识别器类。有 1 个虚拟函数必须重实现,和其它 2 个可以按要求重实现。

过滤输入事件

The recognize() 函数必须重实现。此函数处理并筛选用于目标对象的传入输入事件,和确定它们是否对应识别器正查找的手势。

尽管手势识别逻辑是在此函数中实现的,可能使用状态机基于 Qt::GestureState 枚举,可以将识别过程状态的有关持久信息存储在 QGesture 供给对象。

您的 recognize() 函数必须返回值为 QGestureRecognizer::Result 指示给定手势和目标对象的识别状态。这确定是否有将手势事件交付给目标对象。

自定义手势

若要选择表示手势通过自定义 QGesture 子类,将需要重实现 create() 函数以构造手势类实例,而不是标准 QGesture 实例。另外,可能想要使用标准 QGesture 实例,但要对它们添加额外动态特性以表达想要处理手势的特定细节。

重置手势

若取消手势时使用需要重置 (或其它特殊处理) 的自定义手势对象,需要重实现 reset() 函数以履行这些特殊任务。

注意, QGesture 对象只创建一次对于目标对象和手势类型的每个组合,且会重用它们每当用户试图对目标对象履行相同手势类型时。因此,会很有用重实现 reset() 函数以清零先前识别的手势在每次尝试后。

使用新的手势识别器

要使用手势识别器,构建实例化的 QGestureRecognizer 子类,并将它注册到应用程序采用 QGestureRecognizer::registerRecognizer ()。可以移除给定类型的手势识别器采用 QGestureRecognizer::unregisterRecognizer ().

延伸阅读

The 图像手势范例 展示如何在简单图像查看器应用程序中为 Widget 启用手势。

Qt Quick 中的手势

Qt Quick 没有一般全局手势识别器;相反,各个组件可以按自己的方式响应触摸事件。例如 PinchArea 处理 2 指手势, Flickable 按单指轻弹内容,和 MultiPointTouchArea 可以处理任意数量的接触点并允许应用程序开发者编写自定义手势识别代码。