Kivy是一个开源的可视化编程框架,支持跨平台 Python 应用程序的开发。Kivy 使用自定义降价风格的 kv 语言kvlang来描述应用程序的结构、风格和功能。kv 语言是一种功能丰富且细致入微的声明性语言,在初次接触 Kivy 时可能难以完全理解。
在本文中,我们将通过GitHub上的四个代码示例探索 kvlang 的一些核心功能。我们将从了解 kvlang 的基础知识开始,包括它的语法和结构。然后,我们将通过演示对自定义属性、引用和事件的支持,继续介绍如何使用 kvlang 在 Kivy 中设置小部件的样式。
了解kvlang
在深入探讨 kvlang 是如何在 kivy 中为小部件设置样式之前,让我们先了解一下 kvlang 的基础知识。Kivy 将可视组件称为小部件。小部件都来自该类Widget,并表示响应输入的图形元素。Widgets是一个应用前端的核心构建块,可以用kvlang来描述的很透彻。
使用以下基本结构在 kvlang 中声明自定义小部件类型:
<MyCustomWidget>: # This is the new widget class name
string_property: "kvlang" # Custom property, with default value
缩进用于在小部件内部和小部件之间传达结构。在此示例中,缩进string_property是 的属性MyCustomWidget。在下一个示例中,我们将分享如何使用缩进来描述小部件之间的父子关系。缩进的含义取决于根元素的上下文,如下所示:
# Declare a custom layout widget type
# This new type descends from BoxLayout, defines a custom
# integer property, and has two button children
<MyCustomWidget@BoxLayout>:
integer_property: 42 # Custom property, with default value 42
Button:
text: "First Button"
Button:
text: "Second Button"
# Use our new type
# Add a third button as an additional child
MyCustomWidget: # Note the lack of angle brackets!
integer_property: 29 # Override the default value. Set to 29
Button:
text: "Third Button"
第一个代码块声明了一个新的 python 类,命名MyCustomWidget为从预先存在的BoxLayout类派生而来。第二个块使用我们的新类的一个实例,作为我们简单应用程序的根小部件。该MyCustomWidget实例会覆盖默认值integer_property,并添加一个额外的Button子项。
如果这太多了,那么请不要担心。我们有一些例子可以开始清理。
使用 kvlang 样式化小部件
下面我们来看看kvlang是如何在kivy中对widgets进行样式化的。Kvlang 支持一整套有用的功能,包括实例变量、简单的 Python 表达式表达式、事件调度和小部件继承。我们可以通过小而实际的例子开始探索这些概念。
Bare Minimum kivy 项目
大多数大型项目通常会将 kvlang 与 Python 代码分开存储在使用.kv文件扩展名的独立文件中。这些文件只不过是存储由 kivy 的语言解析器解析的命令的纯文本文件。kivy通过将其编写kvlang为字符串并使用几个简单的命令加载它们,可以轻松快速地试验功能。
这是一个示例 python 脚本,它定义了一个最小的 kivy 示例。我们将在整篇文章中继续使用这个简单的设置,因为它简化了整体设置。如果你安装了 kivy,你可以在 python REPL 中输入这些命令,或者使用 python 运行文件。
from kivy.lang import Builder
from kivy.app import runTouchApp
kvlang = '''\
Button:
text: 'Click me!'
'''
root_widget = Builder.load_string(kvlang)
runTouchApp(root_widget)
在这个例子中,我们定义了一个 kvlang 字符串,它创建了一个Button带有文本“Click me!”的 kivy 小部件。我们Button使用 来创建实例Builder.load_string(),它解析 kvlang 并返回根小部件实例。一旦我们有了小部件实例,我们就可以runTouchApp()在窗口中显示按钮。最终结果是一个包含单个按钮的窗口:
image.png动态小部件类
kvlang 最强大的功能之一是它能够动态创建小部件类定义,而无需 python 声明。kvlang 类声明语法几乎与 python 语言本身一样健壮,允许我们使用继承和自定义属性来定义小部件。
这是一个示例 kvlang 类声明:
from kivy.lang import Builder
from kivy.app import runTouchApp
kvlang = '''
# Declare a new class using <Name@Parent> syntax.
# Button is a fancy Label that responds to clicks, and
# uses a background image to display its state
<PressDirectionButton@Button>:
background_normal: 'button_up.png'
background_down: 'button_down.png'
font_size: 24
color: 1, 1, 1, 1
halign: 'left'
valign: 'top'
text_size: self.width, self.height
# This will be the root widget
# Using the class name directly declares an instance.
BoxLayout:
PressDirectionButton:
text: 'Show Pressed Direction'
'''
root_widget = Builder.load_string(kvlang)
runTouchApp(root_widget)
在本例中,我们使用ChildClass@ParentClass语法动态创建一个名为PressDirectionButton. 这个新类派生自Button,并覆盖了几个Button默认属性,以提供一个模板按钮,我们可以在整个应用程序中重复使用。PressDirectionButton将创建其中的实例,MyWidget其背景会在每次按下按钮时发生变化。最终的应用程序如下所示:
1_t6xJFif2bBfAyK3GlSP8lg.gif参考
kvlang 的另一个常用于样式化小部件的功能是引用其他小部件的能力。引用允许小部件同步它们的属性,并响应其他小部件的更改。kivy 框架会自动执行同步,让我们更专注于定义行为,而不是担心事件注册代码。
我们可以使用引用来修改我们的第二个例子。Button我们可以使用按钮的按下状态来控制应用程序其他地方包含的图像源,而不是更新a 的背景。
from kivy.lang import Builder
from kivy.app import runTouchApp
kvlang = '''
<PressDirectionImage@Image>:
# is_up is a custom property, defined here
is_up: False
source: 'button_up.png' if self.is_up else 'button_down.png'
BoxLayout:
Button:
id: controller_button
text: 'Press to change image'
PressDirectionImage:
id: example_img
is_up: controller_button.state == 'normal'
'''
root_widget = Builder.load_string(kvlang)
runTouchApp(root_widget)
在此示例中,我们定义了一个名为的自定义图像小部件PressDirectionImage,其中包含一个布尔属性is_up. 我们直接在 kvlang 中使用类似 python 的条件逻辑,因此当为is_up真时PressDirectionImage显示向上箭头,当为假时显示向下箭头。通过使用更多的条件逻辑 ( ),属性PressDirectionImage.is_up与属性同步。Kvlang 支持简单的 python 语法,并会在更改时自动更新 的值。Button.statecontroller_button.state == 'normalexample_img.is_upcontroller_button.state
最终的应用程序包含一个按钮,单击该按钮可更改图像的显示,如下所示:
1_BYv0gZ2yF5nvQNCYU54zgQ.gif自动引用更新大大简化了最终的应用程序逻辑。如果没有这种同步,我们将需要编写自己的状态管理代码来正确响应变化。更多的代码通常意味着更多的错误空间——所以自动更新很好。当然,在某些情况下可能需要更深入的逻辑。幸运的是,kivy 提供了丰富的事件系统以在需要时支持额外的复杂性。
事件
如果自动属性更新还不够,kvlang 还能够执行存储在小部件上的任何 python 代码。这使我们能够利用事件驱动编程,而无需创建事件和事件处理程序的麻烦。
Kvlang 不完全支持内联 python 代码,因此要充分利用 kivy 中的事件,您通常需要结合 kvlang 和 python 源代码。以下示例演示了如何实现这一点。
该类SaveLabel有两个声明 - 一个在 python 中,一个在 kvlang 中。这些声明由 kvlang 解析器组合以创建单个 python 类,以允许开发人员将 kvlang 编程的最佳方面与传统 python 编程相结合。
from kivy.lang import Builder
from kivy.app import runTouchApp
from kivy.uix.label import Label
from kivy.properties import NumericProperty
class SaveLabel(Label):
# This could be a simple python variable
# Declaring a NumericProperty makes this
# property accessible from kvlang (not needed here)
entry_cntr = NumericProperty(0)
def save(self, text_to_save):
# Do not save empty strings or null values
if not text_to_save:
return
self.entry_cntr += 1
self.text += f'Entry {self.entry_cntr}:\n{text_to_save}\n'
kvlang = '''
<SaveLabel>:
text_size: self.width, self.height
halign: 'left'
valign: 'top'
BoxLayout:
BoxLayout:
orientation: 'vertical'
Button:
text: 'Save Text Input'
on_press: save_label.save(text_input.text)
Button:
text: 'Clear'
id: clear_button
on_press: save_label.text = ''
TextInput:
id: text_input
SaveLabel:
id: save_label
'''
root_widget = Builder.load_string(kvlang)
runTouchApp(root_widget)
在此示例中,我们使用 的 python 声明SaveLabel来实现一个简单的计数器和一个将文本存储在标签的文本属性中的基本方法。我们使用 kvlangSaveLabel声明来风格化标签,确保保存的文本显示在小部件的左上角,没有文本被截断。
最后,我们使用两个Buttons 和 aTextInput来生成、存储和清除一些文本。按钮上的事件on_press用于协调 的行为SaveLabel,导致以下行为:
1_k9J9cFnitn39321p-CXjhg.gif这个例子只是触及了 kivy 中事件系统的表面。虽然我们没有足够的空间来涵盖所有细节,但重要的是要认识到,您可以使用 kvlang 实现的任何事情,您也应该能够在 python 源代码中实现。Kivy 使用格式自动为所有自定义 kivy 属性创建事件on_{property_name}。如果需要,您可以通过使用事件绑定系统绑定到它们或在 python 源代码中定义自定义实现来利用这些事件。您可以在此处了解全套功能。
结论
我们只触及了 kvlang 功能的皮毛。我希望我已经激起您的兴趣,并让您了解 kivy 框架的可扩展性。如果您对特定的 kivy 主题感兴趣并希望在此处查看详细说明,请在下方发表评论!
网友评论