简体   繁体   中英

Kivy: Can't update text input value from another class

I'm new on kivy .I'm trying to change text input value (the string value that is shown in TextInput box) from another class, But nothing passes.

I have following widgets in class MyFirstScreen :

  • One RecycleView with multiple items (each item is a dictionary )
  • Two TextInput s

What I want to do: When each items in RecycleView selected, TextInput s should be update and load with corresponding values of selected item's dictionary .

But when i try to access current TextInput 's value from another class ( class SelectableLabel ):

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"

I can access to my_text_input widget but its text ( my_text_input.text ) is an empty string ( '' ).Furthermore when i change the value of my_text_input.text , nothing happens in TextInput 's box.

Does anyone know what am I doing wrong here or how to make this work? Thank you in advance...

Here is my.py code:

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()

And my.kv code:

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)}

Problem is because using MyFirstScreen() in update_text_inputs() you create new instance of class MyFirstScreen but you have to use existing instance.

It should be better way to get it but at this moment my only idea is to use parent for this.

Because there are many widgets between these elements so it needs many parent :

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

I found it using print(self.parent) , next print(self.parent.parent) , etc.


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" 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM