Kivy - 如何从 ListView 调用函数?

Kivy - How do I call a function from the ListView?

所以我正在努力解决这一点,我找不到在按下 ListView 项时通过过程传递值的方法。下面的示例是我想要发生的事情以及我的主要代码是如何设置的一个更简单的版本。 目标是可以在 KV 文件中标记为 on_release: 的散列上看到 ChangeScreen(self.index)。但是问题是我不知道如何成功调用 ChangeScreen。谢谢!

基维:

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

尝试将 changeScreen 方法放在 ListButton class 中。
然后将 ScreenManager 作为 App class 中的一个属性。通过这种方式,您可以在 kv.
中以 app.sm 的形式访问它 然后你可以将 app.sm 传递给 ChangeScreen 方法,以获取屏幕。
这是您修改后的示例:

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

另一种解决方案:

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

...

但只对 on_press 这样的事件执行此操作,on_release 永远不会对 on_parent 执行此操作,因为在调用最后一个事件时它们没有父级

对于解释,您只需要知道 self.parent.parent.parent 就是您的 ListView

希望对您有所帮助

这是我认为你应该做的:

  • ChangeScreen 方法应该移动到 ScreenManager:这是一个 管理屏幕更改的屏幕管理员。

  • 更改选择的反应应放在 ListAdapter 内: 列表适配器 - 是一个控制器,用于管理选择时发生的事情 on_selection_change 事件。 你可以实现它继承 ListAdapter 和你做的一样 ListItemButton.

  • 在 kvfile 中调用回调是(恕我直言)坏主意:kvfile 是一种视图,将回调留给控制者,例如 ListAdapter。同样可以实现继承ListAdapter.

  • 因为ListAdapter使用ScreenManagerChangeScreen方法,适配器应该用屏幕启动(替代它可以像EL3PHANTEN建议的那样把ScreenManager 进入应用程序,但它变成了某种全局变量)。

这是包含以上所有建议的重构代码:

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