You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python Kivy切换屏幕后ModalView无法关闭的技术求助

解决ModalView切换屏幕后无法关闭的问题

我看了你的代码,问题出在切换屏幕时没有主动关闭ModalView,而且你的方法赋值逻辑有点绕,导致弹窗的关闭逻辑完全没被触发。咱们一步步来修复:

问题根源

在Screen1的MyButton方法里,你直接切换到Screen2,但完全没调用ModalView的dismiss()方法。ModalView是独立于Screen的弹窗组件,不会因为屏幕切换自动关闭,必须手动触发关闭操作。另外,你通过来回赋值self.MyButton的方式传递逻辑,很容易让代码逻辑混乱,不如直接把完整逻辑绑定到按钮点击事件上。

修复方案

  1. 在切换屏幕前关闭ModalView:在Screen1的按钮回调方法中,先调用ModalView的dismiss(),再执行屏幕切换。
  2. 简化方法绑定逻辑:直接在创建ModalView时传入点击回调,避免来回赋值的混乱。

修改后的关键代码

首先调整Screen1的MyButton方法,加上关闭弹窗的步骤:

def MyButton(self, button, instance, *args):
    # 先关闭ModalView
    self.sview.MM.dismiss()
    # 再切换到Screen2
    self.manager.current = "screen2"
    print("screen 2")

然后优化MyModal的初始化逻辑,支持传入外部点击回调:

class MyModal(ModalView):
    text = StringProperty("text")
    def __init__(self, on_button_click=None, **kwargs):
        super(MyModal, self).__init__(**kwargs)
        self.size_hint = (None, None)
        self.size = (200, 200)
        self.layout=GridLayout(cols=1,padding=10,spacing=10)
        self.layout.add_widget(Label(text=self.text))
        self.btn=Button(text="My Button")
        # 绑定外部传入的点击回调
        if on_button_click:
            self.btn.bind(on_release=on_button_click)
        self.layout.add_widget(self.btn)
        self.add_widget(self.layout)

最后调整SView的ModalOpen方法,创建Modal时直接传入Screen1的回调:

def ModalOpen(self, button, instance, *args):
    # 创建Modal并绑定Screen1的回调方法
    self.MM = MyModal(on_button_click=partial(self.parent.parent.MyButton, button))
    self.MM.open()

完整修改后的代码

from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.properties import StringProperty, partial
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.modalview import ModalView
from kivy.uix.screenmanager import Screen
from kivy.uix.screenmanager import ScreenManager
from kivy.uix.scrollview import ScrollView
from kivy.uix.stacklayout import StackLayout

# My Scroll View for putting buttons to screen1
class SView(ScrollView):
    def __init__(self, **kwargs):
        super(SView, self).__init__(**kwargs)
        Clock.schedule_interval(self.update, 1)
        # 初始化时不创建Modal,打开时再实例化
        self.MM = None

    def ModalOpen(self, button, instance, *args):
        # 创建Modal并传入Screen1的回调
        self.MM = MyModal(on_button_click=partial(self.parent.parent.MyButton, button))
        self.MM.open()

    def update(self, *args):
        layout = GridLayout(width=480, padding=10, cols=7, spacing=10, size_hint_y=None)
        layout.bind(minimum_height=layout.setter('height'))
        self.buttons=["One", "Two", "Three","One", "Two", "Three","One", "Two", "Three","One", "Two", "Three","One", "Two", "Three","One", "Two", "Three","One", "Two", "Three","One", "Two", "Three","One", "Two", "Three","One", "Two", "Three"]
        for key in self.buttons:
            stacklayout = StackLayout(size_hint_y=None, size_hint_x=None, height=130, width=100, orientation='lr-tb')
            btn = Button(text=key, size_hint_y=None, size_hint_x=None, height=130, width=100)
            stacklayout.add_widget(btn)
            btn.bind(on_release=partial(self.ModalOpen, key))
            layout.add_widget(stacklayout)
        self.clear_widgets()
        self.add_widget(layout)

# when press the button of screen1 open the modal
class MyModal(ModalView):
    text = StringProperty("text")
    def __init__(self, on_button_click=None, **kwargs):
        super(MyModal, self).__init__(**kwargs)
        self.size_hint = (None, None)
        self.size = (200, 200)
        self.layout=GridLayout(cols=1,padding=10,spacing=10)
        self.layout.add_widget(Label(text=self.text))
        self.btn=Button(text="My Button")
        if on_button_click:
            self.btn.bind(on_release=on_button_click)
        self.layout.add_widget(self.btn)
        self.add_widget(self.layout)

# My Screen1
class Screen1(Screen):
    def __init__(self, **kwargs):
        super(Screen1, self).__init__(**kwargs)
        self.sview=SView()
        self.mylayout = GridLayout(cols=1)
        self.mylayout.add_widget(self.sview)
        self.add_widget(self.mylayout)

    def MyButton(self, button, instance, *args):
        # 先关闭弹窗
        self.sview.MM.dismiss()
        # 再切换屏幕
        self.manager.current = "screen2"
        print("screen 2")

class Screen2(Screen):
    def __init__(self, **kwargs):
        super(Screen2, self).__init__(**kwargs)
        # 增加返回按钮方便测试
        self.add_widget(Button(text="Back to Screen1", on_release=lambda x: self.manager.current="screen1"))

# my builder string
Builder.load_string('''
<SM>:
    Screen1:
        name: "screen1"
    Screen2:
        name: "screen2"
''')

# my screen Manager
class SM(ScreenManager):
    def __init__(self, **kwargs):
        super(SM, self).__init__(**kwargs)
        self.current = 'screen1'

# my app
class Main(App):
    def __init__(self, **kwargs):
        super(Main, self).__init__(**kwargs)

    def build(self):
        self.title = "MAVA"
        # 注释本地图标路径避免运行报错,可自行恢复
        # self.icon = "./images/logo/logo.png"
        return SM()

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

效果说明

现在点击Modal里的"My Button"时,会先关闭弹窗,再切换到Screen2,完美解决了弹窗无法关闭的问题。同时优化了代码逻辑,让回调绑定更直观,还给Screen2加了返回按钮,方便测试来回切换。

内容的提问来源于stack exchange,提问作者Nejat CKl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 03:58:30