Search code examples
kivy

Kivy RecycleView not showing data after moving into a screen


I've modified a Kivy RecycleView example and put it into a screen with menu, but now it's showing empty pages instead of data. I suspect the root have changed but I'm unable to connect the RecycleView to the correct source.

To be honest I think I've to work more on learning how to proper link between different objects.

My modified version that has the issue:

from kivy.app import App
from kivy.properties import ObjectProperty, StringProperty, NumericProperty, ListProperty
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.boxlayout import BoxLayout
from kivy.lang.builder import Builder

from kivy.uix.screenmanager import Screen, ScreenManager

KV = """

<SomeMenu_ActionBar@ActionBar>:

    ActionView:
        id: ActionView
        HiddenIcon_ActionPrevious:
        ActionButton:
            text: 'Stop'
            on_release:  app.stop()
       
       
<HiddenIcon_ActionPrevious@ActionPrevious>:

    
<MainBox>:
    orientation: 'vertical'
    canvas.before:
        Color:
            rgb: .6, .6, .6
        Rectangle:
            pos: self.pos
            size: self.size
            # source: 'data/background.png'

    MyManager:
        id: sm
        RVScreen:
            id: rvscreen

    SomeMenu_ActionBar:
        id: ActionBar


<RecycleItem>:
    orientation: 'horizontal'
    Label:
        text: root.label_text
    TextInput:
        text: root.input_text
        on_text: root.set_text(self.text)

RecycleView:
    data: app.data
    viewclass: 'RecycleItem'
    RecycleBoxLayout:
        spacing: 10
        default_size: None, dp(80)
        default_size_hint: 1, None
        orientation: 'vertical'
        size_hint_y: None
        height: self.minimum_height


<RVScreen>:
    id: "rvscreen"
    BoxLayout:
        orientation: "vertical"
        RecycleView:
        Button:
            text: 'Previous screen'
            size_hint: None, None
            size: 150, 50
            on_release: root.manager.current = root.manager.previous()
"""

class RecycleItem(RecycleDataViewBehavior, BoxLayout):
    owner = ObjectProperty()
    index = NumericProperty(0)
    input_text = StringProperty()
    label_text = StringProperty()

    def set_text(self, text):
        if self.owner is not None:
            self.owner.data[self.index]['input_text'] = text
    
    def refresh_view_attrs(self, rv, index, data):
        self.index = index
        return super(RecycleItem, self).refresh_view_attrs(rv, index, data)

class MainBox(BoxLayout):
    """Mainbox under MainApp
    It contains the ScreenManager
    """
    pass

class RVScreen(Screen):
    pass

class MyManager(ScreenManager):
    """The screen manager that handles app screens
    """
    pass

class Test(App):
    data = ListProperty()

    def build(self):
        self.data = [{'label_text': "Label "+str(x), "input_text": "Input "+str(x), 'owner': self} for x in range(20)]
        Builder.load_string(KV)
        return MainBox()

Test().run()

Original working example:

from kivy.app import App
from kivy.properties import ObjectProperty, StringProperty, NumericProperty, ListProperty
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.boxlayout import BoxLayout
from kivy.lang.builder import Builder

KV = """
<RecycleItem>:
    orientation: 'horizontal'
    Label:
        text: root.label_text
    TextInput:
        text: root.input_text
        on_text: root.set_text(self.text)

RecycleView:
    data: app.data
    viewclass: 'RecycleItem'
    RecycleBoxLayout:
        spacing: 10
        default_size: None, dp(80)
        default_size_hint: 1, None
        orientation: 'vertical'
        size_hint_y: None
        height: self.minimum_height


<RVScreen>:
    BoxLayout:
        orientation: "vertical"
        RecycleView:
        Button:
            text: 'Previous screen'
            size_hint: None, None
            size: 150, 50
            on_release: root.manager.current = root.manager.previous()
"""

class RecycleItem(RecycleDataViewBehavior, BoxLayout):
    owner = ObjectProperty()
    index = NumericProperty(0)
    input_text = StringProperty()
    label_text = StringProperty()

    def set_text(self, text):
        if self.owner is not None:
            self.owner.data[self.index]['input_text'] = text
    
    def refresh_view_attrs(self, rv, index, data):
        self.index = index
        return super(RecycleItem, self).refresh_view_attrs(rv, index, data)

class Test(App):
    data = ListProperty()

    def build(self):
        self.data = [{'label_text': "Label "+str(x), "input_text": "Input "+str(x), 'owner': self} for x in range(20)]
        return Builder.load_string(KV)

Test().run()

Solution

  • You can define the root widget in several ways.

    If you have the build method in the App's subclass and it returns some Widget, it will be set as the root widget. Or you can assign the root to the kv-rule by loading it there etc.

    In this case in your kv-rule you defined RecycleView as the root widget but you haven't used it in build to set it as the root.

    Meanwhile you returned MainBox from the method build so it will be used as root widget. Again you loaded the kv-rule there so the design etc. will be applied to this root widget.

    Now in order to add RecycleView to RVScreen (as you mentioned in comment) and set MainBox as root widget (as you did already) you can modify your kvlang as follows:

    <SomeMenu_ActionBar@ActionBar>:
    
        ActionView:
            id: ActionView
            HiddenIcon_ActionPrevious:
            ActionButton:
                text: 'Stop'
                on_release:  app.stop()
           
           
    <HiddenIcon_ActionPrevious@ActionPrevious>:
    
        
    <MainBox>:
        orientation: 'vertical'
        canvas.before:
            Color:
                rgb: .6, .6, .6
            Rectangle:
                pos: self.pos
                size: self.size
                # source: 'data/background.png'
    
        MyManager:
            id: sm
            RVScreen:
                id: rvscreen
    
        SomeMenu_ActionBar:
            id: ActionBar
    
    
    <RecycleItem>:
        orientation: 'horizontal'
        Label:
            text: root.label_text
        TextInput:
            text: root.input_text
            on_text: root.set_text(self.text)
    
    
    <RVScreen>:
        id: "rvscreen"
        BoxLayout:
            orientation: "vertical"
            RecycleView:
                data: app.data
                viewclass: 'RecycleItem'
                RecycleBoxLayout:
                    spacing: 10
                    default_size: None, dp(80)
                    default_size_hint: 1, None
                    orientation: 'vertical'
                    size_hint_y: None
                    height: self.minimum_height
            Button:
                text: 'Previous screen'
                size_hint: None, None
                size: 150, 50
                on_release: root.manager.current = root.manager.previous()