如何在__init__()中从"self"访问Kivy属性
我怎样才能在一个小部件的 __init__()
函数中访问 Kivy 属性呢?
我写了一个自定义的 Kivy 小部件。我需要在 Kivy 的一个 网格 上显示几千个这个小部件的实例。这样做会导致系统崩溃,所以我把网格包裹在一个 回收视图 中。这样做可以立即渲染,而且没有任何延迟,太棒了!
之前,我在自定义小部件的 __init__()
函数中传递了一些位置参数,用来设置一些实例字段,这些字段决定了小部件中显示的内容(以及显示的方式)。不幸的是,回收视图让我不得不把位置参数换成 Kivy 属性。而我似乎无法在对象的 __init__()
函数中访问这些属性的值。
为了简单起见,我们来看一个最小的例子,取自 Kivy 邮件列表上的 这个问题。
#!/usr/bin/env python3
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty, ListProperty
kv = '''
<TwoButtons>:
# This class is used as the viewclass in the RecycleView
# The means this widget will be instanced to view one element of data from the data list.
# The RecycleView data list is a list of dictionaries. The keys in the dictionary specify the
# attributes of the widget.
Button:
text: root.left_text
on_release: print(f'Button {self.text} pressed')
Button:
text: root.right_text
on_release: print(f'Button {self.text} pressed')
BoxLayout:
orientation: 'vertical'
Button:
size_hint_y: None
height: 48
text: 'Add widget to RV list'
on_release: rv.add()
RV: # A Reycleview
id: rv
viewclass: 'TwoButtons' # The view class is TwoButtons, defined above.
scroll_type: ['bars', 'content']
bar_width: 10
RecycleBoxLayout:
# This layout is used to hold the Recycle widgets
default_size: None, dp(48) # This sets the height of the BoxLayout that holds a TwoButtons instance.
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height # To scroll you need to set the layout height.
orientation: 'vertical'
'''
class TwoButtons(BoxLayout): # The viewclass definitions, and property definitions.
left_text = StringProperty()
right_text = StringProperty()
print( "TwoButtons top" )
def __init__(self, **kwargs):
print( "self.left_text:|" +str(self.left_text)+ "|" )
super().__init__(**kwargs)
print( "self.left_text:|" +str(self.left_text)+ "|" )
class RV(RecycleView):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.data = [{'left_text': f'Left {i}', 'right_text': f'Right {i}'} for i in range(2)]
def add(self):
l = len(self.data)
self.data.extend(
[{'left_text': f'Added Left {i}', 'right_text': f'Added Right {i}'} for i in range(l, l + 1)]
)
class RVTwoApp(App):
def build(self):
return Builder.load_string(kv)
RVTwoApp().run()
在上面的代码片段中,我们使用 RecycleView
实例化的自定义小部件叫做 TwoButtons
。
TwoButtons
类有两个 Kivy 属性:
- 字符串属性
left_text
和 - 字符串属性
right_text
如果你运行这个应用,你会清楚地看到 RecycleView
能够将数据传递到 TwoButtons
实例的属性中,因为按钮上的文本如预期那样显示。
问题是,在 __init__()
中,left_text
属性的值是空字符串
考虑以下程序的执行结果:
user@buskill:~/tmp/rv$ python3 rv.py
[INFO ] [Logger ] Record log in /home/user/.kivy/logs/kivy_24-03-17_23.txt
[INFO ] [Kivy ] v2.1.0
[INFO ] [Kivy ] Installed at "/home/user/.local/lib/python3.9/site-packages/kivy/__init__.py"
[INFO ] [Python ] v3.9.2 (default, Feb 28 2021, 17:03:44)
[GCC 10.2.1 20210110]
...
[INFO ] [Text ] Provider: sdl2
[INFO ] [Base ] Start application main loop
[INFO ] [GL ] NPOT texture support is available
self.left_text:||
self.left_text:||
self.left_text:||
self.left_text:||
正如你所看到的,TwoButtons
的 __init__()
函数中的 print()
语句返回了 left_text
的空字符串(尽管 GUI 中按钮的实际 left_text
显示为 Left 0
和 Left 1
,如预期)。
而且,如果你点击 添加小部件到 RV 列表
按钮,添加一行按钮,文本为 Added Left 2
和 Added Right 2
,那么以下新行会再次被 print()
。
self.left_text:||
self.left_text:||
我怎样才能在对象的 __init__()
函数中实际访问给定对象的属性值呢?
1 个回答
简要说明
你可以通过 bind()
函数或者内置的 on_<属性名>()
函数 来访问 Kivy 属性。
def __init__(self, **kwargs):
super().__init__(**kwargs)
def on_left_text( self, instance, value ):
print( "self.left_text:|" +str(self.left_text)+ "|" )
on_<属性名>()
我不太清楚 Kivy 是怎么创建它的控件属性对象的,但你说得对,__init__()
里面是无法直接访问这些值的。
不过,如果你能等几秒钟再进行设置,可以在每个属性设置好之后,使用 on_<属性名>()
函数来做,这个函数会在 <属性名>
的值变化时被调用。
根据文档:
使用 ‘on_<属性名>’ 进行观察
如果你自己定义了类,可以使用 ‘on_’ 回调:
class MyClass(EventDispatcher): a = NumericProperty(1) def on_a(self, instance, value): print('My property a changed to', value)
代码
我对原作者的程序进行了更新,修改如下:
#!/usr/bin/env python3
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty, ListProperty
kv = '''
<TwoButtons>:
# This class is used as the viewclass in the RecycleView
# The means this widget will be instanced to view one element of data from the data list.
# The RecycleView data list is a list of dictionaries. The keys in the dictionary specify the
# attributes of the widget.
Button:
text: root.left_text
on_release: print(f'Button {self.text} pressed')
Button:
text: root.right_text
on_release: print(f'Button {self.text} pressed')
BoxLayout:
orientation: 'vertical'
Button:
size_hint_y: None
height: 48
text: 'Add widget to RV list'
on_release: rv.add()
RV: # A Reycleview
id: rv
viewclass: 'TwoButtons' # The view class is TwoButtons, defined above.
scroll_type: ['bars', 'content']
bar_width: 10
RecycleBoxLayout:
# This layout is used to hold the Recycle widgets
default_size: None, dp(48) # This sets the height of the BoxLayout that holds a TwoButtons instance.
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height # To scroll you need to set the layout height.
orientation: 'vertical'
'''
class TwoButtons(BoxLayout): # The viewclass definitions, and property definitions.
left_text = StringProperty()
right_text = StringProperty()
print( "TwoButtons top" )
def __init__(self, **kwargs):
super().__init__(**kwargs)
def on_left_text( self, instance, value ):
print( "self.left_text:|" +str(self.left_text)+ "|" )
class RV(RecycleView):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.data = [{'left_text': f'Left {i}', 'right_text': f'Right {i}'} for i in range(2)]
def add(self):
l = len(self.data)
self.data.extend(
[{'left_text': f'Added Left {i}', 'right_text': f'Added Right {i}'} for i in range(l, l + 1)]
)
class RVTwoApp(App):
def build(self):
return Builder.load_string(kv)
RVTwoApp().run()
执行示例
上述程序的一个执行示例正确输出了 left_text
的值。
user@buskill:~/tmp/rv$ python3 rv2.py
[INFO ] [Logger ] Record log in /home/user/.kivy/logs/kivy_24-03-17_27.txt
[INFO ] [Kivy ] v2.1.0
[INFO ] [Kivy ] Installed at "/home/user/.local/lib/python3.9/site-packages/kivy/__init__.py"
[INFO ] [Python ] v3.9.2 (default, Feb 28 2021, 17:03:44)
[GCC 10.2.1 20210110]
[INFO ] [Python ] Interpreter at "/usr/bin/python3"
...
[INFO ] [Base ] Start application main loop
[INFO ] [GL ] NPOT texture support is available
self.left_text:|Left 0|
self.left_text:|Left 1|
self.left_text:|Left 0|
self.left_text:|Left 1|