Kivy - 如何从ListView调用函数?

4

我在处理一个问题,无法找到在按下ListView项目时通过过程传递值的方法。下面的示例是我想要发生的事情以及我的主要代码如何设置的简化版本。

目标是在KV文件中看到带有哈希标记的on_release:上的ChangeScreen(self.index)。然而问题是我不知道如何成功调用ChangeScreen。谢谢!

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() 
3个回答

1
一种替代方案:

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

...

但是只对类似于on_presson_release这样的事件进行操作,而不是对on_parent进行操作,因为当调用最后一个事件时,它们没有父级。

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

我希望这可以帮到你。


1
我认为你应该这样做:
  • ChangeScreen方法移到ScreenManager中:屏幕管理器是负责管理屏幕更改的。

  • 对选择更改的反应应放在ListAdapter中:列表适配器是控制器,负责管理其on_selection_change事件发生时会发生什么。您可以通过继承ListAdapter来实现它,就像使用ListItemButton一样。

  • 在kv文件中放置回调调用(我认为)是一个坏主意:kv文件是一种视图,把回调留给像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() 

0
尝试将changeScreen方法放在ListButton类中。
然后将ScreenManager作为App类的属性。这样你就可以在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()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接