使用 On_Press 事件更改屏幕,而无需动态创建按钮的 KV 语言

Use On_Press Event to Change Screen without KV Language for Dynamically Created Buttons

问题:

如何在不使用 KV 语言的情况下使用 On-Press 事件为动态创建的按钮更改屏幕 python?

目标:

能够通过单击动态创建的按钮导航到新屏幕,

[无需在 Kivy 中创建按钮,并且仍然可以在 Python 和 Kivy 中使用 Screenmanager(不确定在整个程序中是否必须坚持使用 Python 或 Kivy? ]

我已经尝试过的事情:

  1. 使用 button_share.bind(on_press = self.changer),然后是:

def changer(self,*args):
    ScreenManager()
    screenmanager.current = 'MainScreen'

但是我收到错误 ScreenManagerException: No Screen with name "MainScreen".

怀疑:

我认为这是因为我正在创建一个新的 ScreenManager 实例,而不是引用现有实例。为了解决这个问题,我考虑在 App class 中实例化 Screenmanager(),然后在我的按钮 def changer(self, *args) 方法中引用该实例化,但如果它与我实际使用的 ScreenManager 不同,那将毫无用处我所有的屏幕。而这些都是用KV语言定义的。如果不付出大量努力,我将无法将它们全部切换过来。

  1. 使用:

button_share.bind(on_press=partial(app.sm.setter('current'), (app.sm, "MainScreen")))`

但是我得到的错误是ValueError: ScreenManager.current accept only str

下面是一个完全可运行的例子:

注意:在这个例子中,我想点击 'Continue Editing' 按钮,然后点击 'Test 1'、'Test 2' 或 'Test 3' 按钮,让它把我带到另一个屏幕。

Python代码:

from kivy.app import App
# kivy.require("1.10.0")
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.clock import Clock
from kivy.uix.widget import Widget
#from kivy.base import runTouchApp
from kivy.properties import StringProperty, ObjectProperty, NumericProperty
from functools import partial

class ScrollableLabelDataEntryInstructions(BoxLayout):
    pass

class NewGarageScreen(Screen):
    pass

class ContinueEditingScreen(Screen):
    pass

class GarageNameBoxLayout(BoxLayout):
    box_share2 = ObjectProperty()
    sm = ScreenManager()

    def __init__(self, **kwargs):
        super(GarageNameBoxLayout, self).__init__(**kwargs)
        self.orientation = "vertical"
        Clock.schedule_interval(self.create_button, 5)

    def create_button(self, *args):
        self.box_share2.clear_widgets()
        app = App.get_running_app()
        #put GarageNameStartList data into app class, then pull from it in this class
        top_button_share = 1.1
        color = (.4, .4, .4, 1)
        for i in range(len(app.GarageNameStartList)):
            top_button_share -= .4
            id_ = app.GarageNameStartList[i]

            button_share = Button(background_normal='',
                                  background_color = color,
                                  id = id_,
                                  pos_hint = {"x": 0, "top": top_button_share},
                                  size_hint_y = None,
                                  height = 60,
                                  font_size = 30,
                                  text = app.GarageNameStartList[i])
            button_share.bind(on_press = self.changer)
            #button_share.bind(on_press=partial(app.sm.setter('current'), (app.sm, "MainScreen")))
            self.box_share2.add_widget(button_share)

    def changer(self,*args):
        ScreenManager()
        #app = App.get_running_app()
        screenmanager.current = 'MainScreen'

class BackHomeWidget(Widget):
    pass

class MainScreen(Screen):
    pass

class AnotherScreen(Screen):
    pass

class ScreenManagement(ScreenManager):
    pass

presentation = Builder.load_file("example_on_press.kv")

class MainApp(App):
    GarageNameStartList = ["Test1", "Test2", "Test3"]

    def Update_GarageNameStartList(self, *args):
        self.GarageNameStartList = ["Test1", "Test2", "Test3"]   


    def build(self):
        return presentation

if __name__ == "__main__":
    MainApp().run()

KV代码:

#: import FadeTransition kivy.uix.screenmanager.FadeTransition

ScreenManagement:
    transition: FadeTransition()
    MainScreen:
    AnotherScreen:
    NewGarageScreen:
    ContinueEditingScreen:

<SmallNavButton@Button>:    
    font_size: 32
    size: 125, 50    
    color: 0,1,0,1

<MedButton@Button>:
    font_size: 30
    size_hint: 0.25, 0.1
    color: 0,1,0,1

<BackHomeWidget>:
    SmallNavButton:
        on_release: app.root.current = "main"
        text: "Home"
        pos: root.x, root.top - self.height

<MainScreen>:
    name: "main"
    FloatLayout: 
        MedButton:
            on_release: app.root.current = "edit"
            text: "Edit"
            pos_hint: {"x":0.3728, "top": 0.4}

<AnotherScreen>:
    name: "edit"
    BackHomeWidget:
        SmallNavButton:
            on_release: app.root.current = "main"
            text: "Back"
            pos: root.x, root.top - (2.25*(self.height))
    FloatLayout:
        MedButton:
            on_release: app.root.current = "continueediting"
            text: "Continue Editing"
            pos_hint: {"x":0.25, "top": 0.6} 
        MedButton:
            on_release: app.root.current = "newgarage"
            text: "Create New"
            pos_hint: {"x":0.3728, "top": 0.4}

<NewGarageScreen>:
    name: "newgarage"
    BackHomeWidget:
        SmallNavButton:
            on_release: app.root.current = "edit"
            text: "Back"
            pos: root.x, root.top - (2.25*(self.height))
    FloatLayout:
        MedButton:
            text: "1. Groundfloor"
            pos_hint: {"x":0, "top": 0.6}


<GarageNameBoxLayout>:
    box_share2: box_share2
    ScrollView:
        GridLayout:
            id: box_share2
            cols: 1
            size_hint_y: None
            size_hint_x: 0.5
            spacing: 5
            padding: 130
            height: self.minimum_height
            canvas:
                Color: 
                    rgb: 0, 0, 0
                Rectangle:
                    pos: self.pos
                    size: self.size         

<ContinueEditingScreen>:
    name: "continueediting"
    GarageNameBoxLayout:
    BackHomeWidget:
        SmallNavButton:
            on_release: app.root.current = "edit"
            text: "Back"
            pos: root.x, root.top - (2.25*(self.height))

您的代码可以在以下方面进行改进:

  • 您不必在 .py 中创建 box_share2,因为您是在 .kv

  • 中创建它
  • 当您使用 sm = ScreenManager() 时,您正在创建另一个与原始版本不同的 ScreenManager,这是没有必要的。

  • 没有必要使用rangelen,让你的代码可读性变差,你只需要迭代。

  • 如果我们查看 .kv 的结构,我们会发现表示对象是 ScreenManager,因此您可以通过 app.root.

    获取它

使用上面的代码,解决方案是:

[...]

class GarageNameBoxLayout(BoxLayout):
    def __init__(self, **kwargs):
        super(GarageNameBoxLayout, self).__init__(**kwargs)
        self.orientation = "vertical"
        Clock.schedule_interval(self.create_button, 5)

    def create_button(self, *args):
        self.box_share2.clear_widgets()
        app = App.get_running_app()
        sm = app.root

        #put GarageNameStartList data into app class, then pull from it in this class
        top_button_share = 1.1
        color = (.4, .4, .4, 1)
        for text in app.GarageNameStartList:
            top_button_share -= .4
            id_ = text
            button_share = Button(background_normal='',
                                  background_color = color,
                                  id = id_,
                                  pos_hint = {"x": 0, "top": top_button_share},
                                  size_hint_y = None,
                                  height = 60,
                                  font_size = 30,
                                  text = text)
            button_share.bind(on_press=lambda *args: setattr(sm, 'current', "main"))
            self.box_share2.add_widget(button_share)

[...]