語言概念
KV語言允許你以聲明的方式創建控件樹,以及綁定控件屬性到其他的控件或使用一種自然的方式進行回調。
- 它允許非常快速並靈活的改變你的UI。
- 它還可以讓你的應用程序與應用程序的界面進行分隔。
如何加載kv文件
你可以告訴Kivy直接加載一個字符串或一個文件。如果這個字符串或文件定義了一個根控件,它將被下面的方法返回:
Builder.load_file('path/to/file.kv)
或者
Builder.load_string(kv_string)
內容規則
KV源自規則的搭建,這些規則被用於描述一個Widget的內容,你可以有一個根規則,以及一些類或模板規則。
你可以以如下方式聲明你的根控件類:
Widget:
使用如下方式聲明其他控件:
<MyWidget>:
KV語言有三個特殊的關鍵字:
- app: 總是與你的應用關聯
- root: 與當前控件的根控件關聯
- self: 與控件關聯
特殊符號
從python中實現一些功能:
#:import name x.y.z
等價於:
from x.y import z as name
設置一個全局值:
#:set name value
等價於:
name = value
示例
MyRootWidget:
BoxLayout:
Button:
Button:
等價於:
root = MyRootWidget()
box = BoxLayout()
box.add_widget(Button())
box.add_widget(Button())
root.add_widget(box)
賦值:
GridLayout:
cols: 3
等價於:
grid = GridLayout(cols=3)
以及
GridLayout:
cols: len(root.data)
等價於:
grid = GridLayout(cols=len(self.data))
self.bind(data=grid.setter('cols'))
事件綁定
Widget:
on_size: my_callback()
你可以使用args關鍵字來傳送值:
TextInput:
on_text: app.search(args[1])
更復雜的表達式:
pos: self.center_x - self.texture_size[0] / 2., self.center_y - self.texture_size[1] / 2.
這個表達式監聽center_x
與center_y
以及texture_size
的變化。如果它們變化了,表達式會重新計算並更新pos
字段。
你也可以處理on_
事件在你的kv語言中。例如TextInput
類有一個焦點屬性,這個屬性可以使用如下的方式訪問:
TextInput:
on_focus: print(args)
擴展畫板
KV語言可以用於定義你的控件的畫板結構:
MyWidget:
canvas:
Color:
rgba: 1, .3, .8, .5
Line:
points: zip(self.data.x, self.data.y)
並且當屬性值改變化,它們進行更新:
當然,你也可以使用 canvas.before
和 canvas.after
.
引用其他控件
在一個控件樹中,經常需要訪問/引用別的控件。KV語言提供一種方式來實現,使用id
。將他們當成一個類級別變量,這僅能夠用於KV語言中。考慮下面的示例:
<MyFirstWidget>:
Button:
id: f_but
TextInput:
text: f_but.state
<MySecondWidget>:
Button:
id: s_but
TextInput:
text: s_but.state
id
被限制在它被定義的范圍內,所以在上面的代碼中s_but
不能被上面的<MySecondWidget>
訪問。
一個id
是一個到控件的弱引用並不是這個控件本身。正因如此,存儲id
對於在垃圾回收中保持控件是不夠的。如:
<MyWidget>
label_widget: label_widget
Button:
text: 'Add Button'
on_press: root.add_widget(label_widget)
Button:
text: 'Remove Button'
on_press: root.remove_widget(lable_widget)
Label:
id: label_widget
text: 'widget'
雖然一個關聯到label_widget
存儲在MyWidget
中,但是當另外的引用已經被使用后,還不足以保持這個對象的存活,因為它僅僅是一個弱引用。
因此,在移除按鈕被點擊后,窗口被重新調整尺寸,當添加按鈕被點擊以添加控件,一個ReferenceError
將會產生:弱引用對象不存在將被拋出。
為了保持控件存活,一個到label_widget
的直接引用必須被保持。在這個例子中,使用id.__self__
或label_widget.__self__
來達到這一效果。正確的方法是:
<MyWidget>
label_widget: label_widget.__self__
在你的Python代碼中,訪問已經被定義在KV語言中的控件
KV示例:
<MyFirstWidget>:
# both these variables can be the same name and this doesn’t lead to
# an issue with uniqueness as the id is only accessible in kv.
txt_inpt: txt_inpt
Button:
id: f_but
TextInput:
id: txt_inpt
text: f_but.state
on_text: root.check_status(f_but)
在python代碼中調用:
# ...
class MyFirstWidget(BoxLayout):
txt_inpt = ObjectPropery(None)
def check_status(self, btn):
print('button state is : {state}'.format(state=btn.state))
print('text input text is :{txt}'.format(txt=self.txt_input))
或者在KV中:
<Marvel>
Label:
id: loki
text: 'loki: I AM YOUR GOD!'
Button:
id: hulk
text: "press to smash loki"
on_release: root.hulk_smash()
在Python中:
當你的KV文件已經被解析,Kivy使用ids
收集所有的控件標簽,並且將放在self.ids
字典中。這意味着你也可以遍歷這個控件並訪問他們。
class Marvel(BoxLayout):
def hulk_smash(self):
self.ids.hulk.text = "hulk: puny god!"
self.ids.loki.text = "loki: >_<!!!"
# ...
for key, val in self.ids.items():
print("key={0}, val={1}".format(key, val))
動態類
<MyWidget>:
Button:
text: "Hello world, watch this text wrap inside the button"
text_size: self.size
font_size: ’25sp’
markup: True
Button:
text: "Even absolute is relative to itself"
text_size: self.size
font_size: ’25sp’
markup: True
Button:
text: "Repeating the same thing over and over in a comp = fail" text_size: self.size
font_size: ’25sp’
markup: True
Button:
代替為每一個按鈕的屬性重復設置同樣的值,我們可以僅僅使用一個__模板__,如下所示:
<MyBigButt@Button>:
text_size: self.size
font_size: ’25sp’
markup: True
<MyWidget>:
MyBigButt:
text: "Hello world, watch this text wrap inside the button"
MyBigButt:
text: "Even absolute is relative to itself"
MyBigButt:
text: "repeating the same thing over and over in a comp = fail"
MyBigButt:
這個類僅僅被這條規則的聲明所創建,從Button
類繼承並在沒有在Python代碼中添加新的代碼的情況下,允許我們改變默認的值並為所有它的實例創建綁定。
在多個控件中重復使用樣式
my.kv文件:
<MyFirstWidget>:
Button:
on_press: self.text(txt_inpt.text)
TextInput:
id: txt_inpt
<MySecondWidget>:
Button:
on_press: self.text(txt_inpt.text)
TextInput:
id: txt_inpt
myapp.py文件:
class MyFirstWidget(BoxLayout):
def text(self, val):
print(’text input text is: {txt}’.format(txt=val))
class MySecondWidget(BoxLayout):
writing = StringProperty(’’)
def text(self, val):
self.writing = val
因為所有類共享同樣的.kv
樣式,如果我們為所有的控件復用樣式,這個設計能夠被簡化。你可以在.kv
中像下面這樣實現:
<MyFirstWidget,MySecondWidget>:
Button:
on_press: self.text(txt_inpt.text)
TextInput:
id: txt_inpt
通過使用一個逗號來分隔類名,所有的在聲明中列出的類將擁有同樣的屬性。
使用Kivy語言進行設計
在py文件中
import kivy
kivy.require(’1.8.0’)
from kivy.uix.floatlayout import FloatLayout
from kivy.app import App
from kivy.properties import ObjectProperty, StringProperty
class Controller(FloatLayout):
’’’Create a controller that receives a custom widget from the kv lang file.
Add an action to be called from the kv lang file. ’’’
label_wid = ObjectProperty()
info = StringProperty()
def do_action(self):
self.label_wid.text = ’My label after button press’
self.info = ’New info text’
class ControllerApp(App):
def build(self):
return Controller(info=’Hello world’)
if __name__ == ’__main__’:
ControllerApp().run()
controller.kv中進行設計
#:kivy 1.8.0
<Controller>:
label_wid: my_custom_label
BoxLayout:
orientation: ’vertical’
padding: 20
Button:
text: ’My controller info is: ’ + root.info
on_press: root.do_action()
Label:
id: my_custom_label
text: ’My label before button press’