在这一章,将深入学习kivy,特别是事件和属性。你将学习如何使用事件处理器来响应事件,和如何在属性上触发事件时自动的更改属性。本文将会在后续的章节中进行讲解,在本章末尾,将完成一个能搜索出地区的客户端。
- 什么是事件
字典中,将事件定义为:“发生的事情,特别是重要的事情”。这对kivy中的events也是一个完美的描述。Kivy一直在触发事件,但是你只需将注意力放在那些你认为重要的事件上。每一个图形工具都有事件的概念。Kivy和其他工具的区别在于,在kivy中,事件的调度和处理是合理又简洁的。
像大多数用户事件工具包一样,kivy提供了一个事件循环。当WeatherApp调用了run方法时,python代码就执行了事件循环,这个事件循环的方法,不断的通过触摸或鼠标运动、时钟计时、键盘输入等事件来触发。当有你需要知道的事情发生时,它会进行必要的处理,使你的代码知道事件已经发生,并且有机会做出响应。 - 为自定义组件添加逻辑
我有一个指导方法,是一个严格的准则,或许你也可以称之为规则(但是有合理的里有来打破它)。这个原则就是:布局和图形信息应该始终在KV文件中;而逻辑应该在python文件中。将这两种类型的信息分开,将有助于维护应用程序。
你可能注意到,在上一章中我们并未涉及python文件。所有的改动都是发生在KV文件中,那是因为上一章完全是和用户界面有关。本章将会加入一些逻辑,倘若这个逻辑会改变用户界面(例如改变ListView),但是这样的活动仍然是属于python文件的。
因此,Python文件需要知道在第1章中定义的AddLocationForm自定义窗口部件。我有一点作弊,通过允许KV语言文件使用@BoxLayout语法动态创建一个类。先看下代码,例Example2-1:
AddLocationForm:
<AddLocationForm>: #1)
orientation: "vertical"
BoxLayout:
height: "40dp"
size_hint_y: None
TextInput:
size_hint_x: 50
Button:
text: "Search"
size_hint_x: 25
Button:
text: "Current Location"
size_hint_x: 25
ListView:
item_strings: ["Palo Alto, MX", "Palo Alto, US"]
1)、@BoxLayout已被删除,将其更改为普通类。
此KV文件无法运行,因为取出了kivy的魔法方法,这个魔法方法的用处是让它知道它应该是什么样的类。如果你想在多个位置重复使用多个一样的组件而又没有附带逻辑的时候,动态类是kivy中很有用的一个快捷方式。例如,如果你有一组按钮需要相同样式,你可以创建一个扩展@Button并设置相关属性的动态类。然后就可以在多个地方使用该类的实例,而不需在为所有按钮提供一堆重复的代码。但是,AddLocationForm是一个相当普通的类,需要附加逻辑。首先将类定义添加到main.py文件中,如示例2-2所示:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout #1)
class AddLocationForm(BoxLayout): #2)
pass
class WeatherApp(App):
pass
if __name__ == '__main__':
WeatherApp().run()
1)、 导入要继承的包
2)、 继承BoxLayout创建一个没有逻辑子类。基于类名的KV文件规则,与此类进行匹配,并将适对此表单应用所有的样式。然后将此样式类设为KV文件的根类。
如果现在运行python main.py,它将表现的与第1章末尾表现的完全相同。没有太多的收获,因为你所做的只是使代码更冗长,但你马上未这个类添加逻辑。
- 响应事件
为新的类快速添加一个当被调用时会向控制台打印简短提示的方法,如Example2-3所示:
class AddLocationForm(BoxLayout):
def search_location(self):#1)
print("Explicit is better than implicit.")
1)、这个方法不接受任何参数,这只是一个普通的方法,并不是一个事件处理器。
现在将向KV文件添加一个事件处理器。只需一行代码。在作者看来,用户界面和逻辑之间经常就是KV文件中的一行代码。这简单的一行代码,可以并且应该调用python文件中的一个方法,这个方法执行所需的处理。所以,我们可以将事件处理器,理解为KV文件中对python代码中方法的指向。看看Example2-4中搜索按钮的更改:
Button:
text: "Search"
size_hint_x: 25
on_press: root.search_location()#1
1)、事件处理器带有前缀“on_”,被当做Button对象的属性来访问。对于不同的组件有特定类型的事件:对于按钮,按压事件由鼠标按钮或者触摸事件来触发。当按压事件发生,冒号后面的代码(在本例中是root.search_location())作为正常的python代码执行。
当运行例子的时候,每次点击search按钮,都会在控制台打印简短的提示,但是到底发生了什么呢?
当按压事件发生,事件就会触发Button的事件处理器,这个事件处理器事实上就是Button类中的一个名为on_press()的方法。然后执行该方法的时候,在KV文件已经定义了该方法,就是执行root.search_location()的代码。
假设一秒钟,根变量指向在该KV语言块中最左缩进的类的实例,也就是说,类规则。这个对象有一个search_location方法,因为你刚刚添加它,而且该方法被调用。所以,每次触摸按钮时,执行search_location中的print语句。
其实这个假设是正确的。当KV语言执行任何原始Python代码时,像这些事件处理程序一样,它使得一些“魔术”变量可用。你刚刚在例子中看到的root,它指的是最左缩进的对象:当前的类规则(根类)。Self变量指的是最左侧的缩进对象。如果在on_press事件处理器中访问了self.size,那么就知道了按钮的大小。app变量指的是你代码最初调用运行的方法中的App。在本例中,它是WeatherApp的一个子例。这在你当前的代码中不是有用的,但是当你开始向WeatherApp添加方法时,app魔术变量将是访问它们的方式。