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
使用ScreenManager
的ChangeScreen
方法,适配器应该用屏幕启动(替代它可以像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()
所以我正在努力解决这一点,我找不到在按下 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
使用ScreenManager
的ChangeScreen
方法,适配器应该用屏幕启动(替代它可以像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()