翻譯|使用教程|編輯:鮑佳佳|2020-12-08 11:22:34.530|閱讀 436 次
概述:Qt Quick中鼠標和觸摸事件的傳遞是很復雜的,幾年前我們就清楚地意識到,我們需要重構事件繼承層次結構,為各種事件類型提供一些通用的API,以便共享更多的傳遞代碼。在Qt 5.8中,我們添加了QQuickPointerEvent和相關的類型,以此作為原型
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
相關鏈接:
Qt是一個跨平臺框架,通常用作圖形工具包,它不僅創建CLI應用程序中非常有用。而且它也可以在三種主要的臺式機操作系統以及移動操作系統(如Symbian,Nokia Belle,Meego Harmattan,MeeGo或BB10)以及嵌入式設備,Android(Necessitas)和iOS的端口上運行。現在我們為你提供了免費的試用版。趕快點擊下載Qt6最新試用版>>
工具推薦:
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@fc6vip.cn
文章轉載自: