標籤:

Kivy中文編程指南:架構概覽

本章我們將從軟體工程的角度,來簡單介紹一下Kivy的設計。這對於理解各個部分如何配合工作會有幫助。如果你只關注代碼,可能有時候會遇到這樣一種情況,就是你已經有了一個初步的想法了,但具體怎麼去實現可能還是一頭霧水,所以本章就針對這種情況,來更深入地講解一下Kivy的一些基本思想。你也可以先跳過這一章,等以後再翻回來看,不過我們建議開發者還是先看一下這些內容比較好,起碼可以快速略讀一下有個印象。

Kivy包含的若干個模塊,我們將對這些模塊一一進行簡要說明。下面這幅圖是Kivy整個架構的概括圖示:

核心模塊和輸入模塊

對理解Kivy的設計內涵,模塊化和抽象化的思想是至關重要的。我們試圖把各種基本的任務進行抽象,比如打開窗口、顯示圖像和文本、播放音頻、從攝像頭獲取圖像、拼寫校正等等。我們將這些部分稱為核心任務。這樣也使得API介面用起來比較簡單,擴展起來也容易。更重要的是,這種思路可以讓Kivy應用在運行的時候,使用各個運行平台所提供的對應功能的API介面。例如,在蘋果的MacOS操作系統、Linux操作系統和Windows操作系統中,就都有各自不同的原生API介面提供各種核心功能。所以就有一部分代碼,調用這些不同介面中的某一個,一方面與操作系統進行通信,另一方面與Kivy進行交互,起到中間人的角色,我們稱之為核心模塊。針對不同的操作系統平台要使用各自對應的核心模塊,這樣的好處是達到一種均衡狀態,既能夠充分利用操作系統提供的功能,又能盡量提高開發效率。(譯者註:我的理解是這樣大家平時不用針對不同操作系統去學習和使用各自的API,而只要專心使用Kivy的核心模塊進行調用就行了。)這也允許用戶來自由選擇,使用Kivy提供的核心模塊,或者直接使用各個操作系統的API介面。此外,由於使用了各個平台所提供的鏈接庫文件,我們大大減小了Kivy髮型版本的體積,也使得打包發布更加容易。這有助於將Kivy應用移植到其他平台。比如Android平台上的Kivy應用就體現了這一特性的好處了。

在輸入模塊這部分,我們也遵循了同樣的思路。輸入模塊,是一段代碼,用於針對各種輸入設備提供支持,比如蘋果公司的Trackpad觸摸板,TUIO多點觸摸設備,或者是滑鼠模擬器等等。如果你需要對某一種新的輸入設備添加支持,只需要提供一個新的類,用這個類來讀取輸入設備的數據,然後傳遞給Kivy基本事件,就可以了。

圖形介面

Kivy的圖形介面是對OpenGL的抽象。在最底層,Kivy使用OpenGL的命令來進行硬體加速的圖形繪製。不過寫OpenGL的代碼可還是挺複雜的,新手就更難以迅速掌握了。所以我們就提供了一系列的圖形介面,利用這些介面可以很簡單地進行圖形繪製,這些介面中用到了例如畫布Canvas、矩形Rectangle等幾何概念,比OpenGL裡面簡單不少。

Kivy自帶的所有控制項,都使用了這個圖形介面;出於性能的考慮,此圖形介面是用C語言來實現的。

這個圖形介面的另一個好處是可以對你代碼中的繪圖指令進行自動優化。這個很有用,尤其是在你對OpenGL的優化不太熟悉的情況下。這能讓你的繪圖代碼更高效。

當然了,你也可以堅持使用原生的OpenGL命令。目前Kivy在所有操作系統平台上用的都是是OpenGL 2.0 ES (GLES2),所以如果你希望保持跨平台的兼容性,我們建議你只是用GLES2兼容的函數。

核心模塊

核心模塊也就是kivy.core,這個包裡面提供了常用的各種功能,比如:

  • Clock

時鐘類,可以用於安排計時器事件。同時支持一次性計時和周期性計時。

  • Cache

If you need to cache something that you use often, you can use our class for that instead of writing your own.

緩存類,如果有一些經常用到的數據需要緩存,就可以用這個類,而不用自己寫了。

  • Gesture Detection

手勢識別,這個可以用來識別各種划動行為,比如畫個圓圈或者方塊之類的。可以訓練來識別你自己設計的圖形。

  • Kivy Language

Kivy語言,這個是用來簡潔高效地描述Kivy應用的用戶界面的。

  • Properties

這裡這些屬性和Python語言中的屬性不同。這裡是我們專門寫的一些類,通過用戶界面描述來連接控制項代碼。

UIX(控制項和布局)

UIX用戶界面模塊,包含了常用的各種控制項和布局,可以通過復用來快速構建用戶界面。

  • Widgets 控制項

控制項是各種用戶界面元素,可以添加到程序中來提供各種功能。有的可見,有的不可見。文件瀏覽器,按鈕、滑動頁、列表等等,這都屬於控制項。控制項接收動作事件。

  • Layouts 布局

布局是控制項的排列方式。當然,你也可以自己自定義控制項的位置,不過從我們提供的預設布局中選擇一個來使用,會更方便很多。網格布局、箱式布局等等,都是布局了。你還可以試試複雜的多層網狀布局。

模塊化

如果你用某一種現代的網路瀏覽器,並且通過一些附加組件對其進行定製,那麼你應該就理解了我們提供的各種模塊類的基本思想了。各種模塊可以用來向Kivy程序中添加功能,即便原作者沒有提供的功能也可以加進去了。

例如,有一個模塊就能顯示當前應用的FPS(Frame Per Second,每秒幀數,即幀率),然後還能統計一段時間的FPS變化。

你可以自己寫各種模塊添加到應用中。

輸入事件(Touches)

Kivy抽象了各種輸入類型和輸入設備,比如觸控,滑鼠按鍵,多點觸摸等等。這些輸入類型有一個共同點,就是都可以把各種輸入事件映射成屏幕上對應的一種2D形態。(當然了,還有的輸入設備就沒法用2D形態來表示,比如通過加速度感測器來衡量設備傾斜角度等。這種情況就得另外考慮了。下面我們討論的只是那些能用2D形態表示的輸入事件,複雜的類型以後再說。)

這些輸入類型,在Kivy中都用Touch()類的實例來表示。(請注意,這裡可不僅僅是針對手指去觸摸的那種touch,而是所有可以這樣抽象表示的輸入事件。這裡用Touch只是為了方便而這麼簡稱一下。就想像一下,這些Touches就是在用戶界面或者顯示屏上面的那些個點擊行為。 )Touch的實例或者對象,有三種狀態。當這個Touch進入了其中的某一個狀態,你的程序就會被告知此事件的發生。Touch的三種狀態如下:

  • Down 落下

處於落下狀態,只能有一次,就是在發生Touch事件的初始時刻。

  • Move 移動

這個狀態的時間無上限。在一個Touch的生命周期中可以沒有這個狀態。移動狀態只發生在Touch的2D平面位置發生變化的情況下。

  • Up 抬起

A touch goes up at most once, or never. In practice you will almost always receive an up event because nobody is going to hold a finger on the screen for all eternity, but it is not guaranteed. If you know the input sources your users will be using, you will know whether or not you can rely on this state being entered.

一個Touch要麼只能抬起一次,要麼就不發生。而實際應用中你會經常遇到Up時間,因為沒有人會一直把手指按到屏幕上,不過也有未必就絕對不會有這種情況。若事先知道用戶用的輸入設備,就可以確定能否完全依靠用戶的輸入狀態。

(譯者註:以手指觸摸屏幕為例,只有開始接觸的時候是Down手指落下這個狀態,之後移動就是接下來的Move移動狀態,手指抬起來的時候就是Up即抬起狀態了;如果以滑鼠左鍵點擊為例,按下去左鍵的時候是Down,按住左鍵不放進行拖動就是Move,鬆開左鍵就是Up了。這段我特別解釋一下,因為自己翻譯的太生硬了。)

控制項和事件調度

在圖形化的軟體開發語境下,控制項這個詞經常出現,一般是來描述程序中用於和用戶進行交互的組件。在Kivy中,控制項是用來接收各種輸入事件的。所以並不一定非要在屏幕上能看得到。Kivy當中所有控制項都以控制項樹的形式來管理,學過計算機科學中數據結構相關知識的話,就會意識到這是一種樹形結構:一個控制項可以有任意多個子控制項,也可以沒有子控制項。根控制項就只能有一個,處於樹形結構的頂端,根控制項不具有父控制項,並且所有其他控制項都是根控制項的直接或者間接子控制項(就像樹根一樣,所以叫根控制項)。

當新的輸入數據可用的時候,Kivy會針對每一個Touch發出一個事件。控制項樹種的根控制項首先接收到這個事件。Touch的不同狀態,on_touch_down, on_touch_move和on_touch_up (Down落下、Move移動和Up),會作為Touch的參數,提供給根控制項,根控制項會調用對應的事件Handler來作出反應。

包括根控制項在內,控制項樹種的每個控制項都可以有兩種選擇,處理該事件,或者將該事件傳遞下去。如果一個事件的Handler返回True,就意味著這個事件已經被接收並妥善處理。這個事件就也到此為止了。如果不是這樣,事件的Handler會跳過此處的空間,調用父類中的對應事件的Handler實現,傳遞給該控制項的子控制項。這樣的過程可以一路走到最基礎的控制項類Widget,在它的Touch事件Handler中,只是把Touch傳遞給子控制項,而不進行其他的操作。

# This is analogous for move/up:ndef on_touch_down(self, touch):n for child in self.children[:]:n if child.dispatch(on_touch_down, touch):n return Truen

說起來挺麻煩,看上去挺複雜,實際上要簡單得多。下一章就會講解如何使用這種特性來快速創建應用了。

經常有一種情況,就是你可能要讓一個控制項只在屏幕上某個特定的區域來監聽Touch事件。這時候就可以使用控制項的collide_point()方法來實現此目的。只需要把Touch的位置發給該方法,然後如果此位置位於監聽區域則返回True,反之返回False。默認情況下,這個方法會監聽屏幕上的一個矩形區域,根據控制項的中心坐標(x & y坐標系),以及空間尺寸(寬度和高度),不過你也可以用自己的類覆蓋掉這一行為。


推薦閱讀:

TAG:Python | Kivy |