2.4 Python
Python 是一種可用戶于多種類型軟件開發(fā)的動(dòng)態(tài)面向?qū)ο缶幊陶Z(yǔ)言,它提供了強(qiáng)大的與其它語(yǔ)言和工具相互協(xié)作支持,擁有廣泛的標(biāo)準(zhǔn)庫(kù),而且你可以在幾天之內(nèi)上手。很多Python 程序員都反映使用Python 獲得了更高的生產(chǎn)力,更強(qiáng)壯的代碼以及更易維護(hù)的特性。
2.4.1 導(dǎo)讀
這一節(jié)來闡述兩種偉大的技術(shù):多點(diǎn)觸摸用戶界面和Python 編程語(yǔ)言。重點(diǎn)講一下如何用Python 開發(fā)多點(diǎn)觸摸應(yīng)用程序。
現(xiàn)在介紹一下Python 里面的幾個(gè)模塊PyMT 以及演示一下它的范例,然后嘗試去寫一下多點(diǎn)觸摸輸入的小程序。PyMT 是一個(gè)開源的、用于快速開發(fā)多點(diǎn)觸摸應(yīng)用程序的Python 模塊。
在許多方面,Python 和多點(diǎn)觸摸在使電腦更容易操作這個(gè)方面用諸多的相同點(diǎn)。雖然作為幾種編程語(yǔ)言,Python 需要更先進(jìn)的技術(shù),來讓開發(fā)者能夠快速而又輕松地編寫代碼。Python 常常說比其它語(yǔ)言更容易學(xué)習(xí),許多Python 程序員也反映使用這種語(yǔ)言使他們更專注地解決問題、實(shí)現(xiàn)目的,而不是拘泥于繁雜的語(yǔ)法和嚴(yán)格的語(yǔ)言屬性。Python 強(qiáng)調(diào)其代碼的可讀性和可以從開源社區(qū)獲取大量的可用模塊,Python 試圖用簡(jiǎn)單和有趣的編程方式構(gòu)建多點(diǎn)觸摸用戶界面,來讓電腦變得更加自然和直觀地使用。
雖然多點(diǎn)觸摸的實(shí)用性得到了快速的發(fā)展,但是就目前的互動(dòng)方式和應(yīng)用來看,多點(diǎn)觸摸還有極大的潛力等待我們?nèi)ネ诰颉S绕涫乾F(xiàn)在很多的多點(diǎn)觸摸項(xiàng)目仍然處于在實(shí)驗(yàn)室內(nèi)的階段,所以能夠最大限度的快速構(gòu)建多點(diǎn)觸摸交互和應(yīng)用藍(lán)圖是非常重要的。Python 的動(dòng)態(tài)語(yǔ)言特性和快速開發(fā)的能力以及社區(qū)里的大量模塊,都使得Python 成為快速開發(fā)多點(diǎn)觸摸交互和應(yīng)用的理想語(yǔ)言。
2.4.2 Python多點(diǎn)觸摸的模塊和項(xiàng)目(Modules and Projects)
以下簡(jiǎn)要概述Python 里面涉及到多點(diǎn)觸摸的模塊和項(xiàng)目。
PyMT
PyMT 是一個(gè)用于開發(fā)多點(diǎn)觸摸的Python 模塊,可以與OpenGL 通信。它最初是一個(gè)愛荷華州大學(xué)的研究項(xiàng)目,最近在很多團(tuán)體和個(gè)人的大力支持和共同開發(fā)下,PyMT 一直保持著較快地發(fā)展。它可以運(yùn)用在Windows,OS X 和Linux操作系統(tǒng)下,PyMT 的許可證是GPL License。我們將在下賣面的章節(jié)中詳細(xì)探討PyMT。
touchPy
Python 框架是與TUIO 協(xié)議協(xié)同工作的。touchPy 監(jiān)聽TUIO 的輸入,并執(zhí)行觀察員模式(Observer pattern),開發(fā)人員可以使用其子類。觀察員模式使得touchPy 平臺(tái)和模塊并不可知,例如它并不關(guān)心是哪一個(gè)框架來繪制屏幕。AlexTeiche 為它寫了一個(gè)很重量級(jí)的教程。
PointIR
PointIR 是在2008 年P(guān)yCon 上演示的多點(diǎn)觸摸系統(tǒng),基于Python。雖然當(dāng)年看上去非常有希望,但是最近似乎從網(wǎng)絡(luò)上消失了(搜索試試?)。授權(quán)信息也不清楚。
libAVG
根據(jù)官網(wǎng)的描述:“libAVG 是一個(gè)高層次的媒體開發(fā)平臺(tái),專注于互動(dòng)裝置。”雖然從嚴(yán)格的意義上講,它不算是多點(diǎn)觸摸模塊。但是它卻能夠接收TUIO的信息和追蹤觸點(diǎn),所以可以作為多點(diǎn)觸摸系統(tǒng)來使用。libAVG 可以在Mac OSX 和Linux 上運(yùn)用,它也是開源的,許可證是LGPL。
pyTUIO
一個(gè)用于接收和分析TUIO 輸如的Python 模塊。在MIT 許可證下發(fā)布。
2.4.3 PyMT
PyMT 是一個(gè)用來開發(fā)多點(diǎn)觸摸富媒體OpenGL 應(yīng)用的Python 模塊。主要目標(biāo)是使開發(fā)變得更新奇,簡(jiǎn)便和快速自定義界面。本節(jié)將介紹討論P(yáng)yMT 詳細(xì)的結(jié)構(gòu),并使用它來作為一個(gè)例討論一些參與編寫多點(diǎn)觸摸界面的若干問題。
每一個(gè)有用的程序都做過兩件事:獲得輸入和提供輸出。如果沒有輸入,程序就只會(huì)輸出相同的結(jié)果(例如"Hello World!"),那么這個(gè)程序是沒有用的。如果沒有輸出,那么沒人知道程序在做什么,或者根本就沒有做什么。運(yùn)用在多點(diǎn)觸摸顯示屏上的程序,最主要的輸入方式就是觸摸。圖形的顯式就是其主要輸出。PyMT 嘗試使輸入和輸出變得簡(jiǎn)單和靈活。在處理輸入方面,PyMT 將TUIO 協(xié)議包裝成一個(gè)事件驅(qū)動(dòng)的框架,輸出方面則是基于OpenGL,這樣的話就可以使用圖形硬件加速,在繪制圖形上獲得最大的自由性。
2.4.4 PyMT的構(gòu)造
圖5 展示的是PyMT 的主要結(jié)構(gòu),如前面所講的,它使用TUIO/OpenGL作為輸入/輸出。當(dāng)前版本的PyMT 依靠pyglet,這是一個(gè)跨平臺(tái)的OpenGL 窗⼝和多媒體Python 庫(kù),特別是pyglet 的多媒體功能使得處理圖像、音頻和視頻變得非常容易。大多數(shù)的媒體問件可以裝載至單線(single line)的Python 代碼中(也包括繪制的OpenGL 內(nèi)容的重現(xiàn))。
圖5:PyMT 結(jié)構(gòu)。PyMT 建立在Pyglet 基礎(chǔ)之上,其中規(guī)定了OpenGL 的窗⼝和多媒體加載的各種問件格式。它同樣通過TUIO 客戶端監(jiān)聽輸入信息。PyMT 將這些技術(shù)組合在一起,提供了一個(gè)面向?qū)ο蟮臉?gòu)件庫(kù)和分級(jí)系統(tǒng)布局和事件傳播。
2.4.5OpenGL
利用OpenGL 作為繪圖后端利弊并存。雖然它可以在2D 和3D 渲染上擁有高性能和很好的靈活性,但是在畢竟處于比較低級(jí)的狀態(tài)。而且它的學(xué)習(xí)曲線比較陡峭,也需要有基本的計(jì)算機(jī)圖形學(xué)知識(shí)。
PyMT 試圖抵消需要比較高級(jí)的知識(shí)才能駕馭的OpenGL 繪圖功能,提供了基本的繪圖功能。PyMT 包括drawCircle 、drawRectangle 、drawLine 和drawTexturedRectangle 函數(shù)以及其它無需高級(jí)OpenGL 知識(shí)的功能。使用OpenGL 作為基本的渲染引擎,可以使高級(jí)用戶充分控制他們的程序的可視化輸出。PyMT 也提供了輔助函數(shù)和類去幫助實(shí)現(xiàn)高級(jí)的OpenGL 編程。例如,PyMT可以用一行程序?qū)崿F(xiàn)建立一個(gè)幀緩沖對(duì)象(Frame Buffer Object)和從glsl 著色。PyMT 還能利用引擎來處理投影矩陣和布局轉(zhuǎn)變,這樣就可以在不用調(diào)節(jié)他們參數(shù)的情況下直接運(yùn)行。這個(gè)構(gòu)思的原因是希望能夠在大多數(shù)情況下能夠最大限度的方便運(yùn)用,但是如果需要用到更高級(jí)的技術(shù)或者需要自定義相關(guān)技術(shù)則必須要用到OpenGL 來編寫。
對(duì)OpenGL 的描述還有其它更加詳細(xì)的資料,關(guān)于OpenGL 的簡(jiǎn)單信息可以從[12][13]查閱到,標(biāo)準(zhǔn)的參考書是“The Red Book”,或者從nehe.gamedev.net網(wǎng)站上查閱經(jīng)典的OpenGL 指導(dǎo)。
2.4.6Windows、Widgets、Events
大多數(shù)圖像用戶界面的開發(fā)包和框架都有一個(gè)叫作“工具(widget)”的概念,一個(gè)“工具(widget)”可以在同一個(gè)圖形用戶界面中構(gòu)建模塊,它是一個(gè)富有交互性及可視化的元素。在維基中是如下定義的:
PyMT 和其它GUI 工具板一樣使⽤類似的概念,它以類似框架式的大部分提供一系列可以用于多點(diǎn)觸摸的工具。但PyMT 的重點(diǎn)在于讓程序員很容易地支持自定義工具以及嘗試開發(fā)新的互動(dòng)技術(shù),而不是提供一套標(biāo)準(zhǔn)的工具。這是來自如下建議和設(shè)想的主要的動(dòng)機(jī):
在用戶自然界面(NUI)里面只有極少數(shù)的“工具”和交互設(shè)計(jì)應(yīng)用證明了自己是標(biāo)準(zhǔn)的。
NUI 本身就不同于傳統(tǒng)的GUI/WIMP(窗⼝、圖標(biāo)、菜單、指向設(shè)備)
NUI 是非常具有內(nèi)容化的,例如:可視化信息和交互應(yīng)用是基于用戶交互背景的。
傳統(tǒng)的鼠標(biāo)鍵盤系統(tǒng)已經(jīng)無法提供實(shí)時(shí)的多用戶協(xié)和操作,需要顛覆式地構(gòu)思新的交互界面。
PyMT 運(yùn)用時(shí)以樹狀目錄組織widget. 根目錄是應(yīng)用程序窗⼝(一般是MTWindow). Widget 可以通過MTwidget 基類的add_widget 方法加進(jìn)這樣的目錄中或者加進(jìn)其他widget 中.各種事件如on_draw, on_resize, mouse event 和touchevent 可以通過這個(gè)樹狀目錄到達(dá)所有的widget. 程序員可以利用這些層級(jí)機(jī)構(gòu)來定義各種容器(container), 用以處理布局和控制widget 對(duì)事件的響應(yīng).
PyMT 提供了豐富的功能強(qiáng)大的widget 和實(shí)用的對(duì)象(object). 比如,所有的widget 可以用CSS 來定義風(fēng)格. 除了用add_widget 來生成widget 之外,還可以用XML 來定義層級(jí)結(jié)構(gòu),然后自動(dòng)生成widget. 由于PyMT 的功能實(shí)在太多,無法意義介紹,詳情請(qǐng)參閱PyMT API 文檔.
接下來的小例子將嘗試展示PyMT 的關(guān)鍵概念. 圖2 展示了該例子的最終效果,用一只手獲取5 個(gè)輸入點(diǎn). 這段代碼可以分為4 個(gè)部分. 評(píng)價(jià)一個(gè)NUI 交互系統(tǒng)的唯一也是最有效的方法就是親自去做,去體驗(yàn).
1. 導(dǎo)如PyMT 并初始化參數(shù):第一行代碼告訴python,我們要用PyMT,這行代碼將會(huì)加載所有的PyMT 對(duì)象和函數(shù)。這段代碼同時(shí)還設(shè)置了一個(gè)名叫touch_positions 的變量。這個(gè)變量用以存儲(chǔ)觸摸事件的坐標(biāo)位置。
2. 定義一個(gè)新的類,名叫TestWidget:這個(gè)類繼承自MTWidget 并定義了4個(gè)事件處理器。on_touch_down 和on_touch_up 事件處理器更新觸摸位置。on_touch_up 處理器從觸摸列表中刪除觸摸。draw 方法會(huì)在每個(gè)觸摸事件的當(dāng)前位置生成一個(gè)半徑為40 的圓。這個(gè)圓通過PyMT 的drawCircle 方法,并調(diào)用touch_positions 的值生成。
3. 創(chuàng)建一個(gè)窗⼝來裝載widget:MTWindow 是一個(gè)應(yīng)用程序窗⼝,你可以通過add_widget 方法來加載widget。被加載的widget 會(huì)通過窗⼝接收touch 和draw事件,并渲染該窗⼝。
4. 啟動(dòng)程序:runTouchApp 函數(shù)會(huì)啟動(dòng)PyMT 的主程序,同時(shí)任命窗⼝,打開TUIO 偵聽器,并開始發(fā)送事件。
程序1:PyMT 程序⽰例,在每個(gè)觸點(diǎn)處⽣成⼀個(gè)紅⾊的圓。
圖6:程序1 所⽰程序運(yùn)⾏截圖。5 個(gè)觸點(diǎn)(屏幕分辨率:640x480)
2.4.7 多點(diǎn)觸摸輸入編程
基于多點(diǎn)觸摸設(shè)備的編程和交互設(shè)計(jì)與基于目標(biāo)的界面的情況完全不同。前面章節(jié)討論了NUI(自然交互界面)與傳統(tǒng)的基于GUI(圖形界面)的WIMP的區(qū)別。主要的區(qū)別在于多點(diǎn)觸摸給交互界面帶來的無限的拓展和可能性,同時(shí)也讓編程變得更加復(fù)雜。
TUIO 方式,以及其他多點(diǎn)觸摸協(xié)議和框架定義了3 種基本的觸摸消息/事件。包括新的觸摸事件,現(xiàn)有觸摸事件的移動(dòng),觸摸事件的移除。這些消息總是被標(biāo)明“touchID”,這樣程序以及這些事件調(diào)用函數(shù)就能把這些觸摸事件各自區(qū)分開來。如程序1 中所示,這些觸摸事件以下面所示的方式到達(dá)PyMT:
· on_touch_down(touches, touchID, x, y)
· on_touch_move(touches, touchID , x, y)
· on_touch_up(touches, touchID, x, y)
每個(gè)事件都攜帶著touchID 和x,y 坐標(biāo)值。通過這些touchID 和坐標(biāo)值,系統(tǒng)就區(qū)目⽬前所有的觸點(diǎn)及其位置。實(shí)際上,系統(tǒng)不僅能區(qū)分目前所有觸點(diǎn)的位置,還記錄著目前每個(gè)觸點(diǎn)的移動(dòng)和加速度,這些都由TUIO 協(xié)議定義。PyMT也為TUIO 對(duì)象提供on_object_*事件(例如:圖形標(biāo)簽的識(shí)別)。
很明顯,解釋多點(diǎn)觸摸要比處理單一指點(diǎn)設(shè)備如目標(biāo)復(fù)雜得多。任何一個(gè)觸點(diǎn)都會(huì)影響到用戶界面,隨后的事件處理器在決定如何處理觸摸事件之前必須兼顧所有其他可能發(fā)生的相互作用戶。
在進(jìn)行PyMT 編程的時(shí)候,一些基本的編程技巧已經(jīng)被證實(shí)很有幫助。例如,讓某個(gè)widget 對(duì)某個(gè)特定的觸摸擁有所有權(quán)在很多案例中被證明很有效。當(dāng)使用這個(gè)技巧的時(shí)候,其他的widget 會(huì)忽略某個(gè)特定touch_ID 所對(duì)應(yīng)的touch_move 或者touch_up 事件。只有對(duì)這個(gè)事件擁有所有權(quán)的widget 才會(huì)處理這些事件。
基本上,多點(diǎn)觸摸和基于多點(diǎn)觸摸的交互界面潛力無限,如果一定要有什么限制的話,那么,這些限制就是我們的創(chuàng)造力和想象力。多點(diǎn)觸摸的手勢(shì)有無窮的組合方式。在接下來的例子里,你將看到,這些手勢(shì)及其組合可以形成很多直觀的交互操作,但是,這也意味著邏輯和算法變得更加復(fù)雜,更加困難。
2.4.8 例子:實(shí)現(xiàn)旋轉(zhuǎn)/縮放/移動(dòng)
這一部分,我們將討論如何實(shí)現(xiàn)一個(gè)著名的操作。旋轉(zhuǎn)/縮放/移動(dòng)(圖7)是最常見的多點(diǎn)觸摸演示示例。這種直觀的,用兩個(gè)手指旋轉(zhuǎn)/縮放/移動(dòng)一個(gè)二維物體的方式,和我們?cè)谧烂嫔弦苿?dòng)一張紙的情況很相似。我對(duì)這個(gè)方式感覺很直觀,很自然然,因?yàn)閮蓚€(gè)觸點(diǎn)始終在物體上一開始的那個(gè)位置,無論手指移動(dòng)到哪⾥。
在PyMT 中,這個(gè)交互方式以ScatterWidget 對(duì)象實(shí)現(xiàn),你可以在這個(gè)對(duì)象中添加其他widget,讓他們成為scatter widget 的一部分。誠(chéng)然,這個(gè)交互方式已經(jīng)用了太多次了,有人或許會(huì)說這個(gè)方式已經(jīng)用爛了。我們?cè)谶@里討論這種交互方式不是為了炫耀,而是因?yàn)樗且粋€(gè)非常不錯(cuò)的例子,一個(gè)典型的證明多點(diǎn)觸摸編程復(fù)雜性的例子。盡管目前只用了兩個(gè)觸點(diǎn),而整個(gè)交互非常自然,但是這里面涉及到的計(jì)算和數(shù)學(xué)知識(shí)已經(jīng)比鼠標(biāo)交互復(fù)雜了很多。
為了理解這個(gè)交互的具體實(shí)現(xiàn),需要對(duì)矩陣變換有基本的了解。通過矩陣乘法,任何變形都可以通過一個(gè)矩陣實(shí)現(xiàn)(一般來說是4x4 矩陣)。比如一個(gè)矩陣表示沿x 軸移動(dòng)5 個(gè)單位,可以乘上一個(gè)繞y 軸旋轉(zhuǎn)90 度的矩陣。得到的矩陣則表示在沿x 周移動(dòng)5 個(gè)單位的同時(shí),繞y 軸旋轉(zhuǎn)90 度。更多矩陣相關(guān)信息,參見[18]。
2.4.9 用矩陣變換來畫圖
程序2a 是做旋轉(zhuǎn)/縮放/移動(dòng)的第一部分。這一部分用于生成對(duì)象。Transform_mat 是一個(gè)變形矩陣。到目前為至它只用來保存標(biāo)準(zhǔn)矩陣,讓所有的點(diǎn)保持不變。基于觸點(diǎn)的位置和移動(dòng),對(duì)變形矩陣進(jìn)行修改,達(dá)到改變對(duì)象的目的。
程序2a.旋轉(zhuǎn)/縮放/移動(dòng)實(shí)例。當(dāng)生成對(duì)象的時(shí)候,應(yīng)用了一個(gè)變形矩陣,這個(gè)矩陣會(huì)隨著觸點(diǎn)位置的變化發(fā)生改變。詳細(xì)的代碼參見pymt/ui/scatter.py 參考文獻(xiàn)[2]
2.4.10 確定參數(shù)并計(jì)算變形
接下來的問題就是決定如何進(jìn)行變換對(duì)象的變形矩陣。圖8 描述了需要變換的部分參數(shù)。重要的一點(diǎn)是,對(duì)任何一個(gè)給定的事件,兩個(gè)觸點(diǎn)中,只有其中一個(gè)可以移動(dòng)。因?yàn)椋總€(gè)事件一次只能傳送一個(gè)觸點(diǎn)的信息。
為了計(jì)算變換,需要以下參數(shù)。事件發(fā)生前和事件發(fā)生后兩個(gè)觸點(diǎn)的距離(d1 和d2)。角度R,用來計(jì)量對(duì)象的旋轉(zhuǎn)角度。旋轉(zhuǎn)和縮放中芯點(diǎn)Pi(兩個(gè)觸點(diǎn)的其中一個(gè))
圖8 旋轉(zhuǎn)/縮放/移動(dòng)。兩個(gè)觸點(diǎn)(A 和B),對(duì)多個(gè)參數(shù)進(jìn)行計(jì)算。縮放值(d2/d1), 旋轉(zhuǎn)角度(R), 旋轉(zhuǎn)和縮放中心(Pi).每次只有一個(gè)觸點(diǎn)位置發(fā)生改變(A=Pi 或者B=Pi)
通過對(duì)參數(shù)的計(jì)算, 變形矩陣會(huì)做出相應(yīng)的修改。程序2 展示了PyMT/OpenGL 是如何完成這些的。具體的步驟如下:
1. 初始矩陣被裝載進(jìn)transform_mat
2. 通過變換,讓旋轉(zhuǎn)中心的坐標(biāo)為(0,0),因?yàn)椋贠penGL 中,旋轉(zhuǎn)和縮放總是圍繞原點(diǎn)進(jìn)行。
3. 繞z 軸旋轉(zhuǎn)(z 軸垂直屏幕)
4. 縮放
5. 把所有的物體移動(dòng)到當(dāng)初的位置
另外一件值得注意的事是,對(duì)象沒有發(fā)生移動(dòng)(平移)。運(yùn)用這種技術(shù),物體的運(yùn)動(dòng)通過繞固定的點(diǎn)旋轉(zhuǎn)或者在不同方向上縮放實(shí)現(xiàn)。如果要移動(dòng)對(duì)象的話, 比如用一個(gè)手指拖動(dòng), 那么這個(gè)拖動(dòng)的動(dòng)作必須添加到PyMT 的ScatterWidget 類中去。為了保持例子的簡(jiǎn)潔,這里就不深入探討。
程序2b。應(yīng)用變形參數(shù)示例。這個(gè)函數(shù)把變形參數(shù)應(yīng)用到變形矩陣上。完整的程序見pymt/ui/scatter.py 參考文獻(xiàn)[2].
2.4.11 觸摸位置轉(zhuǎn)譯成計(jì)算參數(shù)
最終,程序必須根據(jù)觸摸事件提供的位置單獨(dú)計(jì)算參數(shù)。程序2c 展示了相應(yīng)代碼。為了簡(jiǎn)潔,這段代碼假設(shè)get_second_touch 函數(shù)用于獲取第一個(gè)觸點(diǎn)的信息。這樣的程序可以通過一個(gè)庫(kù)來跟蹤記錄touchID,確保每次只有兩個(gè)點(diǎn)在這個(gè)庫(kù)中,然后返回與被傳遞的參數(shù)不同的那個(gè)。
這段代碼也假設(shè)了一些基本的矢量函數(shù),用于計(jì)算兩點(diǎn)間的距離和角度。具體的實(shí)現(xiàn)參見PyMT 的vector 類[2]。或者參考[20][21]。這個(gè)計(jì)算基本上只是兩個(gè)向量的大小和乘積。
旋轉(zhuǎn)的角度通過A1-B 和A2-B 得出。縮放的比例通過d2/d1 得出。
程序2c。計(jì)算旋轉(zhuǎn)/縮放/移動(dòng)參數(shù)的程序示例。詳細(xì)代碼見pymt/ui/scatter.py 附錄[2]。