彩票走势图

最新版Qt6中的输入事件

翻译|使用教程|编辑:鲍佳佳|2020-12-08 11:22:34.530|阅读 353 次

概述:Qt Quick中鼠标和触摸事件的传递是很复杂的,几年前我们就清楚地意识到,我们需要重构事件继承层次结构,为各种事件类型提供一些通用的API,以便共享更多的传递代码。在Qt 5.8中,我们添加了QQuickPointerEvent和相关的类型,以此作为原型

# 慧都年终大促·界面/图表报表/文档/IDE等千款热门软控件火热促销中 >>

相关链接:

Qt是一个跨平台框架,通常用作图形工具包,它不仅创建CLI应用程序中非常有用。而且它也可以在三种主要的台式机操作系统以及移动操作系统(如Symbian,Nokia Belle,Meego Harmattan,MeeGo或BB10)以及嵌入式设备,Android(Necessitas)和iOS的端口上运行。现在我们为你提供了免费的试用版。赶快点击下载Qt6最新试用版>>

工具推荐:

  • QtitanRibbon| 下载试用: 遵循Microsoft Ribbon UI Paradigm for Qt技术的Ribbon UI组件,致力于为Windows、Linux和Mac OS X提供功能完整的Ribbon组件。
  • QtitanChart | 下载试用 :是一个C ++库,代表一组控件,这些控件使您可以快速地为应用程序提供漂亮而丰富的图表。并且支持所有主要的桌面操作系

Qt Quick中鼠标和触摸事件的传递是很复杂的,几年前我们就清楚地意识到,我们需要重构事件继承层次结构,为各种事件类型提供一些通用的API,以便共享更多的传递代码。在Qt 5.8中,我们添加了QQuickPointerEvent和相关的类型,以此作为原型。它们是QObjects;从那时起,QQuickWindow就开始交付这些包装器事件,里面携带着原始事件。现在终于在Qt 6中,我们能够完成QEvent的重构,这样QQuickWindow就不再需要包装器了。与此一起,我们还能够增加一些功能,并修复一些bug。剩下的许多在 Qt 5 中看起来难以解决的 bug 至少在以后应该可以修复。

QPointerEvent和QEventPoint

现在,继承层次结构如下所示:

QPointerEvent是一个新的抽象类型,适用于所有来自指向设备(鼠标、触摸屏、平板电脑手写笔)的事件。它拥有通用的API,能够以设备无关的方式处理所有这些事件。由于QTouchEvent可以在一个事件中携带多个触摸点,我们将这个概念标准化:每个QPointerEvent都可能代表一个QEventPoint实例的集群(即使大多数事件只携带一个点),因此具有适当的API:point()、point(i)和pointCount()。
每个QInputEvent(包括QPointerEvent)都携带一个指向它来自的QInputDevice的指针。这允许事件处理代码以特定设备的方式进行响应,即使在处理合成鼠标事件时也是如此。

每个QEventPoint都有速度。Qt Quick 在 Qt 5 中使用的 Kalman 过滤器已经被移到了 QtGui 中,因此无论事件在哪里传递,都可以得到最近几次移动的平均速度。这使得对速度敏感的行为(如区分慢速拖动和快速轻弹,或对特定的运动方向做出反应)可以不受来自哪个设备的影响。瞬时速度通常对这种目的来说太不稳定了,但如果你需要它,你可以用(globalPosition()-globalLastPosition())来计算。/ (timestamp() - lastTimestamp())。

QSinglePointEvent 是另一个抽象类型,它将过去在 QMouseEvent、QTabletEvent、QHoverEvent、QWheelEvent 和其他一些类型中单独且不一致地实现的位置访问器标准化。position()取代了 pos()和 posF(), scenePosition()取代了 windowPos(), globalPosition()取代了 screenPos()。旧的访问器现在仍然存在,但已经废弃:例如,Qt 5 应用程序不会因为处理 QMouseEvent 而遇到 SC 断裂。QEventPoint 取代了 QTouchEvent::TouchPoint,但为了源代码的兼容性,有一个 "using "声明。

我已经分叉了 clazy,并添加了一个新的 qevent-accessors 检查,这可能会给你省去一些麻烦:它可以自动应用 "fixits "来摆脱来自事件访问器重命名的废弃警告。

在C++中处理与设备无关的事件

在Qt中的各个地方,我们现在可以对鼠标、触摸和平板电脑事件(如检测到点击或拖动)做出响应,或者通过迭代QEventPoints,或者只对第一个点做出响应。下面是一个人为的例子,说明QQuickItem子类如何做到这一点。


bool MyItem::event(QEvent *ev) override {
  if (ev->isPointerEvent()) {
    QPointerEvent *pev = static_cast<QPointerEvent *>(ev);
    for (QEventPoint &point : pev->points()) {
      switch (point.state()) {
      case QEventPoint::State::Pressed:
        if (reactToPress(point.position()))
          pev->setExclusiveGrabber(point, this);
        break;
      case QEventPoint::State::Updated:
        ...
      }
    }
    return true;
  }
  return QQuickItem::event(ev);
}

例如,QQuickFlickable :: childMouseEventFilter()以这种方式工作。这产生了一个有趣的结果:

Flickable现在可以处理触摸了

Qt 5的Flickable只能处理实际的鼠标事件和合成的鼠标事件,有很多开放性的bug。Qt只支持一个鼠标,一个鼠标位置,一个光标(到目前为止,但我们正在努力解决这个问题......),因此你不能用两根手指轻触两个Flickable。如果你触摸了Flickable里面的某个能够处理触摸事件的组件,但是你在允许的方向上拖动你的手指穿过Flickable,它就会使用childMouseEventFilter()从该组件中窃取抓取;但是这涉及到从实际的触摸事件切换到合成鼠标事件,而且还要记住将以下更新作为合成鼠标事件传递给Flickable。各种事情都出了问题。好吧......那些日子结束了,因为Flickable::childMouseEventFilter()不再关心QPointerEvents来自哪个设备。如果你设置了pressDelay,它就能够暂缓实际的触摸按压,然后在定时器过期时重播给里面的项目。是的,现在你也可以用多个手指拖动多个Flickables了。

多点触控虽然仍然不能与其余的只用鼠标的项目(如MouseArea)一起工作,因为这些项目仍然依赖于协同鼠标事件。但可以避免。一般来说:尽量使用事件处理程序而不是MouseArea,因为(正如它的名字所示)它并不是真的要支持任何比鼠标交互更多的东西。

QTabletEvents(来自你的Wacom手写笔、三星S-pen、Apple Pencil等)也只是指针事件,它们携带了一些更多的属性,可以由任何处理鼠标和触摸事件的设备无关代码来处理。但我们会继续努力改善这些体验。我们没有在QQuickItem中为它们添加任何新的虚拟函数,但它们很快就会被交付给QQuickItem::event()。

另一件事我们还在努力,就是让Flickable在笔记本触摸板上表现得更好。很快就会有一个修复方案。

QInputDevice

QInputDevice是一个QObject,它的parent()可以是另一个设备,如果有一个自然的层次结构:例如,X11有主设备和从设备,一个平板电脑手写笔 "属于 "一个特定的平板设备。在其他情况下,父设备只是平台插件中的一个对象,出于内存管理的目的,它拥有该设备。

在没有进行设备发现工作的平台上,QInputEvent::device()从来不是空的,而可能是取自QInputDevice::primaryKeyboard()或QPointingDevice::primaryMouse()的通用实例。不过触摸屏设备是独一无二的,我们在Qt 5中已经这样做了。

QInputDevice::seatName()对应于Wayland的 "座位 "概念:一个用户正在使用的一组设备。到目前为止,对多座位的支持还很少,但随着时间的推移,它将会得到改进。如果您配置了一个多指针X服务器,您可以在不同的设备上看到不同的座位名称,但这些名称是由xcb插件中的xinput ID自动生成的。在Wayland合成器上,如Sway,可以给座位任意命名;我们计划Qt最终会与之合作。


$ xinput list
Virtual core pointer id=2 [master pointer (3)]
  Virtual core XTEST pointer id=4 [slave pointer (2)]
  ZSA Technology Labs ErgoDox EZ Mouse id=11 [slave pointer (2)]
  ZSA Technology Labs ErgoDox EZ Consumer Control id=13 [slave pointer (2)]
  Logitech MX Master 2S id=15 [slave pointer (2)]
Virtual core keyboard id=3 [master keyboard (2)]
  Virtual core XTEST keyboard id=5 [slave keyboard (3)]
  Power Button id=6 [slave keyboard (3)]
  Power Button id=7 [slave keyboard (3)]
  Sleep Button id=8 [slave keyboard (3)]
  UVC Camera (046d:0992) id=9 [slave keyboard (3)]
  ZSA Technology Labs ErgoDox EZ id=10 [slave keyboard (3)]
  ZSA Technology Labs ErgoDox EZ System Control id=12 [slave keyboard (3)]
  ZSA Technology Labs ErgoDox EZ Keyboard id=14 [slave keyboard (3)]
  ZSA Technology Labs ErgoDox EZ Consumer Control id=16 [slave keyboard (3)]
  Logitech MX Master 2S id=17 [slave keyboard (3)]
aux pointer id=22 [master pointer (23)]
  Microsoft Microsoft Optical Mouse by Starck id=19 [slave pointer (22)]
  aux XTEST pointer id=24 [slave pointer (22)]
aux keyboard id=23 [master keyboard (22)]
  Apple, Inc Apple Keyboard id=20 [slave keyboard (23)]
  Apple, Inc Apple Keyboard id=21 [slave keyboard (23)]
  aux XTEST keyboard id=25 [slave keyboard (23)]

$ qtdiag
Qt 6.0.0 (x86_64-little_endian-lp64 shared (dynamic) debug build; by GCC 10.2.0) on "xcb" 
OS: Arch Linux [linux version 5.9.11-arch2-1]
...
Input devices: 23
QInputDevice::DeviceType::Mouse "Virtual core pointer", seat: "30002" capabilities: Position Scroll Hover
QInputDevice::DeviceType::Keyboard "Virtual core keyboard", seat: "30002" capabilities:
QInputDevice::DeviceType::Mouse "aux pointer", seat: "170016" capabilities: Position Scroll Hover
QInputDevice::DeviceType::Keyboard "aux keyboard", seat: "170016" capabilities:
QInputDevice::DeviceType::Mouse "Virtual core XTEST pointer", seat: "30002" capabilities: Position Scroll Hover
QInputDevice::DeviceType::Keyboard "Virtual core XTEST keyboard", seat: "30002" capabilities:
QInputDevice::DeviceType::Keyboard "Power Button", seat: "30002" capabilities:
QInputDevice::DeviceType::Keyboard "Power Button", seat: "30002" capabilities:
QInputDevice::DeviceType::Keyboard "Sleep Button", seat: "30002" capabilities:
QInputDevice::DeviceType::Keyboard "UVC Camera (046d:0992)", seat: "30002" capabilities:
QInputDevice::DeviceType::Keyboard "ZSA Technology Labs ErgoDox EZ", seat: "30002" capabilities:
QInputDevice::DeviceType::Mouse "ZSA Technology Labs ErgoDox EZ Mouse", seat: "30002" capabilities: Position Scroll Hover
QInputDevice::DeviceType::Keyboard "ZSA Technology Labs ErgoDox EZ System Control", seat: "30002" capabilities:
QInputDevice::DeviceType::Mouse "ZSA Technology Labs ErgoDox EZ Consumer Control", seat: "30002" capabilities: Position Scroll Hover
QInputDevice::DeviceType::Keyboard "ZSA Technology Labs ErgoDox EZ Keyboard", seat: "30002" capabilities:
QInputDevice::DeviceType::Keyboard "ZSA Technology Labs ErgoDox EZ Consumer Control", seat: "30002" capabilities:
QInputDevice::DeviceType::Mouse "Logitech MX Master 2S", seat: "30002" capabilities: Position Scroll Hover
QInputDevice::DeviceType::Keyboard "Logitech MX Master 2S", seat: "30002" capabilities:
QInputDevice::DeviceType::Mouse "Microsoft Microsoft Optical Mouse by Starck", seat: "170016" capabilities: Position Scroll Hover
QInputDevice::DeviceType::Keyboard "Apple, Inc Apple Keyboard", seat: "170016" capabilities:
QInputDevice::DeviceType::Keyboard "Apple, Inc Apple Keyboard", seat: "170016" capabilities:
QInputDevice::DeviceType::Mouse "aux XTEST pointer", seat: "170016" capabilities: Position Scroll Hover
QInputDevice::DeviceType::Keyboard "aux XTEST keyboard", seat: "170016" capabilities:

QInputDevice :: availableVirtualGeometry()旨在告诉您该设备可以访问虚拟桌面的哪个区域。例如,您可能正在使用带有外部显示器的触摸屏笔记本电脑:那么触摸屏的QPointingDevice :: availableVirtualGeometry()应该与屏幕的QScreen :: geometry()相同。Wacom数位板可以映射到小于整个屏幕的区域,以提高绘图精度(使用xinput或特定于操作系统的控制面板)。但是同样,这项工作尚未在所有受支持的平台上完成。

合成鼠标事件

合成鼠标事件仍然存在,尽管我们现在正试图减少对它们的依赖。

QEvent::spontaneous()是我们区分OS产生的事件和合成事件的最古老的方法,但它并不适合区分合成鼠标事件。在Qt 5中,增加了QMouseEvent::source()来帮助你区分由其他设备、操作系统、Qt或应用程序合成的鼠标事件;但后来我们发现,假设这样的事件是由触摸点合成的,既诱人又错误。(例如,它可能是由QTabletEvent合成的。)所以我们建议现在使用event->device()->type()和/或pointerDevice()->pointerType()来区分这些事件。当一个QMouseEvent从其他类型的事件中合成时,设备实例保持不变,这样你就可以知道它到底来自哪里。

分类日志

自从几年前增加了分类日志后,我们在Qt中增加了越来越多的内部日志,(在qtbase和qtdeclarative中git grep Q_LOGGING_CATEGORY会发现很多对调试Qt Quick应用很有用的)。对于排除鼠标和触摸交互问题来说,最有用的类别是qt.pointer.grab,因为grab转换是大多数此类问题的症状或原因。但还有更多:你可以在QPA级别、QtGui级别、QQuickWindow中、向项目和/或处理程序传递过程中记录事件;你还可以在各种项目和处理程序中记录交互的各个方面。

我可能会在以后的文章中写更多关于抓取和接受事件的技术细节。

好了这就是今天的内容了,如果今天的文章未解决你的需求,点击获取更多文章教程。不要忘了在评论与我们分享您的想法和建议。

我可能会在以后的文章中写有关捕获和接受事件的更多技术细节。


标签:

本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@pclwef.cn

文章转载自:

为你推荐

  • 推荐视频
  • 推荐活动
  • 推荐产品
  • 推荐文章
  • 慧都慧问
扫码咨询


添加微信 立即咨询

电话咨询

客服热线
023-68661681

TOP