編程向導4.6輸入管理
一蛛壳、輸入架構
Kivy能處理很多類型的輸入:鼠標、觸摸屏所刀、加速器衙荐、陀螺儀等等。它在下列的平臺中能處理多點觸摸協(xié)議:Tuio浮创,WM_Touch, MacMultitouchSupport, MT Protocol A/B and Android.
輸入全局架構可以被描述為:
輸入提供者(Input Providers) -> 運動事件(Motion event) -> 投遞處理(Post processing) -> 發(fā)送到窗口(Dispatch to Window)
所有管理輸入事件的類是運動事件(MotionEvent)忧吟。它派生成兩種類型的事件類:
觸摸事件:一個運動事件能至少包含X和Y坐標。所有的觸摸事件通過部件樹進行發(fā)送斩披。
非觸摸事件:剩下的溜族,例如加速器是一個連續(xù)事件,沒有坐標垦沉。它沒有開始和停止狀態(tài)煌抒。這些事件不通過部件樹發(fā)送。
一個運動事件被一個輸入提供者(Input Provider)生成厕倍。一個輸入提供者負責從操作系統(tǒng)寡壮、網(wǎng)絡或其他應用程序讀取輸入事件。下是幾個輸入提供者:
TuioMotionEventProvider:創(chuàng)建一個UDP服務并監(jiān)聽TUIO/OSC消息讹弯。
WM_MotionEventProvider:使用窗口API讀取多點觸摸信息并發(fā)送到Kivy诬像。
ProbeSysfsHardwareProbe:在Linux系統(tǒng)下,迭代所有連接到計算機上的硬件闸婴,并附著一個多點輸入提供者為每一個被發(fā)現(xiàn)的多點觸摸設備坏挠。
更多:...
當你寫一個應用程序時,你不必創(chuàng)建一個輸入提供者邪乍,Kivy會嘗試自動檢測可用的硬件降狠。但是对竣,如果你想支持定制的硬件,你就需要配置Kivy并使它工作榜配。
在新創(chuàng)建的運動事件被傳遞到用戶之前否纬,Kivy應用投遞處理(post-processing)到輸入。每一個運動事件被分析并糾正錯誤輸入蛋褥,做出有意義的解釋:
- 通過距離和時間臨界值临燃,檢測雙擊/三擊(Double/triple-tap detection)。
- 當硬件不是太精確時烙心,使得事件更精確膜廊。
- 如果本地觸摸硬件在基本相同的位置發(fā)送多個事件時,降低事件的數(shù)量淫茵。
當處理事件后爪瓜,運動事件被發(fā)送到窗口。正如前面解釋的那樣匙瘪,不是所有的事件都發(fā)送到事件樹:窗口過濾它們铆铆。對于一個事件:
如果它僅僅是一個運動事件,它會被發(fā)送到on_motion()
-
如果它是一個觸摸事件丹喻,則觸摸點的(x, y)坐標(在0-1范圍內(nèi))會被重新轉換為屏幕尺寸(寬/高)薄货,并發(fā)送到:
- on_touch_down()
- on_touch_move()
- on_touch_up()
二、運動事件配置
依賴你的硬件和使用的輸入提供者碍论,也許有更多的信息可以被使用菲驴。例如,觸摸輸入有(x, y)坐標骑冗,但是也許還有壓力信息赊瞬,觸摸尺寸,加速度等等贼涩。
一個運動事件配置是一個標識事件里面有什么特征可用字符串巧涧,讓我們想象一下你在on_touch_move方法中:
def on_touch_move(self, touch):
print(touch.profile)
return super(..., self).on_touch_move(touch)
打印信息為:
['pos', 'angle']
注意:很多人將配置的名字和屬性對應的名字混淆在了一塊。在可用的配置文件里的'angle'不意味著觸摸事件對象有一個angle對象遥倦。
對于'pos'配置谤绳,屬性pox, x, y是可用的。對于'angle'配置袒哥,屬性 a 是可用的缩筛。正如我們所說,對于觸摸事件堡称,'pos'是一個托管的配置瞎抛,但'angle'不是。你能通過檢測'angle'配置是否存在來擴展你的交互:
def on_touch_move(self, touch):
print('the touch is at position', touch.pos)
if 'angle' in touch.profile:
print('the touch angle is', touch.a)
你能在motionevent文檔中找到一個關于可用的配置的列表却紧。
三桐臊、觸摸事件
一個觸摸事件是一個特殊的運動事件胎撤,它的屬性is_touch被設置為True。對于所有的觸摸事件断凶,你會自動擁有X和Y坐標伤提,對應著窗口的寬和高。換句話說认烁,所有的觸摸事件有'pox'配置肿男。
(一)觸摸事件基礎
默認情況下,觸摸事件被發(fā)送到當前所有的可顯示的部件却嗡。這意味著無論事件發(fā)生在部件的內(nèi)部與否舶沛,它都能收到事件。
如果你有關于其他GUI的開發(fā)經(jīng)驗稽穆,這可能是反直覺的冠王。一般典型的做法是劃分屏幕為幾何區(qū)域赶撰,并且只有坐標在部件區(qū)域內(nèi)部時才發(fā)送觸摸或鼠標事件到部件舌镶。
當使用觸摸事件工作時,這個要求變得非常的有限制性豪娜。強擊餐胀,縮放和長按可能來自想了解部件及其如何反應的外部。
為了提供最大的靈活性瘤载,Kivy發(fā)送事件到所有的部件否灾,并讓它們決定是否響應它們。如果你僅想在部件內(nèi)部響應觸摸事件鸣奔,你可以簡單的進行檢測:
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
#觸摸發(fā)生在部件區(qū)域的內(nèi)部墨技,做自己的相應
pass
(二)坐標
一旦你使用了一個帶有矩陣轉換的部件,你必須注意矩陣轉換挎狸。一些部件扣汪,例如分散(Scatter)有它們自己的矩陣轉換,這意味著觸摸必須能正確的傳遞觸摸坐標到Scatter的子部件锨匆。
- 從父空間到局部空間獲取坐標使用to_local()
- 從局部空間到父空間獲取坐標使用to_parent()
- 從局部空間到window空間使用:to_window()
- 從window空間到局部空間使用:to_widget()
你必須根據(jù)上下文使用上面的一個來轉換相應的坐標崭别。看分散(scatter)的實現(xiàn):
def on_touch_down(self, touch):
#將當前的坐標壓入恐锣,為了后面能存儲它
touch.push()
#轉換觸摸坐標到局部坐標
touch.apply_transform_2d(self.to_local)
#像平時一樣茅主,發(fā)送觸摸到子部件
#在touch里面的坐標是局部坐標
ret = super(..., self).on_touch_down(touch)
#無論結果如何,不要忘記調(diào)用之后彈出你的轉換土榴,
#這樣诀姚,坐標會變成父坐標
touch.pop()
#返回結果(依賴于你的所需)
return ret
(三)觸摸形狀
如果觸摸有一個形狀,它可以在'shape'屬性中體現(xiàn)$枨荩現(xiàn)在学搜,僅僅ShapeRect被暴露:
from kivy.input.shape import ShapeRect
def on_touch_move(self,touch):
if isinstance(touch.shape, ShapeRect):
print('My touch have a rectangle shape of size',
(touch.shape.width, touch.shape.height))
(四)雙擊
雙擊是指在一段時間和范圍內(nèi)輕點兩次娃善。它由doubletap post-processing模塊來計算。你能測試當前的觸摸是雙擊或不是:
def on_touch_down(self, touch):
if touch.is_double_tap:
print('touch is a double tap!')
print('- interval is', touch.double_tab_time)
print('- distance between previous is', touch.double_tap_distance)
(五)三擊
三擊是只在一段時間和一定范圍內(nèi)輕擊三次瑞佩。它由tripletap post-processing模塊來計算聚磺。你能測試當前的觸摸是否為三擊:
def on_touch_down(self, touch):
if touch.is_triple_tap:
print('Touch is a triple tap !')
print(' - interval is', touch.triple_tap_time)
print(' - distance between previous is', touch.triple_tap_distance)
(六)捕獲觸摸事件
對于父部件使用on_touch_down發(fā)送一個觸摸事件到子部件,它是可能的炬丸,但通過on_touch_move或on_touch_up就不可以瘫寝。這可能發(fā)生在特定的場景下,例如當一個觸摸事件發(fā)生在父部件邊框外部時稠炬,父部件決定不通知它的子部件焕阿。
當你捕獲了一個事件,你會總是收到移動(move)和彈起(up)事件首启,但是對于捕獲有一些限制:
- 你將會收到事件至少兩次:一次來自你的父部件(正常事件)暮屡,一次來自window(捕獲)。
- 你可能收到一個事件毅桃,但是不是來自你:它可能因為父部件發(fā)送給它的子部件褒纲。
- 觸摸坐標沒有轉換到你的部件空間,因為觸摸是來自Window钥飞。你需要收動轉換它們莺掠。
下面是一個例子來演示如何使用捕獲:
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
#如果觸摸檢測來自我們自己的部件,讓我們捕獲它读宙。
touch.grab(self)
# 并響應這次觸摸.
return True
def on_touch_up(self, touch):
#這里彻秆,你不用檢測觸摸碰撞或類似操作,
#你僅需要檢測是否它是一個捕獲的觸摸事件
if touch.grab_current is self:
#OK结闸,當前觸摸事件被派發(fā)給我們
#做一些感興趣的操作
print('Hello world!')
#不要忘記釋放掉唇兑,否則可能會有副作用
touch.ungrab(self)
#最后響應這次觸摸
return True
(七)觸摸事件管理
為了了解觸摸事件如何在部件間被控制和傳播,請參閱部件觸摸事件冒泡機制(Widget touch event bubbling)