4

So I'm struggling with this one small bit where I cannot find a way to pass a value through a procedure when a ListView item is pressed. The example below is a simpler version of what I want to happen and how my main code is set out. The goal is so a can see ChangeScreen(self.index) on the hash tagged on_release: in the KV file. However the issue is I do not know how to call ChangeScreen successfully. Thanks!

Kivy:

#: import main main
#: import ListAdapter kivy.adapters.listadapter.ListAdapter 

AppScreenManager:
    DemoScreen1:
        id: screen1
    DemoScreen2:
        id: screen2

<DemoScreen1>:
    name: "demoscreen1"
    BoxLayout:
        orientation: "vertical"
        ListView:
            adapter:
                ListAdapter(data=["Screen 1","Screen 2"], cls=main.ListButton)

<DemoScreen2>:
    name: "demoscreen2"
    orientation: "vertical"
    BoxLayout:
        orientation: "vertical"
        Label:
            id: labText
            text: "Hello World"

<ListButton>:
    height: self.texture_size[1]
    on_release: ### HOW DO I CALL THE ChangeScreen FUNCTION HERE

Python:

import kivy
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import StringProperty, ObjectProperty, ListProperty
from kivy.uix.listview import ListItemButton


class DemoScreen1(Screen):
    def ChangeScreen(self, option):
        if option == 0:
            print("Screen1")
            self.parent.ids.screen2.labelUpdater("Screen 1 was pressed")
        else:
            print("Screen2")
            self.parent.ids.screen2.labelUpdater("Screen 2 was pressed")
        self.parent.current = "demoscreen2"

class DemoScreen2(Screen):
    labText = StringProperty()
    def labelUpdater(self,newText):
        self.ids.labText.text = newText

class ListButton(ListItemButton):
    data = ListProperty()

class AppScreenManager(ScreenManager):
    pass
class Tester(App): 
    pass
if __name__ == '__main__':
    Tester().run() 
Alex Ford
  • 659
  • 3
  • 13

3 Answers3

1

an alternative solution:

...
<ListButton>:
    height: self.texture_size[1]
    on_release: self.parent.parent.parent.parent.parent.ChangeScreen(self.index)

...

but just do this for the events like on_press, on_release never for on_parent because they have no parent when this last event is call

for the explanation all you have to know is that self.parent.parent.parent is your ListView

I hope this helps

Simon Mengong
  • 2,625
  • 10
  • 22
1

Here what I think you should do:

  • ChangeScreen method should be moved to ScreenManager: it's a screen managers who manages screen changes.

  • Reaction on changing selection should be placed inside ListAdapter: list adapter - is a controller that manages what happens on selection with its on_selection_change event. You can achieve it inheriting ListAdapter same way as you did with ListItemButton.

  • Placing callbacks calling in kvfile is (IMHO) bad idea: kvfile is sort of view, leave callbacks to cotrollers such as ListAdapter. Again it can be achieved inheriting ListAdapter.

  • Since ListAdapter uses ScreenManager's ChangeScreen method, adapter should be inited with screen (alternative to it can be as EL3PHANTEN suggested putting ScreenManager into App, but it becomes sort of global variable then).

Here's refactored code that contains all suggested above:

tester.kv

#: import main main

AppScreenManager:
    DemoScreen1:
        id: screen1
    DemoScreen2:
        id: screen2

<DemoScreen1>:
    name: "demoscreen1"
    BoxLayout:
        orientation: "vertical"
        ListView:
            adapter:
                main.MyListAdapter(
                data=["Screen 1","Screen 2"], 
                cls=main.MyListButton, 
                scr=root.parent
                )

<DemoScreen2>:
    name: "demoscreen2"
    orientation: "vertical"
    BoxLayout:
        orientation: "vertical"
        Label:
            id: labText
            text: "Hello World"

<MyListButton>:
    height: self.texture_size[1]

main.py

import kivy
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import StringProperty, ObjectProperty, ListProperty
from kivy.uix.listview import ListItemButton
from kivy.adapters.listadapter import ListAdapter


class DemoScreen1(Screen):
    pass


class DemoScreen2(Screen):
    labText = StringProperty()
    def labelUpdater(self,newText):
        self.ids.labText.text = newText


class MyListButton(ListItemButton):
    data = ListProperty()


class MyListAdapter(ListAdapter):
    scr = ObjectProperty()

    def on_selection_change(self, *args):
        self.scr.ChangeScreen(self.selection[0].index)


class AppScreenManager(ScreenManager):
    def ChangeScreen(self, option):
        if option == 0:
            print("Screen1")
            self.ids.screen2.labelUpdater("Screen 1 was pressed")
        else:
            print("Screen2")
            self.ids.screen2.labelUpdater("Screen 2 was pressed")
        self.current = "demoscreen2"


class Tester(App): 
    pass


if __name__ == '__main__':
    Tester().run() 
Mikhail Gerasimov
  • 36,989
  • 16
  • 116
  • 159
0

Try put the changeScreen method in the ListButton class instead.
Then make the ScreenManager as an attribute in the App class. Thet way you can access it as app.sm in kv.
Then you can pass app.sm to the ChangeScreen method, to get the screens.
Here is your modified example:

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.listview import ListItemButton
from kivy.lang import Builder


class DemoScreen2(Screen):

    def labelUpdater(self,newText):
        self.ids.labText.text = newText


class ListButton(ListItemButton):
    data = ListProperty()

    def ChangeScreen(self, option, sm):
        if option == 0:
            print("Screen1")
            sm.ids.screen2.labelUpdater("Screen 1 was pressed")
        else:
            print("Screen2")
            sm.ids.screen2.labelUpdater("Screen 2 was pressed")
        sm.current = "demoscreen2"


class AppScreenManager(ScreenManager):
    pass


KV = """

#: import ListAdapter kivy.adapters.listadapter.ListAdapter
#: import Factory kivy.factory.Factory

<AppScreenManager>:
    DemoScreen1:
        id: screen1
    DemoScreen2:
        id: screen2

<DemoScreen1@Screen>:
    name: "demoscreen1"
    BoxLayout:
        orientation: "vertical"
        ListView:
            adapter:
                ListAdapter(data=["Screen 1","Screen 2"], cls=Factory.ListButton)

<DemoScreen2>:
    name: "demoscreen2"
    orientation: "vertical"
    BoxLayout:
        orientation: "vertical"
        Button:
            id: labText
            text: "Hello World"
            on_release:
                app.sm.current = "demoscreen1"

<ListButton>:
    height: self.texture_size[1]
    on_release: root.ChangeScreen(self.index,app.sm)

"""


class Tester(App):

    def build(self):
        Builder.load_string(KV)
        self.sm = AppScreenManager()
        return self.sm

if __name__ == '__main__':
    Tester().run()
el3ien
  • 5,362
  • 1
  • 17
  • 33