如何从 __init__() 上的“自身”访问 Kivy 属性

问题描述 投票:0回答:1

如何在小部件自己的 __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

GUI 中的按钮会根据需要显示文本
Left 0
Left 1
,但尝试从
__init__()
中访问它们的值会导致空字符串 (
self.left_text:||
)

TwoButtons
类有两个Kivy属性:

  1. 字符串属性
    left_text
  2. 字符串属性
    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__()
函数中实际访问给定对象的属性值?

python android-recyclerview kivy
1个回答
0
投票

tl;博士

您可以使用

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)+ "|" )

on_()

我不知道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|
© www.soinside.com 2019 - 2024. All rights reserved.