编程向导4.4架构预览
我们将要花费一些时间以软件管理的角度来解释如何设计Kivy。这将是理解每一部分如何一起工作的关键。你也许浏览过源代码,也许有理一个粗糙的概念;但是看源代码也许是令人生畏的,因此这节内容将会详细解释一些基本的概念。你可以略过本节以待日后再看,但我们建议你至少大致浏览一下。
Kivy由几个模块组成,下面是Kivy架构图:
一、核心提供者和输入提供者
理解Kivy内部思想的关键是模块化和抽象。我们试图抽象一些基本的任务,例如打开一个窗口、展示图片和文本、播放音频、从摄像头获取图像、拼写检查等等。我们称它们为核心任务。这使得API既容易使用又容易扩展。更重要的是,它允许我们用:what we call,在你的应用程序运行时的不同场景指定不同的提供者。例如,在OS X,Linux和Windows系统上,针对不同的核心任务有不同的本地APIS。一方面这些APIS同操作系统进行交流,另一方面我们调用的Kivy的核心提供者扮演着中间交流层的角色。使用特殊核心提供者的便利之处是我们可以充分的使用操作系统暴露出来的功能,尽可能的使程序运行的更高效。它给用户提供了一个机会。另外,通过使用那些针对一个平台的库,我们可以高效地较低应用程序打包的尺寸。这也使得移植到别的平台更容易些。安卓端口从这方面就获利巨大。
我们利用同样的思路处理输入提供者。输入提供者,支持一些特殊的输入设备,例如苹果的trackpads,TUIO或者鼠标模拟器。如果你需要添加一个新的输入设备,你只需要简单的添加一个新的类来读取你的输入数据并且将其转换到Kivy的基本事件。
二、图形
Kivy的图像接口是我们对OpenGL的抽象。在更低的层面,Kivy使用OpenGL来分发硬件加速指令。对于一个初学者来说,写OpenGL代码也许是令人迷惑的。这也是我们提供图形API接口的原因,你只需要使用简单的封装好的方法(例如Canvas,Rectangle等等)进行绘画即可。
我们的所有部件本身就是使用这些图形接口,为了更高效的表现,它们在C语言执行。
另外一个便利之处是这些图形接口可以自动优化绘画指令。如果你不是一个OpenGL的专家,这将是很有用的帮助。在很多的场景下,它将使你的绘画代码更高效。
当然你也可以使用OpenGL命令。我们在所有的设备上使用的是OpenGL2.0(GLSE2),所以如果你想保持更好的跨平台的兼容性,我们建议你仅仅使用GLSE2提供的函数。
三、核心
核心提供者提供了一些通用的功能,例如:
- Clock(时钟):你可以使用时钟来计划定时器事件。一次性的定时器和周期性的定时器都被支持。
- Cache(缓存):如果你需要将你常用的一些东西缓存起来,你可以使用我们提供的缓存类,而不是自己再造一个轮子。
- Gesture Detection(手势检测):我们提供了一个简单的手势识别,你可以用来检测不同类型的轨迹,例如圆圈或者矩形。你可以自己训练它来检测自己的轨迹。
- Kivy Language(Kivy语言):Kivy语言可以容易并高效地描述用户接口。
- Properties(属性):这里的属性不是你熟知的Python里面的属性,它们是我们自己的属性类,以用来链接你的部件代码和用户接口描述。
四、UIX(部件和布局)
UIX模块包含常用的部件和布局,你可以重复使用它们来创建一个用户接口。
- Widgets:部件是用户接口元素,它们有的可见,有的不可见,例如文件浏览窗口、按钮、滑块、列表等等。部件接收MotionEvents。
- Layouts:你可以使用布局来排列部件。当然你可以手动的自己来布局部件,但是通常使用布局会更加的方便。布局包括网格布局、盒子布局等,你也可以布局嵌套。
五、模块
如果你曾经使用过现代的网页浏览器,并定制过一些插件,那么你已经了解了关于我们模块类的基本概念。模块用来被注入功能到Kivy程序中,即使原作者没有包括它。
一个例子展示了一个总是显示FPS的模块和一些描绘FPS的图表。
你也可以写自己的模块。
六、输入事件(触摸)
Kivy抽象了不同的输入类型和资源,例如触摸、鼠标、TUIO等等。所有这些输入类型的共同之处是你可以使用一个屏幕上的2D坐标以及一些特殊的输入事件关联起来。(一些其它的输入设备,例如加速器你就不能简单的使用2D坐标来描述。这类输入设备被分别对待)
所有的这些输入设备类型被Touch()类的实例来表示。(注意这不仅仅代表手指触摸,也同样表示别的输入类型。我们只是因为类似而采取了Touch的名字。)一个Touch的实例或对象,包含着三个状态,当一个触摸进入到了这些状态,你的程序被通知,该事件发生了。这三个状态如下:
- Down:一个触摸被按下一次
- Move:A touch can be in this state for a potentially unlimited time. A touch does not have to be in this state during its lifetime. (一个触摸可能在一个潜在的无限的时间里。一个触摸在它的生命周期中不会一直在这个状态。)当一个触摸的2D坐标发生改变时,Move将会事件发生。
gthank:前面的两句话不知道什么意思!
- Up:一个触摸抬起至多一次或没有。实际应用中,你将总会接收到一个Up事件,因为没有人会永远将一个手指放在屏幕上,但是这不是有保证的。如果你知道用户正在使用的输入源,你将会知道是否依赖这个状态。
七、部件和事件发送
部件这个术语通常用在GUI编程中,来描述和用户交互的那部分程序。在Kivy中,一个部件是一个对象由来接收输入事件。在屏幕上有一个可视的代表是没有必要的。所有的部件在一个部件树中管理:一个部件能有零或者多个子部件。但是在树的顶端只能有一个根部件,根部件没有父部件;其它的部件直接或间接的是根部件的子部件。
当一个新的输入数据可用时,Kivy发送一个事件。根部件首先收到该事件。根据触摸的状态,on_touch_down, on_touch_move, on_touch_up事件被发送到根部件,这将导致在根部件中相应的事件处理函数被调用。
在树中的每一个部件能选择吸收或传递事件。如果一个事件处理函数返回True,这意味着这个事件被吸收并正确的处理,对于该事件,没有进一步的处理会发生。否则,这个事件处理函数通过调用它的父类的各自事件处理函数的实现,传递部件到它自己的子部件上。最终事件传递到部件的基类,在它的事件处理函数里,什么到不做,仅仅传递touchs到它的子部件。
#这是一个move/up的模拟
def on_touch_down(self, touch):
for child in self.children[:]:
if(child.dispatch('on_touch_down', touch)):
return True
它可能比看起来更容易。下面的章节会展示一个例子,漂亮的应用程序能够快速的被创建。
通常你想限制一个部件监听触摸事件的屏幕区域。你可以使用部件的collide_point()方法来做到这点。你传递给它触摸的坐标,并且它将返回监听的区域。默认情况下,屏幕上矩形区域的检测使用部件的坐标和尺寸来描述,但是你也能在自己的类中进行重载。