ForyszeP
ForyszeP

Reputation: 163

Kivy. Refreshing screen with different data

When I click on the button I need the data from the code to be shown on different screen. Below code does what I need despite that Clock adds widgets every second. I would like the TextInput to be editable so when I type some value to the 'Row' it will stay as typed. I've tried to use Clock.schedule_once() but then the widgets does not appear. I've even tried something like below:

clock = 1
def fill_with_data(self, dt)
    if clock == 1: 
    for item ...
    clock +=1

witch locks adding new widgets (ridicules I know) but when I go back to MainScreen to show another data then it does not appear on the screen. Below you will find whole code.

from kivy.config import Config
Config.set('graphics', 'multisamples', '0')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import Screen
from kivy.clock import Clock


kv = """
#:import FadeTransition kivy.uix.screenmanager.FadeTransition

ScreenManager:
    MainScreen:
    ShowData:    

<DataSwitch>

    Button:
        text: 'Data 1'
        on_press: app.root.current = 'ShowData'
        on_press: root.show_data1()
    Button:
        text: 'Data 2'
        on_press: app.root.current = 'ShowData'
        on_press: root.show_data2()

<Row>
    TextInput:
        id: text_input
        size_hint_y: None
        height: 30

<Rows>        
    orientation: 'vertical'

<MainScreen>   
    name: 'MainScreen'
    DataSwitch:

<ShowData>:
    name: 'ShowData'
    Rows:
    Button:
        text: 'Go back'
        size_hint_y: None
        height: 20
        on_press: app.root.current = 'MainScreen'

"""

class Row(BoxLayout):
    text = ''
    def __init__(self, **kwargs):
        super(Row, self).__init__(**kwargs)
        self.set_text()

    def set_text(self):
        print('set', self.text)
        self.ids.text_input.text = self.text

class Rows(BoxLayout):

    data =[]

    def __init__(self, **kwargs):
        super(Rows, self).__init__(**kwargs)
        #self.fill_with_data()
        Clock.schedule_interval(self.fill_with_data, 1)
        #Clock.schedule_once(self.fill_with_data)
    def fill_with_data(self, dt):
        for item in self.data:
            Row.text = item
            row = Row()
            self.add_widget(row)
            #self._rows[str(self.row_id)] = weakref.ref(row)

class MainScreen(Screen):
    pass

class DataSwitch(BoxLayout):

    data1 = ['1', '2', '3', '4']
    data2 = ['5', '6', '7', '8']

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

    def show_data1(self):
        print('data1', self.data1)
        Rows.data = self.data1
        Rows()

    def show_data2(self):
        Rows.data = self.data2
        Rows()

class ShowData(Screen):
    pass

sm = Builder.load_string(kv)

class TestApp(App):
    def build(self):
        return sm

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

Upvotes: 0

Views: 1688

Answers (1)

ikolim
ikolim

Reputation: 16011

Running the app will produce the following:

Problem 1 - too many Rows instantiated

  • Rows: as per kv file
  • Rows() as per Python script in methods show_data1() and show_data2(). These Rows instances created does not have an associated ModalView.

Problem 2 - switched screen before populating list, data

Button:
    text: 'Data 1'
    on_press: app.root.current = 'ShowData'
    on_press: root.show_data1()
Button:
    text: 'Data 2'
    on_press: app.root.current = 'ShowData'
    on_press: root.show_data2()

Solution

The following example is just an illustration.

kv file - Add id: rows

Add id: rows to Rows: in class rule, <ShowData>:. This will be use to reference attributes or methods in class Rows().

kv file - invoke fill_with_data()

Use on_pre_enter event of Screen and Clock.schedule_once() function to invoke method fill_with_data()

Note: Row will be added each time, the screen is displayed. In other words, Row widget will double each time. To prevent this, one might want to delete the widgets added in on_pre_leave event.

Snippets - kv file

<ShowData>:
    name: 'ShowData'
    on_pre_enter:
        Clock.schedule_once(self.ids.rows.fill_with_data, 0.1)
    Rows:
        id: rows

py file - Access attributes / methods in class Rows()

  • Get object, app using App.get_running_app()
  • Get object, ShowData using Screen Manager's get_screen() function e.g. root.get_screen('ShowData')
  • Access attribute, data using ids.rows.data

Snippets

def show_data1(self):
    App.get_running_app().root.get_screen('ShowData').ids.rows.data = self.data1

def show_data2(self):
    App.get_running_app().root.get_screen('ShowData').ids.rows.data = self.data2

Example

main.py

from kivy.config import Config

Config.set('graphics', 'multisamples', '0')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import Screen

kv = """
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
#:import Clock kivy.clock.Clock

ScreenManager:
    MainScreen:
    ShowData:    

<DataSwitch>

    Button:
        text: 'Data 1'
        on_press: root.show_data1()
        on_press: app.root.current = 'ShowData'
    Button:
        text: 'Data 2'
        on_press: root.show_data2()
        on_press: app.root.current = 'ShowData'

<Row>:
    TextInput:
        id: text_input
        size_hint_y: None
        height: 30

<Rows>:   
    orientation: 'vertical'

<MainScreen>:
    name: 'MainScreen'
    DataSwitch:

<ShowData>:
    name: 'ShowData'
    on_pre_enter:
        Clock.schedule_once(self.ids.rows.fill_with_data, 0.1)
    Rows:
        id: rows
    Button:
        text: 'Go back'
        size_hint_y: None
        height: 20
        on_press: app.root.current = 'MainScreen'

"""


class Row(BoxLayout):

    def __init__(self, text, **kwargs):
        super(Row, self).__init__(**kwargs)
        self.ids.text_input.text = text


class Rows(BoxLayout):
    data = []

    def fill_with_data(self, dt):
        for item in self.data:
            row = Row(text=str(item))
            self.add_widget(row)


class MainScreen(Screen):
    pass


class DataSwitch(BoxLayout):
    data1 = ['1', '2', '3', '4']
    data2 = ['5', '6', '7', '8']

    def show_data1(self):
        App.get_running_app().root.get_screen('ShowData').ids.rows.data = self.data1

    def show_data2(self):
        App.get_running_app().root.get_screen('ShowData').ids.rows.data = self.data2


class ShowData(Screen):
    def on_pre_leave(self):
        self.ids.rows.clear_widgets()


sm = Builder.load_string(kv)


class TestApp(App):
    def build(self):
        return sm


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

Upvotes: 2

Related Questions