kivy chapter1~chapter3
Kivy
root widget & child widgets
WeatherRoot:
<WeatherRoot>:
AddLocationForm
<AddLocationForm>:
orientation: "vertical"
search_input: search_box
search_results: search_results_list
BoxLayout:
height: "40dp"
size_hint_y: None
TextInput:
id: search_box
size_hint_x: 50
Button:
text: "Search"
size_hint_x: 25
on_press: root.search_location()
Button:
text: "Current Location"
size_hint_x: 25
ListView:
id: search_results_list
adapter:
ListAdapter(data=[], cls=main.LocationButton)
<LocationButton>:
on_press: app.root.show_current_weather(self.text)
- root widget 形如 WeatherRoot
- child widgets 形如 AddLocationForm,LocationButton
其中比较重要的区别定义当中root和app.root的使用,这分别引用(refers to)不懂的对象(object)在KV language file.
app.root始终关联root widget,root则关联‘当前’的widget
Be careful not to confuse the root magic variable, which refers to the leftmost rule in the current indentation block, with app.root,which always refers to the root widget of the app. app.root refers tothe same object anywhere in the KV language file, but root refers to a different thing depending on what rule it is found in.
3.值得注意的是AddLocationForm中ListView Adapter中的main,这里的main指的是类似‘模块’,main.LocationButton则是传递一个‘类模板'给adpter用于初始化ListView的对象。以下贴出Python code的代码(即main.py):
对应root widget的class:
class WeatherRoot(BoxLayout): def show_current_weather(self, location): from kivy.uix.label import Label self.clear_widgets() # self.add_widget(Label(text=location)) current_weather = Factory.CurrentWeather() current_weather.location = location self.add_widget(current_weather) def show_add_location_form(self): self.clear_widgets() self.add_widget(AddLocationForm())
对应child widget的class:
class AddLocationForm(BoxLayout): search_input = ObjectProperty() search_results = ObjectProperty() def search_location(self): search_template = "http://api.openweathermap.org/data/2.5/find?q={}&type=like" search_url = search_template.format(self.search_input.text) request = UrlRequest(search_url, self.found_location) def found_location(self, request, data): data = json.loads(data.decode()) if not isinstance(data, dict) else data cities = ["{} ({})".format(d['name'], d['sys']['country']) for d in data['list']] self.search_results.item_strings = cities del self.search_results.adapter.data[:] self.search_results.adapter.data.extend(cities) self.search_results._trigger_reset_populate()
以及用于生成按钮风格的ListView的class:
class LocationButton(ListItemButton): pass
LocationButton只是单纯地继承ListItemButton.其中LiteItemButton源自:
from kivy.uix.listview import ListItemButton
值得注意的的是root widget WeatherRoot的“属性”(property)设置貌似没有。
WeatherRoot: <WeatherRoot>: AddLocationForm
properties
通常在网页里面,比如一个文本输入框里面的值是怎么获取的,这就不得不了解property.
还是以child widget AddLocationForm为栗子:
<AddLocationForm>: orientation: "vertical" search_input: search_box search_results: search_results_list BoxLayout: height: "40dp" size_hint_y: None TextInput: id: search_box size_hint_x: 50 Button: text: "Search" size_hint_x: 25 on_press: root.search_location() Button: text: "Current Location" size_hint_x: 25 ListView: id: search_results_list adapter: ListAdapter(data=[], cls=main.LocationButton)
oritation,search_input,height,id,size_hint_x等都是property.
其中oritation不同于search_input和search_results,search_input和search_results是Python code代码里面的逻辑产生的,KV language file里面引用,只在界面与布局里面简单使用。
以下是AddLocationForm的对应的Python code,为了阅读简洁,我略去了一个method:
class AddLocationForm(BoxLayout): search_input = ObjectProperty() search_results = ObjectProperty() def search_location(self): search_template = "http://api.openweathermap.org/data/2.5/find?q={}&type=like" search_url = search_template.format(self.search_input.text) request = UrlRequest(search_url, self.found_location)
对于不同的widget,形如文本输入框或者按钮,对应的属性不同。特别地,还有独有的event property.
Widget
<CurrentWeather@BoxLayout>: location: "" orientation: "vertical" Label: text: root.location BoxLayout: orientation: "horizontal" height: "40dp" size_hint_y: None Button: text: "Add Location" on_press: app.root.show_add_location_form() Button: text: "ForeCast"
CurrentWeathor是一个Layout classes.按《creating app in kivy》作者的说法,Widget有如下分类:
Widgets
Kivy uses the word widget to describe any user interface element. Just a few examplesof widgets include:
• The label you rendered in Example 1-3
• The text input field and buttons you’ll render shortly
• Layout classes that comprise other widgets and determine where they should be displayed
• Complicated tree views such as file pickers
• Movie and photo renderers
• Tabbed boxes that display different widgets depending on the selected tabI find it convenient to think of a widget as a sort of box that has behaviors and can contain other boxes. The Widget class is the most basic such box.
event
借助上面的代码,首先聚焦Button,text表名这个button的名称,on_press的值指明了当这个按钮被按下的事件由root widget对应的class的方法来具体响应。再来看看Python Code代码,看看事件具体是如何响应的:
class WeatherRoot(BoxLayout): def show_current_weather(self, location): from kivy.uix.label import Label self.clear_widgets() # self.add_widget(Label(text=location)) current_weather = Factory.CurrentWeather() current_weather.location = location self.add_widget(current_weather) def show_add_location_form(self): self.clear_widgets() self.add_widget(AddLocationForm())
1.首先调用clear_widgets方法清除所有widgets(按函数的名称字面上来理解至少是这样的)
2.然后root widget调用add_widget方法来创建一个新的AddLocationForm'页面'(views)
最后形如clear_widget和add_widget方法如同extend、_trigger_reset_populate方法一样,似飞来之物。所以要多看手册文档,甚至作者说的,_trigger_reset_populate方法是一个undocumented method,需要看源代码才能获悉这样的函数(data变化来force an update here),这毋庸置疑需要看ListViews.
self.search_results.adapter.data.extend(cities)
self.search_results._trigger_reset_populate()
2015/05/29