Jim Henderson
Jim Henderson

Reputation: 31

Kivy Properties and communications between different widgets and layout classes

I have a simple example of the issue below. I have three classes MyLayout (root), Pop a popup class and MyBox which is a Boxlayout created dynamically with a click of a button in MyLayout. I have made the capitalise() function in root that works with the popup fields. My issue is interaction with instances of MyBox. for example how can the popup know which MyBox called it and return the first name + last Name to the appropriate TextInput box?

Also if I wanted to collate all the data in the TextInput boxes across all the MyBox instances, how would i do that. I assume using properties.

Thanks in advance

# filename popper.py
from kivy.app import App
from kivy.uix.popup import Popup
from kivy.uix.stacklayout import StackLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder


Builder.load_string('''
#:import Factory kivy.factory.Factory
<MyBox>:
    orientation:'vertical'
    TextInput:
        text: 'N/A'
    Button:
        text: 'Choose a name'
        on_press: Factory.Pop().open()

<Pop>:
    auto_dismiss: False
    title: 'Names'
    size_hint: [0.4, 0.5]
    pos_hint:{'right': 0.4, 'top': 1}
    id: msg_box
    GridLayout:
        id: _pop
        rows: 3
        GridLayout:
            id: pop_grid
            cols:2
            padding: [0,5]
            Spinner:
                text: 'First Name'
                id: fn
                sync_height: True
                values: ['andrew', 'brian', 'colin', 'david', 'edmond']
                width: self.width
                on_text: self.text = app.root.capitalise(self.text)
            Spinner:
                text: 'Last Name'
                id: ln
                sync_height: True
                values: ['Adams', 'Bass', 'Carney', 'Davies', 'Edmonds']
                width: self.width


        Button:
            padding: [0,5]
            text: 'OK'
            on_release: root.dismiss()
            width: self.width

<MyLayout>:
    orientation: 'tb-lr'
    size_hint: .2, 0.5
    width: self.width
    Button:
        text: 'Create name box.'
        on_press: app.root.make_name_box()
        width: 300
''')



class MyLayout(StackLayout):
    pass

    def make_name_box(self):
        self.add_widget(MyBox())

    def capitalise(self, text):
        return text.capitalize()

class Pop(Popup):
    def __init__(self, **kwargs):
        super(Pop, self).__init__(**kwargs)

class MyBox(BoxLayout):
    def __init__(self, **kwargs):
        super(MyBox, self).__init__(**kwargs)
        size_hint = None, None
        width = 300

class PopperApp(App):
    def build(self):
        return MyLayout()

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

Upvotes: 2

Views: 236

Answers (1)

noEmbryo
noEmbryo

Reputation: 2231

The main thing you want is to pass a reference of the "creator" box to the "created" popup. You can do that by passing the widget at creation time on_press: Factory.Pop(root).open()

Full working code here:

Builder.load_string('''
#:import Factory kivy.factory.Factory
<MyBox>:
    orientation:'vertical'
    TextInput:
        text: root.box_text # bind the text to the box_text
    Button:
        text: 'Choose a name'
        on_press: Factory.Pop(root).open()  # pass the creator of the pop here

<Pop>:
    auto_dismiss: False
    title: 'Names'
    size_hint: [0.4, 0.5]
    pos_hint:{'right': 0.4, 'top': 1}
    id: msg_box
    GridLayout:
        id: _pop
        rows: 3
        GridLayout:
            id: pop_grid
            cols:2
            padding: [0,5]
            Spinner:
                text: 'First Name'
                id: fn
                sync_height: True
                values: ['andrew', 'brian', 'colin', 'david', 'edmond']
                width: self.width
                on_text: self.text = app.root.capitalise(self.text)
            Spinner:
                text: 'Last Name'
                id: ln
                sync_height: True
                values: ['Adams', 'Bass', 'Carney', 'Davies', 'Edmonds']
                width: self.width


        Button:
            padding: [0,5]
            text: 'OK'
            on_release:
                root.creator.box_text = "{} {}".format(fn.text, ln.text)  # use the creator reference
                root.dismiss()
            width: self.width

<MyLayout>:
    orientation: 'tb-lr'
    size_hint: .2, 0.5
    width: self.width
    Button:
        text: 'Create name box.'
        on_press: app.root.make_name_box()
        width: 300
''')


class MyLayout(StackLayout):
    pass

    def make_name_box(self):
        self.add_widget(MyBox())

    def capitalise(self, text):
        return text.capitalize()


class Pop(Popup):
    def __init__(self, creator, **kwargs):
        super(Pop, self).__init__(**kwargs)
        self.creator = creator # keep a reference to the creator here


class MyBox(BoxLayout):
    box_text = StringProperty("N/A")

    def __init__(self, **kwargs):
        super(MyBox, self).__init__(**kwargs)
        size_hint = None, None
        width = 300


class PopperApp(App):
    def build(self):
        self.my_layout = MyLayout()
        return self.my_layout

    def on_stop(self):  # print the names when leaving the app
        boxes = self.my_layout.children[:-1]
        print([i.box_text for i in boxes])


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

EDIT: As for the second question, I added the printing of the names as you close the app.

Upvotes: 1

Related Questions