Kivy:无法从另一个类更新文本输入值

Posted

技术标签:

【中文标题】Kivy:无法从另一个类更新文本输入值【英文标题】:Kivy: Can't update text input value from another class 【发布时间】:2020-09-04 20:59:48 【问题描述】:

我是 kivy 的新手。我正在尝试从另一个类更改文本输入值(TextInput 框中显示的字符串值),但没有通过。

我在class MyFirstScreen 中有以下小部件:

一个RecycleView 有多个项目(每个项目是一个dictionary) 两个TextInputs

我想做的事情:RecycleView中的每个项目被选中时,TextInputs应该更新并加载所选项目的dictionary的相应值。

但是当我尝试从另一个类 (class SelectableLabel) 访问当前 TextInput 的值时:

class SelectableLabel(RecycleDataViewBehavior, Label):
    #...
    def update_text_inputs(self, *kwarg):
        my_text_input = MyFirstScreen().ids.system_name_text_input_id #<--i can access to widget
        print("my_print_val is:", my_text_input.text) #<--but widget's current text value is : ''
        my_text_input.text = "Updated Value"

我可以访问 my_text_input 小部件,但它的文本 (my_text_input.text) 是一个空字符串 ('')。此外,当我更改 my_text_input.text 的值时,TextInput 的框中没有任何反应。

有谁知道我在这里做错了什么或如何使它工作?提前谢谢你...

这是我的 .py 代码:

from kivy.app import App
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.properties import BooleanProperty
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior


class Manager(ScreenManager):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)


class MyFirstScreen(Screen):
    system_name_text_input_id = ObjectProperty(None)

    def __init__(self, **kwarg):
        super().__init__(**kwarg)
        print("__init__ of MyFirstScreen is Called")

    def get_text_inputs(self):
        ret_val = self.ids.system_name_text_input_id.text
        print("get_text_input is called, ret_val:", ret_val)
        return ret_val


class RecycleViewWidget(RecycleView):
    def __init__(self, **kwargs):
        super(RecycleViewWidget, self).__init__(**kwargs)
        self.items_of_rv = []
        for i in range(1, 21):
            self.items_of_rv.append(
                "color": (0, 0, 0, 1), "font_size": "20", "text": f"User i",
                 "user_id": f"100 * i")
        self.data = [item for item in self.items_of_rv]


class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior, RecycleBoxLayout):
    """ Adds selection and focus behaviour to the view. """


class SelectableLabel(RecycleDataViewBehavior, Label):
    """ Add selection support to the Label """
    index = None
    selected = BooleanProperty(False)
    selectable = BooleanProperty(True)

    def refresh_view_attrs(self, rv, index, data):
        """ Catch and handle the view changes """
        self.index = index
        return super(SelectableLabel, self).refresh_view_attrs(rv, index, data)

    def on_touch_down(self, touch):
        """ Add selection on touch down """
        if super(SelectableLabel, self).on_touch_down(touch):
            return True
        if self.collide_point(*touch.pos) and self.selectable:
            return self.parent.select_with_touch(self.index, touch)

    def apply_selection(self, rv, index, is_selected):
        """ Respond to the selection of items in the view. """

        self.selected = not is_selected
        if is_selected:
            rv.data[index].update('color': (1, 1, 1, 1))
            self.refresh_view_attrs(RecycleViewWidget(), index, rv.data[index])
            print("selection changed to 0".format(rv.data[index]))
            self.update_text_inputs()
        else:
            if rv.data[index].get("color") == (1, 1, 1, 1):
                rv.data[index].update('color': (0, 0, 0, 1))
                self.refresh_view_attrs(RecycleViewWidget(), index, rv.data[index])
        self.selected = not self.selected

    def update_text_inputs(self, *kwarg):
        my_text_input = MyFirstScreen().ids.system_name_text_input_id#<--i can access to widget
        print("my_print_val is:", my_text_input.text)#<--but widget's current text value is : ''
        # my_text_input.text = "Updated Value" 

main_style = Builder.load_file("test.kv")


class MyApp(App):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def build(self):
        return main_style


if __name__ == '__main__':
    MyApp().run()

还有我的 .kv 代码:

Manager:
    MyFirstScreen:

<SelectableLabel>:
    canvas.before:
        Color:
            rgba: (0, 0, 1, 1) if self.selected else (1, 1, 1, 1)
        Rectangle:
            pos: self.pos
            size: self.size
<RecycleViewWidget>:
    viewclass: 'SelectableLabel'
    SelectableRecycleBoxLayout:
        default_size: None, dp(56)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        orientation: 'vertical'

<MyFirstScreen>:
    name: 'system_setup_page'
    system_name_text_input_id:system_name_text_input_id
    GridLayout:
        cols: 2
        BoxLayout:
            cols: 1
            padding: 20
            RecycleViewWidget:
        FloatLayout:
            Label:
                size_hint: None, None
                text: "Name:"
                font_size: 22
                pos_hint: 'center': (20/100, 90/100)
            TextInput:
                id: system_name_text_input_id
                size_hint: None, None
                hint_text: "Type Your Name..."
                size: 200, 30
                multiline: False
                pos_hint: 'center': (65/100, 90/100)
                on_text: root.get_text_inputs()
            Label:
                size_hint: None, None
                text: "User ID:"
                font_size: 20
                pos_hint: 'center': (20/100, 70/100)
            TextInput:
                size_hint: None, None
                size: 200, 30
                hint_text: "Type Your ID..."
                pos_hint: 'center': (65/100, 70/100)

【问题讨论】:

可能使用MyFirstScreen() 创建类MyFirstScreen 的第二个实例,然后从第二个实例读取文本而不是访问原始的第一个实例。当您再次运行update_text_inputs 时,您将创建MyFirstScreen 类的第三个(第四个、第五个等)实例,而不是使用第一个实例。但只有第一个实例可能有您输入 TextInput 的文本。 【参考方案1】:

问题是因为在update_text_inputs() 中使用MyFirstScreen() 会创建MyFirstScreen 类的新实例,但必须使用现有实例。

这应该是获得它的更好方法,但目前我唯一的想法是为此使用parent

因为这些元素之间有很多小部件,所以需要很多parent

    my_text_input = self.parent.parent.parent.parent.parent.system_name_text_input_id  

我使用print(self.parent)、下一个print(self.parent.parent)等找到了它。


def update_text_inputs(self, *kwarg):
    #print('parent:', self.parent.parent.parent.parent.parent.system_name_text_input_id)
    #print('parent:', self.parent.parent.parent.parent.parent.system_name_text_input_id.text)

    my_text_input = self.parent.parent.parent.parent.parent.system_name_text_input_id 

    print("my_print_val is:", my_text_input.text)

    my_text_input.text = "Updated Value" 

【讨论】:

顺便说一句:如果您将system_name_text_input_id:system_name_text_input_id 移动到RecycleViewWidget:(而不是&lt;RecycleViewWidget&gt;),那么您将需要更少的parents。 亲爱的 furas,再次感谢您的帮助。这个问题已经解决了,但是我发现你建议的解决方案有新问题。在这种情况下会出现错误: 1.如果我选择一个项目(例如:“用户 1”)->TextInputs 值正确加载。 2. 然后我向下滚动,直到所选项目完全从RecycleView 中隐藏。 3. 我向上滚动以再次显示所选项目。当我想执行第 3 步时,立即出现此错误:my_text_input= self.parent.parent.parent.parent.parent.system_name_text_input_id AttributeError: 'NoneType' object has no attribute 'parent' 这是什么问题? 我必须运行代码才能理解问题。在新页面上创建新问题 - 您将有更多空间放置完整代码、完整错误消息和描述问题。新人会看到它 - 所以也许有人会找到更好的解决方案。 我的代码没有变化。刚刚发生了新的错误。但是,如果需要创建一个新问题,我会这样做。 这是我的新问题:AttributeError: 'NoneType' object has no attribute 'parent' when scroll down and scroll up again in recycle view

以上是关于Kivy:无法从另一个类更新文本输入值的主要内容,如果未能解决你的问题,请参考以下文章

kivy textinput 无法输入文字

Kivy 更改标签小部件来自另一个类的文本

Kivy 无法使用 ScreenManager 获取文本输入

如何从另一个类/屏幕 kivy 访问值

更新另一个类中的 kivy 标签文本

Python Kivy 无法从另一个类访问 id