5
.. versionadded:: 1.4.0
7
The :class:`ModalView` widget is used to create modal views. By default, the
8
view will cover the whole "parent" window.
10
Remember that the default size of a Widget is size_hint=(1, 1). If you don't
11
want your view to be fullscreen, either use size hints with values lower than
12
1 (for instance size_hint=(.8, .8)) or deactivate the size_hint and use fixed
18
Example of a simple 400x400 Hello world view::
20
view = ModalView(size_hint=(None, None), size=(400, 400))
21
view.add_widget(Label(text='Hello world'))
23
By default, any click outside the view will dismiss it. If you don't
24
want that, you can set :attr:`ModalView.auto_dismiss` to False::
26
view = ModalView(auto_dismiss=False)
27
view.add_widget(Label(text='Hello world'))
30
To manually dismiss/close the view, use the :meth:`ModalView.dismiss` method of
31
the ModalView instance::
35
Both :meth:`ModalView.open` and :meth:`ModalView.dismiss` are bindable. That
36
means you can directly bind the function to an action, e.g. to a button's
39
# create content and add it to the view
40
content = Button(text='Close me!')
41
view = ModalView(auto_dismiss=False)
42
view.add_widget(content)
44
# bind the on_press event of the button to the dismiss function
45
content.bind(on_press=view.dismiss)
54
There are two events available: `on_open` which is raised when the view is
55
opening, and `on_dismiss` which is raised when the view is closed.
56
For `on_dismiss`, you can prevent the view from closing by explictly returning
57
True from your callback. ::
59
def my_callback(instance):
60
print('ModalView', instance, 'is being dismissed, but is prevented!')
63
view.add_widget(Label(text='Hello world'))
64
view.bind(on_dismiss=my_callback)
68
.. versionchanged:: 1.5.0
69
The ModalView can be closed by hitting the escape key on the
70
keyboard if the :attr:`ModalView.auto_dismiss` property is True (the
75
__all__ = ('ModalView', )
77
from kivy.logger import Logger
78
from kivy.animation import Animation
79
from kivy.uix.anchorlayout import AnchorLayout
80
from kivy.properties import StringProperty, BooleanProperty, ObjectProperty, \
81
NumericProperty, ListProperty
84
class ModalView(AnchorLayout):
85
'''ModalView class. See module documentation for more information.
89
Fired when the ModalView is opened.
91
Fired when the ModalView is closed. If the callback returns True,
92
the dismiss will be canceled.
95
auto_dismiss = BooleanProperty(True)
96
'''This property determines if the view is automatically
97
dismissed when the user clicks outside it.
99
:attr:`auto_dismiss` is a :class:`~kivy.properties.BooleanProperty` and
103
attach_to = ObjectProperty(None)
104
'''If a widget is set on attach_to, the view will attach to the nearest
105
parent window of the widget. If none is found, it will attach to the
108
:attr:`attach_to` is an :class:`~kivy.properties.ObjectProperty` and
112
background_color = ListProperty([0, 0, 0, .7])
113
'''Background color in the format (r, g, b, a).
115
:attr:`background_color` is a :class:`~kivy.properties.ListProperty` and
116
defaults to [0, 0, 0, .7].
119
background = StringProperty(
120
'atlas://data/images/defaulttheme/modalview-background')
121
'''Background image of the view used for the view background.
123
:attr:`background` is a :class:`~kivy.properties.StringProperty` and
124
defaults to 'atlas://data/images/defaulttheme/modalview-background'.
127
border = ListProperty([16, 16, 16, 16])
128
'''Border used for :class:`~kivy.graphics.vertex_instructions.BorderImage`
129
graphics instruction. Used for the :attr:`background_normal` and the
130
:attr:`background_down` properties. Can be used when using custom
133
It must be a list of four values: (top, right, bottom, left). Read the
134
BorderImage instructions for more information about how to use it.
136
:attr:`border` is a :class:`~kivy.properties.ListProperty` and defaults to
140
# Internals properties used for graphical representation.
142
_anim_alpha = NumericProperty(0)
144
_anim_duration = NumericProperty(.1)
146
_window = ObjectProperty(None, allownone=True)
148
__events__ = ('on_open', 'on_dismiss')
150
def __init__(self, **kwargs):
152
super(ModalView, self).__init__(**kwargs)
154
def _search_window(self):
155
# get window to attach to
157
if self.attach_to is not None:
158
window = self.attach_to.get_parent_window()
160
window = self.attach_to.get_root_window()
162
from kivy.core.window import Window
166
def open(self, *largs):
167
'''Show the view window from the :attr:`attach_to` widget. If set, it
168
will attach to the nearest window. If the widget is not attached to any
169
window, the view will attach to the global
170
:class:`~kivy.core.window.Window`.
172
if self._window is not None:
173
Logger.warning('ModalView: you can only open once.')
176
self._window = self._search_window()
178
Logger.warning('ModalView: cannot open view, no window found.')
180
self._window.add_widget(self)
182
on_resize=self._align_center,
183
on_keyboard=self._handle_keyboard)
184
self.center = self._window.center
185
self.fbind('size', self._update_center)
186
a = Animation(_anim_alpha=1., d=self._anim_duration)
187
a.bind(on_complete=lambda *x: self.dispatch('on_open'))
191
def _update_center(self, *args):
194
# XXX HACK DONT REMOVE OR FOUND AND FIX THE ISSUE
195
# It seems that if we don't access to the center before assigning a new
196
# value, no dispatch will be done >_>
197
self.center = self._window.center
199
def dismiss(self, *largs, **kwargs):
200
'''Close the view if it is open. If you really want to close the
201
view, whatever the on_dismiss event returns, you can use the *force*
205
view = ModalView(...)
206
view.dismiss(force=True)
208
When the view is dismissed, it will be faded out before being
209
removed from the parent. If you don't want animation, use::
211
view.dismiss(animation=False)
214
if self._window is None:
216
if self.dispatch('on_dismiss') is True:
217
if kwargs.get('force', False) is not True:
219
if kwargs.get('animation', True):
220
Animation(_anim_alpha=0., d=self._anim_duration).start(self)
223
self._real_remove_widget()
226
def on_size(self, instance, value):
229
def _align_center(self, *l):
231
self.center = self._window.center
232
# hack to resize dark background on window resize
233
_window = self._window
235
self._window = _window
237
def on_touch_down(self, touch):
238
if not self.collide_point(*touch.pos):
239
if self.auto_dismiss:
242
super(ModalView, self).on_touch_down(touch)
245
def on_touch_move(self, touch):
246
super(ModalView, self).on_touch_move(touch)
249
def on_touch_up(self, touch):
250
super(ModalView, self).on_touch_up(touch)
253
def on__anim_alpha(self, instance, value):
254
if value == 0 and self._window is not None:
255
self._real_remove_widget()
257
def _real_remove_widget(self):
258
if self._window is None:
260
self._window.remove_widget(self)
262
on_resize=self._align_center,
263
on_keyboard=self._handle_keyboard)
269
def on_dismiss(self):
272
def _handle_keyboard(self, window, key, *largs):
273
if key == 27 and self.auto_dismiss:
278
if __name__ == '__main__':
279
from kivy.base import runTouchApp
280
from kivy.uix.button import Button
281
from kivy.uix.label import Label
282
from kivy.uix.gridlayout import GridLayout
283
from kivy.core.window import Window
286
content = GridLayout(cols=1)
287
content.add_widget(Label(text='This is a hello world'))
288
view = ModalView(size_hint=(None, None), size=(256, 256),
290
view.add_widget(content)
295
layout = GridLayout(cols=3)
297
btn = Button(text='click me %s' % x)
298
btn.bind(on_release=view.open)
299
layout.add_widget(btn)
300
Window.add_widget(layout)