您的位置:首页 > 其它

翻译:kivy语言

2017-07-17 00:23 169 查看

Programming Guide » Kv language

Concept behind the language

As your application grow more complex, it’s common that the construction ofwidget trees and explicit declaration of bindings, becomes verbose and hard tomaintain. TheKV Language is a attempt to overcome these short-comings.

由于你的app越来越复杂,一般来说widget树的建造和明确声明的绑定,这个变成了沉重的负担并很难去维护。KV 语言尝试克服这些短板。

The KV language (sometimes called kvlang, or kivy language), allows you tocreate your widget tree in a declarative way and to bind widget propertiesto each other or to callbacks in a natural manner. It allows for very fastprototyping and agile
changes to your UI. It also facilitates a goodseparation between the logic of your application and its User Interface.

KV语言,允许你以声明的形式去创建你的widget树,并以比较自然的方式绑定widget属性给每个或者是callback。它可以快速的塑造一个原型并灵活地改变你的ui。它也很好帮助分开你的app跟用户接口的逻辑。

How to load KV

There are two ways to load Kv code into your application:

By name convention:通过名字
Kivy looks for a Kv file with the same name as your App class inlowercase, minus “App” if it ends with ‘App’ e.g:

MyApp -> my.kv


小写,只留着App前头的字眼


If this file defines a Root Widget it will be attached to the App’srootattribute and used as the base of the application widget tree.

如果这个文件定义为根widget,它将隶属于App的根属性并且作为一个app widget树的基础。

Builder
:You can tell Kivy to directly load a string or a file. If this string or filedefines a root widget, it will be returned by the method:

Builder:你可以直接告诉kivy从一个字符串或者文件里头加载。如果字符串或者文件定义了根widget,它将会被这方法返回:

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


or:

Builder.load_string(kv_string)


Rule context

语句规则

A Kv source constitutes of rules, which are used to describe the contentof a Widget, you can have oneroot rule, and any number ofclass ortemplate rules.

The root rule is declared by declaring the class of your root widget, withoutany indentation, followed by: and will be set as theroot attribute of theApp instance:

一个 KV最初的构成或者规则,是被用作定义widget的内容,你可以有个根规则,并且可以有任意数量的类或者规则模板。

Widget:


A class rule, declared by the name of a widget class between
< > andfollowed by :, defines how any instance of that class will begraphically represented:

属于类的规则,在<>中的名字声明,并后面紧跟一个冒号,定义了接下来有多少个类将被生动地表现出来:

<MyWidget>:


Rules use indentation for delimitation, as python, indentation should be offour spaces per level, like the python good practice recommendations.

规则使用缩进来划分界限,就如python一样。

There are three keywords specific to Kv language:

app: always refers to the instance of your application.
app:总是涉及关联到你app的实例
root: refers to the base widget/template in the current ruleroot:涉及关联到当前规则下的base widget/template
self: always refer to the current widgetself,总是涉及关联当前的widget

Special syntaxes¶特殊语法

There are two special syntaxes to define values for the whole Kv context:

To access python modules and classes from kv,

这里有2种特殊语法去定义整个KV语境的值:

一个是访问来自kv的类,一个是访问python的模块

#:import name x.y.z
#:import isdir os.path.isdir
#:import np numpy


is equivalent to:

from x.y import z as name
from os.path import isdir
import numpy as np


in python.

To set a global value,

#:set name value


is equivalent to:

name = value


in python.

Instantiate children¶实例化

To declare the widget has a child widget, instance of some class, just declarethis child inside the rule:

声明这个widget有一个 子widget,其实就是一些类的实例化,仅仅参考这条规则,声明这个child:

MyRootWidget:BoxLayout:Button:Button:


The example above defines that our root widget, an instance of MyRootWidget,which has a child that is an instance of the
BoxLayout
.
That BoxLayout further has twochildren, instances of the
Button
class.

上述例子定义了我们的根widget,其实就是MyRootWidget的一个实例,这个实例会有一个child:也就是BoxLayout的实例。那么BoxLayout会有两个children:也就是Button的实例

A python equivalent of this code could be:

python实现的语句:

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


Which you may find less nice, both to read and to write.python语句看起来缺点多多。

Of course, in python, you can pass keyword arguments to your widgets atcreation to specify their behaviour. For example, to set the number of columnsof a
gridlayout
,
we would do:

当然,python中,你可以在创建的时候传递键的属性到你的widget ,去定义它们的行为。例如,设定gridlayout的列数:

grid = GridLayout(cols=3)


To do the same thing in kv, you can set properties of the child widget directlyin the rule:

在kv怎么做?同样你可以直接为子widget设置属性:

GridLayout:
cols: 3


The value is evaluated as a python expression, and all the properties used inthe expression will be observed, that means that if you had something like thisin python (this assumeself is a widget with adata
ListProperty
):

这个值同等于python的表达方式,并且这种形式的声明会发现到所有的属性,也意味着在python中有的(假设self 是一个widget并带有ListProperty数据):

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


To have your display updated when your data change, you can now have just:

为了当你数据改变时候你的显示也可以更新到,你现在可以:

GridLayout:
cols: len(root.data)


Note
Widget names should start with upper case letters while property namesshould start with lower case ones. Following thePEP8
Naming Conventionsis encouraged.
Widget命名应该开头字母大小,属性名就要小写。这里是命名规则:

Event Bindings¶事件绑定

You can bind to events in Kv using the “:” syntax, that is, associating acallback to an event:

你可以绑定事件通过使用 冒号 这个语法,连接一个callback到事件当中:

Widget:on_size: my_callback()


You can pass the values dispatched by the signal using the args keyword:

你可以传递一个已被一个 信号源派遣的值(这个信号源使用了键这个变量)

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


More complex expressions can be used, like:

更加复杂的表达:

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


This expression listens for a change in
center_x
,
center_y
,and
texture_size
.
If one of them changes, the expression will bere-evaluated to update the
pos
field.

You can also handle
on_
events inside your kv language.For example the TextInput class has a
focus
property whose auto-generated
on_focus

event can be accessed inside the kv language like so:

这个表达监听
center_x
,
center_y
,and
texture_size
的变化。如果其中一个改变了,表达式会重新求值并更新到pos域。

你也可以嵌入kv语言处理on事件。例如,TextInput类有个focus属性,这个属性自动生成的on_focus事件可以嵌入kv语言:

TextInput:
on_focus: print(args)


Extend canvas¶扩展画布

Kv lang can be used to define the canvas instructions of your widget like this:

Kv语言可被用来定义你widget的画布构造:

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


And they get updated when properties values change.

Of course you can use canvas.before and canvas.after.

Referencing Widgets¶引用 Widget

In a widget tree there is often a need to access/reference other widgets.The Kv Language provides a way to do this using id’s. Think of them as classlevel variables that can only be used in the Kv language. Consider thefollowing:

在widget树中,很常碰到要引用其他的widget,Kv语言提供了一种方式:id。考虑到它们作为一个类级别的变量,也就只能用在kv语言中。:

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

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


An
id
is limited in scope to the rule it is declared in, so in thecode above
s_but
can not be accessed outside the
<MySecondWidget>
rule.

id限制在声明的规则域中,所以,上述代码中的s_but不可以在
<MySecondWidget>
规则外被引用使用。

Warning
When assigning a value to
id
, remember that the value isn’ta string. There are no quotes: good ->
id:value
,
bad ->
id:'value'

当赋值给id时,记住这个值不是一个字符串,没有这样的标注:正确的姿势->
id:value
,错误的姿势->[code]id:'value'

[/code]

An
id
is a
weakref
to the widget and not the widget itself. As aconsequence, storing the
id
is not sufficient to keep the widget from beinggarbage collected. To demonstrate:

id是一个
weakref(附注:)给到widget,所以不是一个widget它本身。因而,存储一个 id 不足以阻止widget被垃圾回收掉。


使用weakref模块,你可以创建到对象的弱引用,Python在对象的引用计数为0或只存在对象的弱引用时将回收这个对象。[code]




<MyWidget>:label_widget: label_widgetButton:
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'


Although a reference to
label_widget
is stored in
MyWidget
, it is notsufficient to keep the object alive once other references have
been removedbecause it’s only a weakref.Therefore, after the remove button is clicked (which removesany direct reference to the widget) and the window is resized (which calls thegarbage collector resulting in the deletion of
label_widget
),
when the addbutton is clicked to add the widget back, a
ReferenceError:weakly-referencedobjectnolongerexists

will be thrown.

尽管一个对label_widget的引用被存于MyWidget,当其他的引用被移除后它还不足以让这个对象保持存活,因为它只是个weakref。因此,在移除按钮按下后(这个移除掉了widget所有的直接的引用)并且 窗口被调整了大小(这会唤醒垃圾回收器,导致对label_widget的删除),当增加按钮按下把这个 widget搞回来,此时异常抛出。

To keep the widget alive, a direct reference to the
label_widget
widgetmust be kept. This is achieved using
id.__self__
or
label_widget.__self__
in this case.
The correct way to do this would be:

为了确保这个widget可以存活,一个对label_widget的直接引用必须被保留。在这个例子中,使用
id.__self__
or
label_widget.__self__


<MyWidget>:label_widget: label_widget.__self__


Accessing Widgets defined inside Kv lang in your python code¶kv语言跟python语言并用

Consider the code below in my.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)


In 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 is defined as a
ObjectProperty
initializedtoNone inside the Class.

txt_inpt被定义为
ObjectProperty
初始化成None的类。

txt_inpt = ObjectProperty(None)


At this point self.txt_inpt is None. In Kv lang this property is updated tohold the instance of the
TextInput
referenced by the idtxt_inpt.:

那么self.txt_inpt 是None.在Kv语言中,通过id txt_inpt,这个属性要被更新,是为了去hold住TextInput的引用实例。

txt_inpt: txt_inpt


From this point onwards, self.txt_inpt holds a reference to the widgetidentified by the idtxt_input and can be used anywhere in the class, as inthe functioncheck_status. In contrast to this method you could also just
passtheid to the function that needs to use it, like in case of
f_but in thecode above.

基于这点,我们继续讨论,self.txt_inpt hold了一个到widget的引用(这个widget是从id txt_input定义的),并且可以被该类的任何地方使用,尽管是在内部方法体:check_status里头。对比这个方法,你也许会仅仅传递这个id到这个方法。

There is a simpler way to access objects with id tags in Kv using theids lookup object. You can do this as follows:

这里有个更为简单的方式,在kv中用id 标签去连接对象:

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


In your python code:

class Marvel(BoxLayout):

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


When your kv file is parsed, kivy collects all the widgets tagged with id’sand places them in thisself.ids dictionary type property. That means youcan also iterate over these widgets and access them dictionary style:

当你的kv文件被解析后,kivy收集所有带id标签的widgets并放置它们到self.ids字典类型属性当中。这意味着你也可以迭代出这些widgets并连接它们:

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


Note
Although the self.ids method is very concise, it is generally regarded as‘best practice’ to use the ObjectProperty. This creates a direct reference,provides faster access and is more explicit.尽管这很简洁,它通常被认定为使用ObjectProperty的最好练习。这创建了直接的引用,提供更快的连接。

Dynamic Classes¶动态类

Consider the code below:

<MyWidget>:Button:
text: "Hello world, watch this text wrap inside the button"
text_size: self.size
font_size: '25sp'
markup: TrueButton:
text: "Even absolute is relative to itself"
text_size: self.size
font_size: '25sp'
markup: TrueButton:
text: "Repeating the same thing over and over in a comp = fail"
text_size: self.size
font_size: '25sp'
markup: TrueButton:


Instead of having to repeat the same values for every button, we can just use atemplate instead, like so:

为了免去重复的赋值动作,我们可以仅仅使用一个模板代替:

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


This class, created just by the declaration of this rule, inherits from theButton class and allows us to change default values and create bindings for allits instances without adding any new code on the Python side.

Re-using styles in multiple widgets¶在多个widget中重复使用样式

Consider the code below in my.kv:

<MyFirstWidget>:
Button:
on_press: root.text(txt_inpt.text)
TextInput:
id: txt_inpt

<MySecondWidget>:
Button:
on_press: root.text(txt_inpt.text)
TextInput:
id: txt_inpt


In 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


Because both classes share the same .kv style, this design can be simplifiedif we reuse the style for both widgets. You can do this in .kv as follows.In my.kv:

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


By separating the class names with a comma, all the classes listed in thedeclaration will have the same kv properties.

Designing with the Kivy Language

One of aims of the Kivy language is toSeparate the concernsof presentation and logic. The presentation (layout) side is addressed
by yourkv file and the logic by your py file.

其实就是将layout用kv写,逻辑用python写

The code goes in py files

Let’s start with a little example. First, the Python file named 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()


In this example, we are creating a Controller class with 2 properties:

info
for receving some text
label_wid
for receving the label widget

In addition, we are creating a
do_action()
method that will use both ofthese properties. It will change the
info
text and change
text in the
label_wid
widget.

The layout goes in controller.kv

Executing this application without a corresponding .kv file will work, butnothing will be shown on the screen. This is expected, because the
Controller
class has no widgets in it,
it’s just a
FloatLayout
. We cancreate the UI around the
Controller
class in a file namedcontroller.kv,which will be loaded
when we run the
ControllerApp
. How this is done andwhat files are loaded is described in the
kivy.app.App.load_kv()

method.

直接执行下面代码,kv文件可以执行,但是没有任何事发生。正如我所料,
因为Controller都没有widgets,它只是个
FloatLayout
。我们可以创建一个围绕[code]Controller
的UI,文件名称为controller.kv,这个文件将在我们运行
ControllerApp
时候加载[/code]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16


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


One label and one button in a vertical
BoxLayout
. Seems very simple. Thereare 3 things going on here:

Using data from the
Controller
. As soon as the
info
property ischanged in the controller, the expression
text:'Mycontrollerinfois:'+root.info

will automatically be re-evaluated, changing the textin the
Button
.使用来自Controller的data。不久这个在controller里头的info属性将被改变,
text:'Mycontrollerinfois:'+root.info
表达式将自动重新赋值,并改变按钮中的text

Giving data to the
Controller
. The expression
id:my_custom_label
is assigning the created
Label

the id of
my_custom_label
. Then,using
my_custom_label
in the expression
label_wid:my_custom_label

gives the instance of that
Label
widget to your
Controller
.赋予data到Controller。表达式
id:my_custom_label正被
my_custom_label
分配已经创建好的Label。然后,使用[code]label_wid:my_custom_label
表达式中的
my_custom_label赋予Label的widget实例到你的controller。
[/code]

Creating a custom callback in the
Button
using the
Controller
’s
on_press
method.在Button创建一个自定义的callback,使用Controller的on_press的方法( 即do_action)

root
and
self
are reserved keywords, useable anywhere.
root
represents the top widget in the rule and
self
representsthe
current widget.root和self是内置的键,可以在任何地方使用。root在rulle中代表定层的widget,self代表当前widget。(例如,牛如果修改kv文件中的第12行代码为on_press:self.do_action(),那么会报错:AttributeError: 'Button' object has no attribute 'do_action')
You can use any id declared in the rule the same as
root
and
self
. For example, you could do this in the
on_press()
:你可以使用任何在rule中声明的id,就像使用root、self那样。

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


And that’s that. Now when we run main.py, controller.kv will be loaded sothat the
Button
and
Label
will
show up and respond to our touch events.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  kivy