小部件和图形视图中的手势
Qt 包含一个手势编程框架,该框架能够从一系列事件中独立于输入方法形成一个手势。手势可能是一个鼠标的特定移动,触摸屏动作,或者来自其他来源的一系列事件。输入的性质、手势的解释以及采取的动作是开发者的选择。
概述
QGesture 是 Qt 手势框架的核心类,提供了用户执行的手势信息存储容器。 QGesture 提供了所有手势通用的信息属性,并且可以扩展以提供额外的特定手势信息。常用的平移、缩放和滑动手势由专门的类表示: QPanGesture、QPinchGesture 和 QSwipeGesture。
开发者还可以通过扩展 QGestureRecognizer 类来创建新的手势。对新手势的支持涉及实现识别输入事件的代码。这将在 创建您自己的手势识别器 部分中描述。
使用标准手势与 widgets 一起
可以为 QWidget 和 QGraphicsObject 子类的实例启用手势。接受手势输入的对象在文档中被称为 目标对象。
要为目标对象启用手势,请使用具有描述所需手势类型的参数调用它的 QWidget::grabGesture() 或 QGraphicsObject::grabGesture() 函数。标准类型由 Qt::GestureType 枚举定义,包括许多常用手势。
for (Qt::GestureType gesture : gestures) grabGesture(gesture);
在上面的代码中,手势是在目标对象的构造函数中设置的。
处理事件
当用户执行手势时,QGestureEvent 事件将被传递到目标对象,这些可以通过重新实现小部件的 QWidget::event() 处理函数或图形对象的 QGraphicsItem::sceneEvent() 来处理。
由于一个目标对象可以订阅多个手势类型,所以 QGestureEvent 可以包含多个 QGesture,表明同时存在多个可能的手势。然后是窗口确定如何处理这些多个手势,并选择是否取消某个以便选择其他选项。
一个QGesture对象,位于QGestureEvent对象内,可以被单独接受()或忽略(),或全部一起。此外,您可以使用几个获取器查询单个QGesture数据对象(状态)。
事件处理的标准过程
当一个QGesture对象到达您的widget时,它默认被接受。然而,始终显式接受或拒绝手势是良好的实践。一般来说,如果您接受了手势,您就在使用它。如果您忽略了它,您就对它没有兴趣。忽略一个手势可能意味着它会提供给另一个目标对象,或者它将被取消。
每个QGesture都会经过几个状态;有几种明确规定的方式来改变状态,通常是用户输入是状态变化的原因(例如通过开始和停止交互),但widget也可以引起状态变化。
当一个特定的QGesture对象第一次被发送到widget或图形项时,它将处于Qt::GestureStarted状态。在此阶段处理手势的方式会影响您是否能以后与其交互。
- 接受手势意味着widget将对此手势采取行动,并会随后出现具有Qt::GestureUpdated状态的额外手势。
- 忽略手势意味着此手势将不再为您提供。它也将被提供给父widget或项。
- 当一个手势处于其开始状态并且已被接受时,调用setGestureCancelPolicy()将导致其他手势被取消。
使用QGesture::CancelAllInContext来取消手势将导致所有状态的手势被取消,除非它们被明确接受。这意味着子项上的活动手势将被取消。这也意味着如果widget忽略了它们,同一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->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子类化以创建自定义识别器类。有一个必须重新实现的一个虚拟函数,以及其他两个虚拟函数可根据需要重新实现。
过滤输入事件
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可以处理任意数量的触摸点,并允许应用程序开发商编写自定义手势识别器代码。
© 2024 Qt公司有限公司。其中包含的文档贡献的版权归其所有者所有。本文档的提供受由自由软件基金会公布并维护的GNU自由文档许可协议版本1.3条款约束。Qt及其相应的标志是芬兰及其它在全球的Qt公司有限公司的商标。所有其他商标均为其相应所有者的财产。