架构概述

我们想花点时间从软件工程的角度解释一下我们是如何设计 Kivy 的。这是理解一切如何协同工作的关键。如果您只看代码,您很可能已经有了一个大概的想法,但由于这种方法对大多数用户来说肯定是令人生畏的,因此本节将更详细地解释实现的基本思想。您可以跳过此部分并在以后参考它,但我们建议至少略读一下以获得粗略的概述。

Kivy 由几个构建块组成,我们将在稍后进行解释。以下是架构的图形摘要:

核心提供者和输入提供者

理解 Kivy 内部结构的一个关键思想是模块化和抽象。我们尝试抽象基本任务,例如打开窗口、显示图像和文本、播放音频、从相机获取图像、拼写校正等。我们称这些为核心任务。这使得 API 既易于使用又易于扩展。最重要的是,它允许我们为您的应用程序运行的各个场景使用——我们称之为——特定的提供者。例如,在 macOS、Linux 和 Windows 上,针对不同的核心任务有不同的原生 API。一段代码使用这些特定的 API 一方面与操作系统对话,另一方面与 Kivy 对话(作为中间通信层)就是我们所说的核心提供. 为每个平台使用专门的核心供应商的好处是我们可以充分利用操作系统公开的功能并尽可能高效地行动。也给了用户选择的余地。此外,通过使用任何一个平台附带的库,我们有效地减少了 Kivy 发行版的大小并使打包更容易。这也使得将 Kivy 移植到其他平台变得更加容易。Android 端口从中受益匪浅。

我们在输入处理方面遵循相同的概念。输入提供程序是一段代码,用于添加对特定输入设备的支持,例如 Apple 的触控板、TUIO 或鼠标模拟器。如果您需要添加对新输入设备的支持,您可以简单地提供一个新类,它从您的设备读取您的输入数据并将它们转换为 Kivy 基本事件。

图形

Kivy 的图形 API 是我们对 OpenGL 的抽象。在最底层,Kivy 使用 OpenGL 发出硬件加速绘图命令。然而,编写 OpenGL 代码可能会有些混乱,尤其是对于新手而言。这就是我们提供图形 API 的原因,它允许您使用 OpenGL 中不存在的简单隐喻(例如 Canvas、Rectangle 等)来绘制事物。

我们所有的小部件本身都使用这个图形 API,出于性能原因,它是在 C 级别上实现的。

图形 API 的另一个优势是它能够自动优化您的代码发出的绘图命令。如果您不是调整 OpenGL 的专家,这将特别有用。在许多情况下,这会使您的绘图代码更加高效。

当然,如果愿意,您仍然可以使用原始 OpenGL 命令。我们的目标版本是所有设备上的 OpenGL 2.0 ES (GLES2),因此如果您想保持跨平台兼容,我们建议您只使用 GLES2 功能。

核心

核心包中的代码提供了常用的功能,例如:

您可以使用时钟来安排计时器事件。支持一次性定时器和周期性定时器。

缓存

如果你需要缓存你经常使用的东西,你可以使用我们的类来代替你自己写。

手势检测

我们提供了一个简单的手势识别器,您可以使用它来检测各种笔画,例如圆形或矩形。你可以训练它来检测你自己的笔画。

Kivy 语言

kivy 语言用于轻松高效地描述用户界面。

特性

这些不是您可能从 Python 了解到的普通属性。它们是我们自己的属性类,将您的小部件代码与用户界面描述联系起来。

UIX(小部件和布局)

UIX 模块包含常用的小部件和布局,您可以重复使用它们来快速创建用户界面。

小部件

小部件是您添加到程序中以提供某种功能的用户界面元素。它们可能可见,也可能不可见。例如文件浏览器、按钮、滑块、列表等。小部件接收 MotionEvents。

布局

您使用布局来排列小部件。当然可以自己计算小部件的位置,但通常使用我们现成的布局之一更方便。示例是网格布局或框布局。您还可以嵌套布局。

模块

如果您曾经使用过现代网络浏览器并使用一些附加组件对其进行自定义,那么您已经了解我们模块类背后的基本思想。模块可用于将功能注入 Kivy 程序,即使原作者没有包含它。

一个例子是始终显示当前应用程序的 FPS 的模块和一些描述 FPS 随时间变化的图表。

您也可以编写自己的模块。

输入事件(触摸)

Kivy 抽象出不同的输入类型和来源,例如触摸、鼠标、TUIO 或类似的。所有这些输入类型的共同点是您可以将 2D 屏幕位置与任何单独的输入事件相关联。(还有其他输入设备,例如加速度计,您无法轻易找到 2D 位置,例如设备的倾斜。这种输入是单独处理的。在下文中,我们将描述前一种类型。)

所有这些输入类型都由 Touch() 类的实例表示。(请注意,这不仅指手指触摸,还指所有其他输入类型。为了简单起见,我们将其称为触摸。将它想象成触摸用户界面或屏幕的东西。)触摸实例,或对象,可以处于三种状态之一。当触摸进入这些状态之一时,您的程序会收到事件发生的通知。触摸可以处于的三种状态是:

Down

触摸只有一次,在它第一次出现的那一刻。

Move

触摸可以在这种状态下保持无限长的时间。触摸在其生命周期内不必处于此状态。只要触摸的 2D 位置发生变化,就会发生“Move”。

Up

触摸最多一次,或者永远不会。实际上,您几乎总是会收到 up 事件,因为没有人会永远将手指放在屏幕上,但这并不能保证。如果您知道您的用户将使用的输入源,您将知道您是否可以依赖正在输入的这种状态。

小部件和事件调度

术语小部件通常用于 GUI 编程上下文中,以描述用户与之交互的程序的某些部分。在 Kivy 中,小部件是一个接收输入事件的对象。它不一定必须在屏幕上有一个可见的表示。所有小部件都排列在小部件树中(这是一种计算机科学课程中已知的树数据结构):一个小部件可以有任意数量的子小部件,也可以没有。在树的顶部只有一个根小部件没有父小部件,所有其他小部件直接或间接地是该小部件的子级(这就是它被称为根的原因)。

当新的输入数据可用时,Kivy 会在每次触摸时发送一个事件。小部件树的根小部件首先接收事件。根据触摸的状态,on_touch_down、on_touch_move 或 on_touch_up 事件被分派(以触摸作为参数)到根小部件,这导致调用根小部件的相应 on_touch_down、on_touch_move 或 on_touch_up 事件处理程序。

树中的每个小部件(包括根小部件)都可以选择摘要或传递事件。如果事件处理程序返回 True,则表示事件已被正确消化和处理。该事件不会进行进一步处理。否则,事件处理程序通过调用其超类对相应事件处理程序的实现,将小部件传递给它自己的孩子。这一直到基类 Widget 类,在它的触摸事件处理程序中,它除了将触摸传递给它的子类外什么都不做:

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

这确实比乍看起来容易得多。下一节将给出一个示例,说明如何使用它来快速创建漂亮的应用程序。

很多时候你会想要限制屏幕上小部件监视触摸的区域。您可以使用小部件的 collide_point() 方法来实现此目的。您只需将触摸的位置传递给它,如果触摸在“监视区域”内,则返回 True,否则返回 False。默认情况下,这会检查屏幕上由小部件的 pos(位置;x 和 y)和大小(宽度和高度)描述的矩形区域,但您可以在自己的类中覆盖此行为。

Last updated