Qt6中的输入事件

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

工具推荐:linux

  • 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 至少在之后应该能够修复。git

QPointerEvent和QEventPoint服务器

如今,继承层次结构以下所示:框架

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

每一个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 "来摆脱来自事件访问器重命名的废弃警告。ui

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

在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中、向项目和/或处理程序传递过程当中记录事件;你还能够在各类项目和处理程序中记录交互的各个方面。

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

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

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