Kivy的KV语言总结
-
- [🔍 **三种规则的终极对比表**](#🔍 三种规则的终极对比表)
- [💡 **一句话区分**`仅仅是一种区分含义,都是可以实例化的`](#💡 一句话区分
仅仅是一种区分含义,都是可以实例化的) - [💡 **属性和id绑定 **](#💡 **属性和id绑定 **)
- [📝 **完整示例:一个登录界面**](#📝 完整示例:一个登录界面)
- [🎯 **规则影响范围的可视化**](#🎯 规则影响范围的可视化)
- [🔧 **Python代码配合**](#🔧 Python代码配合)
- [🚨 **关键点再强调**](#🚨 关键点再强调)
- [🚨 **案例界面展示**](#🚨 案例界面展示)
说实在的,KV语言说的不明不白,官网解释一团糟
🔍 三种规则的终极对比表
| 规则类型 | 写法 | 什么时候用 | 作用 | 类比 |
|---|---|---|---|---|
| 根规则 | Button: Label: MyWidget: |
在应用中需要实际显示这个控件时 | 创建具体的控件实例 | 就像在餐厅点一份具体的菜 |
| 类规则 | <Button>: <Label>: <MyWidget>: |
想要改变某一类控件的默认外观(所有该类控件都变) | 修改已有类的默认样式 | 就像改变餐厅菜单上某道菜的默认做法(所有点这道菜的人都受影响) |
| 动态类规则 | <MyButton@Button>: <BigLabel@Label>: |
想要创建一个新类型的控件,基于现有控件但有自己的样式 | 创建一个继承自现有类的新类 | 就像创建一道新菜,基于现有菜但有自己的特色 |
💡 一句话区分 仅仅是一种区分含义,都是可以实例化的
- 根规则 :
Button:→ "我要一个按钮" - 类规则 :
<Button>:→ "所有按钮都应该长这样" - 动态类规则 :
<MyButton@Button>:→ "我发明了一种新按钮叫MyButton"
💡 **属性和id绑定 **
- 使用ids访问不太好,把id和property绑定起来,这样代码就可以用property代替id访问控件了。
📝 完整示例:一个登录界面
kv
# kvlang.kv - 完整的KV文件示例
# ============================================
# 1. 动态类规则:创建自定义样式的控件(新类型)
# ============================================
<Label>
color: (0, 1, 0, 1)
<ErrorLabel@Label>: # 创建一种"新标签"叫ErrorLabel
opacity: 0.5 # 初始
font_size: '14sp'
bold: True
<StyledButton@Button>: # 创建一种"新按钮"叫StyledButton
background_color: (0.2, 0.6, 1, 1)
color: (1, 1, 1, 1)
font_size: '16sp'
size_hint: (None, None)
size: (200, 50)
border_radius: [10]
# ============================================
# 2. 类规则:修改所有TextInput的默认样式
# ============================================
<TextInput>: # 影响文件中所有的TextInput
size_hint: (None, None)
size: (300, 60)
padding: [10, 10]
background_color: (0.95, 0.95, 0.95, 1)
foreground_color: (0, 0, 0, 1)
# ============================================
# 3. 根规则:创建实际的界面布局
# ============================================
<MyScreen@BoxLayout>: # 这是应用的根控件
# 关键代码在这里
# 将Python属性绑定到KV的id,
# KV中的 my_label: lbl_id 就是告诉Kivy:把Python里的my_label属性和KV里叫lbl_id的控件连接起来。
# 并且注意这种用法只能在自定义的类规则内部,即绑定必须在自定义类的规则内。
# 不一定属性的名称和值必须一样。
my_label: lbl_id
orientation: 'vertical'
padding: 40
spacing: 20
# 1. 背景层
canvas.before:
# 渐变背景
Color:
rgba: (0.5, 0.5, 0.5, 1)
Rectangle:
pos: self.pos
size: self.size
Label: # 根规则:创建一个具体的标签
text: "用户登录"
font_size: '24sp'
size_hint_y: None
height: 50
ErrorLabel: # 根规则:使用动态类创建的错误标签实例
id: error_label
text: "ErrorLabel" # 初始为空
TextInput: # 根规则:创建一个具体的输入框(自动应用上面的类规则)
id: username_input
hint_text: "用户名"
TextInput: # 另一个输入框(也自动应用类规则)
id: password_input
hint_text: "密码"
password: True # 密码模式
StyledButton: # 根规则:使用动态类创建的按钮实例
text: "登录"
on_press: app.login(username_input.text, password_input.text)
Button: # 根规则:普通的按钮(不受上面的动态类影响)
text: "注册"
background_color: (0.4, 0.8, 0.4, 1) # 绿色
size_hint: (None, None)
size: (150, 40)
Label:
id: lbl_id # 定义id
text: "初始文本"
Button:
text: "点击我(查看id和属性绑定的案例)"
on_press: root.change_text_by_property() # 调用Python方法
Label:
id: raw_id # 定义id
text: "初始文本"
Button:
text: "点击我(查看id和ids的案例)"
on_press: root.change_text_by_ids() # 调用Python方法
🎯 规则影响范围的可视化
整个KV文件
├── 动态类规则:<StyledButton@Button>:
│ └── 影响:所有使用 `StyledButton:` 的地方
│
├── 类规则:<TextInput>:
│ └── 影响:文件中所有 `TextInput:` 的地方
│
└── 根规则:BoxLayout:(根控件)
├── Label:(普通标签)
├── TextInput:(自动应用类规则样式)
├── TextInput:(自动应用类规则样式)
├── ErrorLabel:(应用动态类样式)
├── StyledButton:(应用动态类样式)
└── Button:(普通按钮,不受类规则影响)
🔧 Python代码配合
python
'''
- 三种规则说明
- kv中的id和python中的ids
- python中的ids是`<class 'kivy.properties.ObservableDict'>`类型,如:
{'error_label': <WeakProxy to <kivy.factory.ErrorLabel object at 0x000001870FADCBB0>>, 'username_input': <WeakProxy to <kivy.uix.textinput.TextInput object at 0x000001870FB07930>>, 'password_input': <WeakProxy to <kivy.uix.textinput.TextInput object at 0x000001871B924360>>}
-
- 属性和id绑定
- 使用ids访问不太好,把id和property绑定起来,这样代码就可以用property代替id访问控件了。
'''
from reg_font import register_font
register_font()
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty # 导入Property
# 自定义的根控件类
class MyScreen(BoxLayout):
# 1. 定义ObjectProperty
my_label = ObjectProperty(None)
# 只用id的方式
def change_text_by_ids(self):
# 必须通过ids字典
self.ids.raw_id.text = "按钮被点击了!"
# 用Property的方式
def change_text_by_property(self):
# 直接通过属性访问
print(type(self.my_label))
print(self.my_label)
self.my_label.text = "按钮被点击了!"
class KvLangApp(App):
def build(self):
return MyScreen()
def login(self, username, password):
# 获取KV中创建的控件
root = self.root
error_label = root.ids.error_label
print(root)
print(type(root)) # <class '__main__.MyScreen'>
# {
# 'error_label': <WeakProxy to <kivy.factory.ErrorLabel object at 0x00000194D4B84DE0>>,
# 'username_input': <WeakProxy to <kivy.uix.textinput.TextInput object at 0x00000194D4BA3B60>>,
# 'password_input': <WeakProxy to <kivy.uix.textinput.TextInput object at 0x00000194E09C4590>>,
# 'lbl_id': <WeakProxy to <kivy.uix.label.Label object at 0x00000194E09F1B70>>,
# 'raw_id': <WeakProxy to <kivy.uix.label.Label object at 0x00000194E0A06F90>>
# }
print(root.ids)
print(type(root.ids)) # <class 'kivy.properties.ObservableDict'>
if not username or not password:
error_label.text = "用户名和密码不能为空"
error_label.opacity = 1 # 显示错误信息
else:
error_label.opacity = 0 # 隐藏错误信息
print(f"尝试登录: {username}")
if __name__ == '__main__':
KvLangApp().run()
🚨 关键点再强调
-
动态类规则是"发明":
kv<MyButton@Button>: # 发明了MyButton这种按钮 <RedLabel@Label>: # 发明了RedLabel这种标签之后才能用
MyButton:和RedLabel:来创建实例。 -
类规则是"修改默认":
kv<Button>: # 修改所有Button的默认样式 <Label>: # 修改所有Label的默认样式影响文件中所有该类控件。
-
根规则是"实际创建":
kvButton: # 创建一个具体的按钮 MyButton: # 创建一个动态类的实例这是实际会显示在屏幕上的东西。
🚨 案例界面展示

