如何在小部件自己的 __init__()
函数中访问
Kivy 属性?
我用 Kivy 编写了一个自定义 小部件 。我需要在 kivy screen 上的 Grid 中显示此小部件对象的数千个实例。这样做会使系统崩溃,因此我将 GridLayout 包装在 RecycleView 中。这样做可以使其立即渲染并且没有任何延迟。太棒了!
以前,我将位置参数传递给自定义小部件的
__init__()
函数,我用它来设置一些实例字段,这些实例字段将用于确定小部件中将出现的内容(以及如何出现)。不幸的是,RecycleView 迫使我用 Kivy Properties 替换位置参数。我似乎无法在对象的 __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
实例的属性中,因为文本按预期出现在按钮中。
问题是
left_text
属性的值是__init__()
内部的空字符串
考虑上述程序的以下执行:
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:||
如您所见,
print()
的__init__()
函数内的TwoButtons
语句为left_text
返回空字符串(即使GUI中按钮中的实际left_text
显示为Left 0
和) Left 1
,根据需要)。
此外,如果您单击
Add widget to RV list
按钮添加带有 Added Left 2
和 Added Right 2
文本的第三行按钮,则以下新行将再次 print()
编辑。
self.left_text:||
self.left_text:||
如何从对象的
__init__()
函数中实际访问给定对象的属性值?
您可以使用
bind()
函数或 内置 on_<property_name>()
函数从其自己的实例中访问 Kivy 属性。
def __init__(self, **kwargs):
super().__init__(**kwargs)
def on_left_text( self, instance, value ):
print( "self.left_text:|" +str(self.left_text)+ "|" )
我不知道Kivy如何实例化它的小部件的Properties对象,但你是对的,该值无法从内部访问
__init__()
。
on_<property_name>()
函数 设置每个属性后进行设置——只要 <property_name>
属性的值发生变化,就会调用该函数。
来自文档:
使用“on_
”观察 如果您自己定义了该类,则可以使用“on_”回调:
class MyClass(EventDispatcher): a = NumericProperty(1) def on_a(self, instance, value): print('My property a changed to', value)
我已经从 OP 更新了程序,更改如下
#!/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|