Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/prompt_toolkit/shortcuts/dialogs.py: 34%
91 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
1from __future__ import annotations
3import functools
4from asyncio import get_running_loop
5from typing import Any, Callable, Sequence, TypeVar
7from prompt_toolkit.application import Application
8from prompt_toolkit.application.current import get_app
9from prompt_toolkit.buffer import Buffer
10from prompt_toolkit.completion import Completer
11from prompt_toolkit.eventloop import run_in_executor_with_context
12from prompt_toolkit.filters import FilterOrBool
13from prompt_toolkit.formatted_text import AnyFormattedText
14from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous
15from prompt_toolkit.key_binding.defaults import load_key_bindings
16from prompt_toolkit.key_binding.key_bindings import KeyBindings, merge_key_bindings
17from prompt_toolkit.layout import Layout
18from prompt_toolkit.layout.containers import AnyContainer, HSplit
19from prompt_toolkit.layout.dimension import Dimension as D
20from prompt_toolkit.styles import BaseStyle
21from prompt_toolkit.validation import Validator
22from prompt_toolkit.widgets import (
23 Box,
24 Button,
25 CheckboxList,
26 Dialog,
27 Label,
28 ProgressBar,
29 RadioList,
30 TextArea,
31 ValidationToolbar,
32)
34__all__ = [
35 "yes_no_dialog",
36 "button_dialog",
37 "input_dialog",
38 "message_dialog",
39 "radiolist_dialog",
40 "checkboxlist_dialog",
41 "progress_dialog",
42]
45def yes_no_dialog(
46 title: AnyFormattedText = "",
47 text: AnyFormattedText = "",
48 yes_text: str = "Yes",
49 no_text: str = "No",
50 style: BaseStyle | None = None,
51) -> Application[bool]:
52 """
53 Display a Yes/No dialog.
54 Return a boolean.
55 """
57 def yes_handler() -> None:
58 get_app().exit(result=True)
60 def no_handler() -> None:
61 get_app().exit(result=False)
63 dialog = Dialog(
64 title=title,
65 body=Label(text=text, dont_extend_height=True),
66 buttons=[
67 Button(text=yes_text, handler=yes_handler),
68 Button(text=no_text, handler=no_handler),
69 ],
70 with_background=True,
71 )
73 return _create_app(dialog, style)
76_T = TypeVar("_T")
79def button_dialog(
80 title: AnyFormattedText = "",
81 text: AnyFormattedText = "",
82 buttons: list[tuple[str, _T]] = [],
83 style: BaseStyle | None = None,
84) -> Application[_T]:
85 """
86 Display a dialog with button choices (given as a list of tuples).
87 Return the value associated with button.
88 """
90 def button_handler(v: _T) -> None:
91 get_app().exit(result=v)
93 dialog = Dialog(
94 title=title,
95 body=Label(text=text, dont_extend_height=True),
96 buttons=[
97 Button(text=t, handler=functools.partial(button_handler, v))
98 for t, v in buttons
99 ],
100 with_background=True,
101 )
103 return _create_app(dialog, style)
106def input_dialog(
107 title: AnyFormattedText = "",
108 text: AnyFormattedText = "",
109 ok_text: str = "OK",
110 cancel_text: str = "Cancel",
111 completer: Completer | None = None,
112 validator: Validator | None = None,
113 password: FilterOrBool = False,
114 style: BaseStyle | None = None,
115 default: str = "",
116) -> Application[str]:
117 """
118 Display a text input box.
119 Return the given text, or None when cancelled.
120 """
122 def accept(buf: Buffer) -> bool:
123 get_app().layout.focus(ok_button)
124 return True # Keep text.
126 def ok_handler() -> None:
127 get_app().exit(result=textfield.text)
129 ok_button = Button(text=ok_text, handler=ok_handler)
130 cancel_button = Button(text=cancel_text, handler=_return_none)
132 textfield = TextArea(
133 text=default,
134 multiline=False,
135 password=password,
136 completer=completer,
137 validator=validator,
138 accept_handler=accept,
139 )
141 dialog = Dialog(
142 title=title,
143 body=HSplit(
144 [
145 Label(text=text, dont_extend_height=True),
146 textfield,
147 ValidationToolbar(),
148 ],
149 padding=D(preferred=1, max=1),
150 ),
151 buttons=[ok_button, cancel_button],
152 with_background=True,
153 )
155 return _create_app(dialog, style)
158def message_dialog(
159 title: AnyFormattedText = "",
160 text: AnyFormattedText = "",
161 ok_text: str = "Ok",
162 style: BaseStyle | None = None,
163) -> Application[None]:
164 """
165 Display a simple message box and wait until the user presses enter.
166 """
167 dialog = Dialog(
168 title=title,
169 body=Label(text=text, dont_extend_height=True),
170 buttons=[Button(text=ok_text, handler=_return_none)],
171 with_background=True,
172 )
174 return _create_app(dialog, style)
177def radiolist_dialog(
178 title: AnyFormattedText = "",
179 text: AnyFormattedText = "",
180 ok_text: str = "Ok",
181 cancel_text: str = "Cancel",
182 values: Sequence[tuple[_T, AnyFormattedText]] | None = None,
183 default: _T | None = None,
184 style: BaseStyle | None = None,
185) -> Application[_T]:
186 """
187 Display a simple list of element the user can choose amongst.
189 Only one element can be selected at a time using Arrow keys and Enter.
190 The focus can be moved between the list and the Ok/Cancel button with tab.
191 """
192 if values is None:
193 values = []
195 def ok_handler() -> None:
196 get_app().exit(result=radio_list.current_value)
198 radio_list = RadioList(values=values, default=default)
200 dialog = Dialog(
201 title=title,
202 body=HSplit(
203 [Label(text=text, dont_extend_height=True), radio_list],
204 padding=1,
205 ),
206 buttons=[
207 Button(text=ok_text, handler=ok_handler),
208 Button(text=cancel_text, handler=_return_none),
209 ],
210 with_background=True,
211 )
213 return _create_app(dialog, style)
216def checkboxlist_dialog(
217 title: AnyFormattedText = "",
218 text: AnyFormattedText = "",
219 ok_text: str = "Ok",
220 cancel_text: str = "Cancel",
221 values: Sequence[tuple[_T, AnyFormattedText]] | None = None,
222 default_values: Sequence[_T] | None = None,
223 style: BaseStyle | None = None,
224) -> Application[list[_T]]:
225 """
226 Display a simple list of element the user can choose multiple values amongst.
228 Several elements can be selected at a time using Arrow keys and Enter.
229 The focus can be moved between the list and the Ok/Cancel button with tab.
230 """
231 if values is None:
232 values = []
234 def ok_handler() -> None:
235 get_app().exit(result=cb_list.current_values)
237 cb_list = CheckboxList(values=values, default_values=default_values)
239 dialog = Dialog(
240 title=title,
241 body=HSplit(
242 [Label(text=text, dont_extend_height=True), cb_list],
243 padding=1,
244 ),
245 buttons=[
246 Button(text=ok_text, handler=ok_handler),
247 Button(text=cancel_text, handler=_return_none),
248 ],
249 with_background=True,
250 )
252 return _create_app(dialog, style)
255def progress_dialog(
256 title: AnyFormattedText = "",
257 text: AnyFormattedText = "",
258 run_callback: Callable[[Callable[[int], None], Callable[[str], None]], None] = (
259 lambda *a: None
260 ),
261 style: BaseStyle | None = None,
262) -> Application[None]:
263 """
264 :param run_callback: A function that receives as input a `set_percentage`
265 function and it does the work.
266 """
267 loop = get_running_loop()
268 progressbar = ProgressBar()
269 text_area = TextArea(
270 focusable=False,
271 # Prefer this text area as big as possible, to avoid having a window
272 # that keeps resizing when we add text to it.
273 height=D(preferred=10**10),
274 )
276 dialog = Dialog(
277 body=HSplit(
278 [
279 Box(Label(text=text)),
280 Box(text_area, padding=D.exact(1)),
281 progressbar,
282 ]
283 ),
284 title=title,
285 with_background=True,
286 )
287 app = _create_app(dialog, style)
289 def set_percentage(value: int) -> None:
290 progressbar.percentage = int(value)
291 app.invalidate()
293 def log_text(text: str) -> None:
294 loop.call_soon_threadsafe(text_area.buffer.insert_text, text)
295 app.invalidate()
297 # Run the callback in the executor. When done, set a return value for the
298 # UI, so that it quits.
299 def start() -> None:
300 try:
301 run_callback(set_percentage, log_text)
302 finally:
303 app.exit()
305 def pre_run() -> None:
306 run_in_executor_with_context(start)
308 app.pre_run_callables.append(pre_run)
310 return app
313def _create_app(dialog: AnyContainer, style: BaseStyle | None) -> Application[Any]:
314 # Key bindings.
315 bindings = KeyBindings()
316 bindings.add("tab")(focus_next)
317 bindings.add("s-tab")(focus_previous)
319 return Application(
320 layout=Layout(dialog),
321 key_bindings=merge_key_bindings([load_key_bindings(), bindings]),
322 mouse_support=True,
323 style=style,
324 full_screen=True,
325 )
328def _return_none() -> None:
329 "Button handler that returns None."
330 get_app().exit()