如何加载Kv文件
有2中加载Kv文件的方式:
- 通过命名约定
# 如果自定义App是以'App'结尾,约定的命名会移除'App', (所有字母小写也是OK的)
MyApp(App) 的kv文件命名: my.kv、My.kv
# 如果自定义App不是以'App'结尾,约定的命名会直接转为小写字母
MySomething(App)的kv文件命名: mysomething.kv、MySomething.kv
2. 通过Builder类直接指定要加载的文件路径
Builder.load_file('path/to/file.kv')
Builder.load_string(kv_string)
规则上下文
类规则的定义
类规则是实际存在的一个class的映射, Kv文件对应的py的作用域中应该有这个对应的class.
类规则由小括号<>内的窗口部件类名声明,后面紧跟冒号,用于定义该类任何实例的外观和行为
<MyWidget>:
规则以4个空格作为缩进.
如在代码中有一个MyGridLayout(GridLayout),此处就可以声明一个<MyGridLayout>并在其中定义其内部组件.
关键字
- app:始终指代你的应用程序实例
- root:指当前规则中的基础部件/模板
- self:始终指代当前部件
特殊用法
import的用法
##访问python的模块和类
#:import name x.y.z
#:import isdir os.path.isdir
#:import np numpy
#等同于
from x.y import z as name
from os.path import isdir
import numpy as np
全局变量
## 设置全局值
#:set name value
#等同于
name = value
实例化子项
构建组件
#在kv中使用4个空格嵌套组件
#一下代码表示MyRootWidget嵌套BoxLayout, BoxLayout嵌套2个Button
MyRootWidget:
BoxLayout:
Button:
Button:
#等同于
root = MyRootWidget()
box = BoxLayout()
root.add_widget(box)
box.add_widget(Button())
box.add_widget(Button())
组件的属性设置
GridLayout:
cols: 3
#等同于
grid = GridLayout(cols = 3)
观察者与组件的结合应用
GridLayout:
cols: len(root.data)
#等同于
grid = GridLayout(cols=len(slf.data))
self.bind(data=grid.setter('cols'))
## 待验证
事件绑定
# 对默认绑定事件设置方法
Widget:
on_size: my_callback()
# 可以对方法进行传参, 通过args
TextInput:
on_text: app.search(args[1])
扩展画布
MyWidget:
canvas:
Color:
rgba: 1, .3, .8, .5
Line:
points: zip(self.data.x, self.data.y)
引用组件(Widget)
在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
一个id的作用域仅限于声明它的规则, s_but只能在<mysecondwidget>规则中访问.
> 警告
当访问一个id时, 不能把它当做一个字符串. 不应该有单引号. 正确的方式是id: value, 错误的方式是id: 'value'
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(label_widget)
Label:
id: label_widget
text: 'widget'
虽然对label_widget的引用存储在MyWidget中,但一旦其他引用被移除,这不足以使该对象继续存活,因为它只是一个弱引用。因此,在点击移除按钮(这会移除对该部件的任何直接引用)并且调整窗口大小(这会调用垃圾回收器,导致删除label_widget)之后,当点击添加按钮将该部件添加回来时,将会抛出一个ReferenceError: weakly-referenced<br>object no longer exists
为了使小部件保持存活状态,必须保留对label_widget小部件的直接引用。在这种情况下,可通过 id.__self__ 或 label_widget.__self__ 来实现。正确的做法如下:
<mywidget>:
label_widget: label_widget.__self__
在Python代码中访问Kv文件中定义的组件
未完待续
动态类
未完待续
在多个部件中重复使用样式
未完待续
使用Kv语言进行设计
未完待续
Kv language示例
原始代码方式:
首先我们实现一个以Python代码形式实现的Kivy界面.
源码如下, 我们命名为sample1.py:
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
# 这是一个布局容器类, 布局容器类用于填充基本组件(Widget,或叫控件)
class RootLayout(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# 设置GridLayout是2行2列
self.cols = 2
self.rows = 2
self.button1 = Button(text="Button1") # 新建一个按钮
self.add_widget(self.button1) # 把这个按钮添加到当前GridLayout中
self.button2 = Button(text="Button2") # 新建一个按钮
self.add_widget(self.button2) # 把这个按钮添加到当前GridLayout中
self.label1 = Label(text="This is label1") # 新建一个Label
self.add_widget(self.label1) # 把这个标签添加到当前GridLayout中
self.label2 = Label(text="This is label2") # 新建一个Label
self.add_widget(self.label2) # 把这个标签添加到当前GridLayout中
# 这是Kivy的应用App入口类,自己实现的App需要集成kivy.app.App
# 自定义的类命名上,应该以App结尾,因为在使用kv language重新定义组件时,会"去除结尾的App,并小写所有字母"
class Sample1App(App):
# 自定义的App需要实现方法build(self), 此方法返回App的Root容器
def build(self):
return RootLayout()
if __name__ == '__main__':
Sample1App().run()
运行后,界面效果:
Kv language方式:
我们对上边Python代码中的RootLayout进行改造, 使它能使用Kv language解析.
基于命名规则, kv文件应该被命名为sample1.kv
sample1.kv的内容:
<RootLayout>:
Button:
text: 'new Button1'
Button:
text: 'new Button2'
Label:
text: 'new Label1'
Label:
text: 'new Label2'
再次运行sample1.py, 会发现命令行报出一个异常: kivy.uix.gridlayout.GridLayoutException: Too many children in GridLayout. Increase rows/cols!
这说明我们的Kv文件加载成功了,在sample1.py中我们定义的GridLayout的行数和列数均为2.
现在我们修改sample1.py中的self.cols = 4, 再一次执行, 得到的界面效果:
此时GridLayout的组件变成了8个, Kv文件中的组件优先加载, 通过Python代码编写的组件在之后加载.
现在可以移除Python代码中的组件的定义过程, 只保留Kv文件中的结果.
最终的sample1.py代码:
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
# 这是一个布局容器类, 布局容器类用于填充基本组件(Widget,或叫控件)
class RootLayout(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# 这是Kivy的应用App入口类,自己实现的App需要集成kivy.app.App
# 自定义的类命名上,应该以App结尾,因为在使用kv language重新定义组件时,会"去除结尾的App,并小写所有字母"
class Sample1App(App):
# 自定义的App需要实现方法build(self), 此方法返回App的Root容器
def build(self):
return RootLayout()
if __name__ == '__main__':
Sample1App().run()
最终的sample1.kv代码:
<RootLayout>:
cols: 2
cols: 2
Button:
text: 'new Button1'
Button:
text: 'new Button2'
Label:
text: 'new Label1'
Label:
text: 'new Label2'
后续的研究我会及时更新此文, 敬请收藏关注
官网链接: kivy.org/doc/stable/…