Is there a way to make it so when my ModalView
/ Popup
widget is active but none of the buttons behind it will be triggered?
Below is an example of a ModalView/popup widget that will notify the user they entered something incorrect. I want the user to be able to click anywhere and the ModalView will disappear. The problem is that when this ModalView is visiable the buttons in the background are still active.
In this case, the BaseBoxLayout has a Label on top and Button on the bottom. I click on the button to trigger the ModalView/popup. If the user clicks on the top, the ModalView/popup disappears – as designed by my on_touch_down method.
However, if the user clicks anywhere in the bottom half (when the ModalView/popup is already triggered) then the ModalView/popup gets removed but then added again because the button in the background is still active.
How do I prevent this?
from kivymd.app import MDApp from kivy.lang import Builder from kivy.uix.boxlayout import BoxLayout from kivy.uix.modalview import ModalView KV = ''' Screen: canvas.before: Color: rgb: [127/255,160/255,100/255] Rectangle: pos: self.pos size: self.size BaseBoxLayout: orientation: "vertical" Label: text: "BaseBoxLayout " Button: text: "Ok button" on_press: self.parent.call_popup() <CustomPopup> BoxLayout: Label: text: "You can do this!" ''' class BaseBoxLayout(BoxLayout): def __init__(self,**kwargs): super().__init__(**kwargs) def call_popup(self): print('created popup') self.popup = CustomPopup() self.popup.open() class CustomPopup(ModalView): def __init__(self, **kwargs): super().__init__(**kwargs) def on_touch_down(self,touch): print('removed popup') self.parent.remove_widget(self) class MainApp(MDApp): def build(self): return Builder.load_string(KV) if __name__ == '__main__': MainApp().run()
Advertisement
Answer
As kivy gives you maximum flexibility in dispatching touch events, you can decide and implement that as you want.
Let’s consider some typical situations you might come across in this scenario.
- Your
ModalView
(I’ll call it popup) needs to be dismissed as soon as you press (corresponds toon_touch_down
event) anywhere on the screen (window). But you don’t want any underlying widget to interfere at this point. Here, this means you don’t want to dispatch the touch event further down the widget tree. You can do this by returningTrue
from methodon_touch_down
which will digest the event and stop it propagate further.
def on_touch_down(self,touch): self.parent.remove_widget(self) return True
- You want the popup to be dismissed as before but also you don’t want to override any default behavior. Call the superclass method. This is more preferable than in 1.
def on_touch_down(self,touch): self.parent.remove_widget(self) return super().on_touch_down(touch)
- Perhaps your popup will not cover whole window. In that case you want it to be dismissed only if touch is within its physical boundary otherwise not. Go as in 1 or 2. But what if the touch is outside its physical boundary ? Return
True
to digest the event. If you don’t handle that youself, callsuper
(but remember it will dismiss the popup unless you setauto_dismiss
to False).
def on_touch_down(self,touch): if self.collide_point(*touch.pos): self.parent.remove_widget(self) # Otherwise stop propagation and digest. return True # Or for default handling. # return super().on_touch_down(touch)
There might be countable (if not infinite) no. of situations you can face here. So better stop here.