四、編程向?qū)?4.9KV語言)

編程向?qū)В?.9KV語言

一养盗、語言背后的思想

當(dāng)你的應(yīng)用程序變得更復(fù)雜時(shí),構(gòu)建部件樹和明確的聲明綁定將變得冗長和難以維護(hù)适篙。KV語言試圖克服這些缺點(diǎn)往核。

KV語言(有時(shí)被叫kvlang,或kivy語言)嚷节,允許你以聲明的方式來創(chuàng)建你的部件樹聂儒,并以一種自然的方式綁定部件屬性或回調(diào)函數(shù)。針對(duì)UI硫痰,它支持快速原型和敏捷改動(dòng)衩婚。它也使得邏輯和用戶接口能更好的分離。

二效斑、如何加載KV

有兩種方式來加載KV代碼:

  • 通過名字約定
    Kivy查找你的應(yīng)用程序類的小寫的同名KV文件非春,如果它以'App'結(jié)尾則去掉它,例如:

    MyApp -> my.kv

如果這個(gè)文件定義了一個(gè)根部件鳍悠,它將會(huì)附著到應(yīng)用程序的根特征值税娜,并用它作為應(yīng)用程序部件樹的根。

  • Builder
    你可以告訴Kivy直接加載一個(gè)字符串或一個(gè)文件藏研。如果這個(gè)字符串或文件定義了根部件敬矩,它將被返回。

    Builder.load_file('path/to/file.kv')

或者

Builder.load_string('kv_string')

三蠢挡、管理上下文

一個(gè)KV源構(gòu)成的規(guī)則弧岳,用來描述部件的內(nèi)容。你可以有一個(gè)根規(guī)則和任何數(shù)量的類或模板規(guī)則业踏。

根規(guī)則通過聲明你的根部件類來聲明禽炬,不需要任何縮進(jìn),后面跟著冒號(hào)(:)勤家,并且被設(shè)置為應(yīng)用程序?qū)嵗母卣髦怠?/p>

Widget:

一個(gè)類規(guī)則腹尖,有一對(duì)尖括號(hào)(<>)包括部件類名組成,后面跟冒號(hào)(:)伐脖,定義類的實(shí)例如何被生動(dòng)地表達(dá):

<MyWidget>:

和Python一樣热幔,規(guī)則使用縮進(jìn)進(jìn)行界定乐设,和良好的Python習(xí)慣一樣,縮進(jìn)的每一級(jí)別最好是4個(gè)空格绎巨。

有三個(gè)關(guān)鍵字來指定KV語言:

  • app:總是引用你的應(yīng)用程序的實(shí)例近尚。
  • root:引用當(dāng)前規(guī)則中的根部件/模板。
  • self:引用當(dāng)前部件场勤。

四戈锻、特殊的語法

有兩個(gè)特殊語法來為整個(gè)KV上下文定義值:

  • 為了從KV中訪問Python的模塊和類:
    #:import name x.y.z
    #:import isdir os.path.isdir
    #:import np numpy

上面的代碼等價(jià)于:

    from x.y import z as name
    from os.path import isdir
    import numpy as np
  • 為了設(shè)置一個(gè)全部變量:
    #:set name value

等價(jià)于:

    name = value

五、實(shí)例化子部件

為了聲明部件的子部件和媳,僅在規(guī)則里面聲明這些子部件即可:

MyRootWidget:
    BoxLayout:
        Button:
        Button:

上面的例子定義了一個(gè)MyRootWidget的實(shí)例作為我們的根部件格遭,它有一個(gè)子部件是BoxLayout的實(shí)例。BoxLayout進(jìn)一步有兩個(gè)Button類的子部件窗价。在Python代碼中應(yīng)該是這樣:

root = MyRootWidget()
box = BoxLayout()
box.add_widget(Button())
box.add_widget(Button())
root.add_widget(box)

你會(huì)發(fā)現(xiàn)在KV中如庭,僅用很少的代碼,易寫并易讀撼港。

當(dāng)然,在Python中骤竹,你可以傳遞關(guān)鍵字參數(shù)到你的部件中帝牡。例如,設(shè)置一個(gè)GridLayout的列的數(shù)目蒙揣,我們可以這樣寫:

grid = GridLayout(cols = 3)

在KV中靶溜,你可以直接在規(guī)則中設(shè)置子部件的屬性:

GridLayout:
    cols:3 

這個(gè)值被評(píng)估為一個(gè)Python表達(dá)式,并且表達(dá)式中所有的屬性值都將被監(jiān)聽懒震。例如在Python中:

grid = GridLayout(cols = len(self.data))
self.bind(data = grid.setter('cols'))

當(dāng)你的數(shù)據(jù)變化時(shí)罩息,顯示跟著更新,在KV中只需這樣:

GridLayout:
    cols:len(root.data)

注意个扰,當(dāng)屬性名以小寫字母開頭時(shí)瓷炮,部件名首字母應(yīng)當(dāng)大寫。遵循PEP8 Naming Conventions是被鼓勵(lì)的递宅。

六娘香、事件綁定

在KV語言中,你可以使用":"語法來綁定事件:

Widget:
    on_size: my_callback()

你也可以使用args關(guān)鍵字傳遞參數(shù):

TextInput:
    on_text:app.search(args[1])

更復(fù)雜的表達(dá)式可能類似這樣:

pos:self.center_x - self.texture_size[0] / 2, self.center_y - self.texture_size[1] / 2

這個(gè)表達(dá)式監(jiān)聽center_x, center_y, texture_size的變動(dòng)办龄。如果其中一個(gè)發(fā)生了改變烘绽,表達(dá)式將會(huì)更新pos字段。

你也可以在KV語言中處理on_事件俐填。例如輸入框有一個(gè)聚焦(focus)屬性,它將自動(dòng)生成on_focus事件:

TextInput:
    on_focus:print(args)

七安接、擴(kuò)展畫布

KV語言可以這樣來定義你的畫布指令:

MyWidget:
    canvas:
        Color:
            rgba: 1, .3, .8, .5
        Line:
            points: zip(self.data.x, self.data.y)

當(dāng)屬性值改變時(shí)它們將更新,當(dāng)然英融,你也可以使用canvas.before和canvas.after.

八盏檐、引用部件

在一個(gè)部件樹中呀打,經(jīng)常需要訪問/引用其他的部件。KV語言提供了一個(gè)使用id's的方法來做這些工作糯笙。將它們認(rèn)為是只能用于Kv語言類級(jí)別變量贬丛。看下面代碼:

<MyFirstWidget>:
    Button:
        id: f_but
    TextInput:
        text: f_but.state

<MySecondWidget>:
    Button:
        id: s_but
    TextInput:
        text: s_but.state

一個(gè)id被限制到它被聲明的作用域內(nèi)给涕,所以在<MySecondWidget>外面s_but不能被訪問豺憔。

id是一個(gè)部件的弱引用(weakref)并且不是部件本身。因此够庙,存儲(chǔ)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(label_widget)
    Label:
        id: label_widget
        text: 'widget'

上面的代碼中,雖然一個(gè)到label_widget的引用被存儲(chǔ)到MyWidget中耘眨,但是因?yàn)樗鼉H僅是一個(gè)弱引用昼榛,一旦別的引用被移除,它不足以保持對(duì)象存活剔难。因此胆屿,當(dāng)移除按鈕被點(diǎn)擊后(將移除其他的引用)窗口將重新計(jì)算尺寸(調(diào)用垃圾回收導(dǎo)致重新檢測(cè)label_widget),當(dāng)點(diǎn)擊添加按鈕來添加部件偶宫,一個(gè)引用錯(cuò)誤將發(fā)生(ReferenceError:weakly-referenced object no longer exists)

為了保持部件存活非迹,一個(gè)對(duì)label_widget的引用必須被保持〈壳鳎可以使用id.self或label_widget.self做到憎兽。正確的方式如下:

<MyWidget>:
    label_widget: label_widget.__self__

九、在Python代碼中訪問Kv語言定義的部件

考慮以下在my.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)

在myapp.py:

...
class MyFirstWidget(BoxLayout):

    txt_inpt = ObjectProperty(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_inpt))
...

txt_inpt被作為ObjectProperty初始化:

txt_inpt = ObjectProperty(None)

這是效果導(dǎo)致self.txt_inpt是None吵冒。在KV語言中纯命,這個(gè)屬性更新被id:txt_inpt引用的持有TextInput的實(shí)例。

txt_inpt:txt_inpt

從這點(diǎn)向上痹栖,self.txt_inpt持有一個(gè)被id txt_input標(biāo)識(shí)的部件的引用并且能被用在類的任何地方亿汞,正如在check_status函數(shù)中一樣。對(duì)照這個(gè)函數(shù)结耀,你僅僅需要傳遞id到你想用的地方留夜。

你可以使用ids來訪問帶id標(biāo)識(shí)的對(duì)象,這是一種更簡單的方法:

<Marvel>
  Label:
    id: loki
    text: 'loki: I AM YOUR GOD!'
  Button:
    id: hulk
    text: "press to smash loki"
    on_release: root.hulk_smash()

在你的Python代碼中:

class Marvel(BoxLayout):

    def hulk_smash(self):
        self.ids.hulk.text = "hulk: puny god!"
        self.ids["loki"].text = "loki: >_<!!!"  # alternative syntax

當(dāng)你的kv文件被解析時(shí)图甜,kivy收集所有的帶id標(biāo)簽的部件碍粥,并放置它們到self.ids字典中。這意味著你能以字典的風(fēng)格來迭代這些部件并訪問它們黑毅。

for key, val in self.ids.items():
    print("key={0}, val={1}".format(key, val))

注意嚼摩,雖然self.ids很簡潔,它被認(rèn)為是使用ObjectProperty的最佳實(shí)踐。但是創(chuàng)建一個(gè)字典的引用枕面,將會(huì)提供更快的訪問速度并更加清晰愿卒。

十、動(dòng)態(tài)類

考慮下面代碼:

<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:

為了替代重復(fù)的代碼潮秘,我們可以使用模板來代替:

<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:

這個(gè)被規(guī)則聲明的類繼承自按鈕類琼开。它允許我們改變默認(rèn)值,并為每一個(gè)實(shí)例創(chuàng)建綁定而不用在Python那邊添加任何新的代碼枕荞。

十一柜候、在多個(gè)部件中重用樣式

看下面的在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

因?yàn)閮蓚€(gè)類共同使用相同的.kv風(fēng)格。如果我們?yōu)閮蓚€(gè)部件重用風(fēng)格躏精,這將使得設(shè)計(jì)簡化渣刷。你可以在my.kv中這樣寫代碼:

<MyFirstWidget,MySecondWidget>:
    Button:
        on_press: self.text(txt_inpt.text)
    TextInput:
        id: txt_inpt

用一個(gè)逗號(hào)(,)來分離類名,所有的類將都有同樣的kv屬性矗烛。

十二辅柴、使用KV語言設(shè)計(jì)

使用Kivy語言的一個(gè)目標(biāo)就是分離邏輯和表現(xiàn)。表現(xiàn)層使用kv文件來表示瞭吃,邏輯使用py文件來表示碌嘀。

(一)py文件中寫代碼

讓我們開始一個(gè)小例子,首先虱而,在main.py文件中:

import kivy
kivy.require('1.0.5')

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()

在這個(gè)例子中筏餐,我們創(chuàng)建了一個(gè)帶有兩個(gè)屬性的控制類:

  • info:接收一些文本
  • label_wid接收標(biāo)簽(label)部件

另外,我們創(chuàng)建了一個(gè)do_action()方法來使用這些屬性牡拇。它將會(huì)改變info文本和label_wid部件的文本。

(二)在controller.kv中布局

執(zhí)行一個(gè)沒有相應(yīng)的.kv文件的應(yīng)用程序可以運(yùn)行穆律,但是沒有任何東西被顯示到屏幕上惠呼。這是被期望的,因?yàn)榭刂祁悰]有部件在里面峦耘,它僅僅是一個(gè)FloatLayout剔蹋。我們能圍繞Controller類在一個(gè)controller.kv文件中創(chuàng)建UI,當(dāng)我們運(yùn)行ControllerApp時(shí)它會(huì)被加載辅髓。這將如何實(shí)現(xiàn)及什么文件被加載都在kivy.app.App.load_kv()方法中被描述泣崩。

#:kivy 1.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'

在垂直布局的BoxLayout中,有一個(gè)標(biāo)簽和一個(gè)按鈕洛口〗酶叮看起來很簡單,有3個(gè)事情將被做:

  1. 從Controller使用數(shù)據(jù)第焰。一旦在controller中info屬性被改變买优,表達(dá)式text:'My Controller info is:' + root.info將會(huì)自動(dòng)更新。

  2. 傳遞數(shù)據(jù)到Controller。表達(dá)式id:my_custom_label被賦值給id為my_custom_label的標(biāo)簽杀赢。于是烘跺,在表達(dá)式label_wid:my_custom_label中使用my_custom_label傳遞部件Label的實(shí)例到你的Controller。

  3. 使用Controller的on_press方法創(chuàng)建一個(gè)定制的回調(diào)函數(shù)脂崔。

    • root和self被保留為關(guān)鍵字滤淳,可用在任何地方。root代表規(guī)則內(nèi)的根部件砌左,self代表當(dāng)前部件脖咐。

    • 在規(guī)則內(nèi)你可以使用任何id聲明,同root和self一樣绊困。例如文搂,你可以在on_press()中這樣:

      Button:
      on_press:root.do_action();my_custom_label.font_size = 18

現(xiàn)在,我們運(yùn)行main.py, controller.kv將會(huì)被自動(dòng)加載秤朗,按鈕和標(biāo)簽也將顯示并響應(yīng)你的觸摸事件煤蹭。

下節(jié)預(yù)告:編程向?qū)?.10集成其他框架

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市取视,隨后出現(xiàn)的幾起案子硝皂,更是在濱河造成了極大的恐慌,老刑警劉巖作谭,帶你破解...
    沈念sama閱讀 223,207評(píng)論 6 521
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稽物,死亡現(xiàn)場離奇詭異,居然都是意外死亡折欠,警方通過查閱死者的電腦和手機(jī)贝或,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,455評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锐秦,“玉大人咪奖,你說我怎么就攤上這事〗创玻” “怎么了羊赵?”我有些...
    開封第一講書人閱讀 170,031評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵,是天一觀的道長扇谣。 經(jīng)常有香客問我昧捷,道長,這世上最難降的妖魔是什么罐寨? 我笑而不...
    開封第一講書人閱讀 60,334評(píng)論 1 300
  • 正文 為了忘掉前任靡挥,我火速辦了婚禮,結(jié)果婚禮上衩茸,老公的妹妹穿的比我還像新娘芹血。我一直安慰自己贮泞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,322評(píng)論 6 398
  • 文/花漫 我一把揭開白布幔烛。 她就那樣靜靜地躺著啃擦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪饿悬。 梳的紋絲不亂的頭發(fā)上令蛉,一...
    開封第一講書人閱讀 52,895評(píng)論 1 314
  • 那天,我揣著相機(jī)與錄音狡恬,去河邊找鬼珠叔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛弟劲,可吹牛的內(nèi)容都是我干的祷安。 我是一名探鬼主播,決...
    沈念sama閱讀 41,300評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼兔乞,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼汇鞭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起庸追,我...
    開封第一講書人閱讀 40,264評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤霍骄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后淡溯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體读整,經(jīng)...
    沈念sama閱讀 46,784評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,870評(píng)論 3 343
  • 正文 我和宋清朗相戀三年咱娶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了米间。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,989評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡膘侮,死狀恐怖车伞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情喻喳,我是刑警寧澤,帶...
    沈念sama閱讀 36,649評(píng)論 5 351
  • 正文 年R本政府宣布困曙,位于F島的核電站表伦,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏慷丽。R本人自食惡果不足惜蹦哼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,331評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望要糊。 院中可真熱鬧纲熏,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,814評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鱼填,卻和暖如春药有,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背苹丸。 一陣腳步聲響...
    開封第一講書人閱讀 33,940評(píng)論 1 275
  • 我被黑心中介騙來泰國打工愤惰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赘理。 一個(gè)月前我還...
    沈念sama閱讀 49,452評(píng)論 3 379
  • 正文 我出身青樓宦言,卻偏偏與公主長得像,于是被迫代替她去往敵國和親商模。 傳聞我的和親對(duì)象是個(gè)殘疾皇子奠旺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,995評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)阻桅,斷路器凉倚,智...
    卡卡羅2017閱讀 134,722評(píng)論 18 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,353評(píng)論 25 707
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法嫂沉,內(nèi)部類的語法稽寒,繼承相關(guān)的語法,異常的語法趟章,線程的語...
    子非魚_t_閱讀 31,669評(píng)論 18 399
  • 幾個(gè)月前由于身體原因确丢,開始運(yùn)動(dòng)绷耍,以前對(duì)腰間贅肉沒多大在意,當(dāng)仔細(xì)看看的時(shí)候鲜侥,發(fā)覺好丑褂始,盡快把它減掉,重新鍛煉成健康...
    8291e247ebd7閱讀 15,610評(píng)論 2 21
  • ?“我愿意給你當(dāng)狗描函,你不要崎苗,你怕我咬你狐粱,你非把我轟出去,結(jié)果我成了龍胆数〖◎撸”(郭德綱炮轟初到北京時(shí)主流相聲屆的打壓) ...
    深夜筆廚閱讀 718評(píng)論 0 0