Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/prompt_toolkit/shortcuts/dialogs.py: 33%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

91 statements  

1from __future__ import annotations 

2 

3import functools 

4from typing import Any, Callable, Sequence, TypeVar 

5 

6from prompt_toolkit.application import Application 

7from prompt_toolkit.application.current import get_app 

8from prompt_toolkit.buffer import Buffer 

9from prompt_toolkit.completion import Completer 

10from prompt_toolkit.eventloop import run_in_executor_with_context 

11from prompt_toolkit.filters import FilterOrBool 

12from prompt_toolkit.formatted_text import AnyFormattedText 

13from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous 

14from prompt_toolkit.key_binding.defaults import load_key_bindings 

15from prompt_toolkit.key_binding.key_bindings import KeyBindings, merge_key_bindings 

16from prompt_toolkit.layout import Layout 

17from prompt_toolkit.layout.containers import AnyContainer, HSplit 

18from prompt_toolkit.layout.dimension import Dimension as D 

19from prompt_toolkit.styles import BaseStyle 

20from prompt_toolkit.validation import Validator 

21from prompt_toolkit.widgets import ( 

22 Box, 

23 Button, 

24 CheckboxList, 

25 Dialog, 

26 Label, 

27 ProgressBar, 

28 RadioList, 

29 TextArea, 

30 ValidationToolbar, 

31) 

32 

33__all__ = [ 

34 "yes_no_dialog", 

35 "button_dialog", 

36 "input_dialog", 

37 "message_dialog", 

38 "radiolist_dialog", 

39 "checkboxlist_dialog", 

40 "progress_dialog", 

41] 

42 

43 

44def yes_no_dialog( 

45 title: AnyFormattedText = "", 

46 text: AnyFormattedText = "", 

47 yes_text: str = "Yes", 

48 no_text: str = "No", 

49 style: BaseStyle | None = None, 

50) -> Application[bool]: 

51 """ 

52 Display a Yes/No dialog. 

53 Return a boolean. 

54 """ 

55 

56 def yes_handler() -> None: 

57 get_app().exit(result=True) 

58 

59 def no_handler() -> None: 

60 get_app().exit(result=False) 

61 

62 dialog = Dialog( 

63 title=title, 

64 body=Label(text=text, dont_extend_height=True), 

65 buttons=[ 

66 Button(text=yes_text, handler=yes_handler), 

67 Button(text=no_text, handler=no_handler), 

68 ], 

69 with_background=True, 

70 ) 

71 

72 return _create_app(dialog, style) 

73 

74 

75_T = TypeVar("_T") 

76 

77 

78def button_dialog( 

79 title: AnyFormattedText = "", 

80 text: AnyFormattedText = "", 

81 buttons: list[tuple[str, _T]] = [], 

82 style: BaseStyle | None = None, 

83) -> Application[_T]: 

84 """ 

85 Display a dialog with button choices (given as a list of tuples). 

86 Return the value associated with button. 

87 """ 

88 

89 def button_handler(v: _T) -> None: 

90 get_app().exit(result=v) 

91 

92 dialog = Dialog( 

93 title=title, 

94 body=Label(text=text, dont_extend_height=True), 

95 buttons=[ 

96 Button(text=t, handler=functools.partial(button_handler, v)) 

97 for t, v in buttons 

98 ], 

99 with_background=True, 

100 ) 

101 

102 return _create_app(dialog, style) 

103 

104 

105def input_dialog( 

106 title: AnyFormattedText = "", 

107 text: AnyFormattedText = "", 

108 ok_text: str = "OK", 

109 cancel_text: str = "Cancel", 

110 completer: Completer | None = None, 

111 validator: Validator | None = None, 

112 password: FilterOrBool = False, 

113 style: BaseStyle | None = None, 

114 default: str = "", 

115) -> Application[str]: 

116 """ 

117 Display a text input box. 

118 Return the given text, or None when cancelled. 

119 """ 

120 

121 def accept(buf: Buffer) -> bool: 

122 get_app().layout.focus(ok_button) 

123 return True # Keep text. 

124 

125 def ok_handler() -> None: 

126 get_app().exit(result=textfield.text) 

127 

128 ok_button = Button(text=ok_text, handler=ok_handler) 

129 cancel_button = Button(text=cancel_text, handler=_return_none) 

130 

131 textfield = TextArea( 

132 text=default, 

133 multiline=False, 

134 password=password, 

135 completer=completer, 

136 validator=validator, 

137 accept_handler=accept, 

138 ) 

139 

140 dialog = Dialog( 

141 title=title, 

142 body=HSplit( 

143 [ 

144 Label(text=text, dont_extend_height=True), 

145 textfield, 

146 ValidationToolbar(), 

147 ], 

148 padding=D(preferred=1, max=1), 

149 ), 

150 buttons=[ok_button, cancel_button], 

151 with_background=True, 

152 ) 

153 

154 return _create_app(dialog, style) 

155 

156 

157def message_dialog( 

158 title: AnyFormattedText = "", 

159 text: AnyFormattedText = "", 

160 ok_text: str = "Ok", 

161 style: BaseStyle | None = None, 

162) -> Application[None]: 

163 """ 

164 Display a simple message box and wait until the user presses enter. 

165 """ 

166 dialog = Dialog( 

167 title=title, 

168 body=Label(text=text, dont_extend_height=True), 

169 buttons=[Button(text=ok_text, handler=_return_none)], 

170 with_background=True, 

171 ) 

172 

173 return _create_app(dialog, style) 

174 

175 

176def radiolist_dialog( 

177 title: AnyFormattedText = "", 

178 text: AnyFormattedText = "", 

179 ok_text: str = "Ok", 

180 cancel_text: str = "Cancel", 

181 values: Sequence[tuple[_T, AnyFormattedText]] | None = None, 

182 default: _T | None = None, 

183 style: BaseStyle | None = None, 

184) -> Application[_T]: 

185 """ 

186 Display a simple list of element the user can choose amongst. 

187 

188 Only one element can be selected at a time using Arrow keys and Enter. 

189 The focus can be moved between the list and the Ok/Cancel button with tab. 

190 """ 

191 if values is None: 

192 values = [] 

193 

194 def ok_handler() -> None: 

195 get_app().exit(result=radio_list.current_value) 

196 

197 radio_list = RadioList(values=values, default=default) 

198 

199 dialog = Dialog( 

200 title=title, 

201 body=HSplit( 

202 [Label(text=text, dont_extend_height=True), radio_list], 

203 padding=1, 

204 ), 

205 buttons=[ 

206 Button(text=ok_text, handler=ok_handler), 

207 Button(text=cancel_text, handler=_return_none), 

208 ], 

209 with_background=True, 

210 ) 

211 

212 return _create_app(dialog, style) 

213 

214 

215def checkboxlist_dialog( 

216 title: AnyFormattedText = "", 

217 text: AnyFormattedText = "", 

218 ok_text: str = "Ok", 

219 cancel_text: str = "Cancel", 

220 values: Sequence[tuple[_T, AnyFormattedText]] | None = None, 

221 default_values: Sequence[_T] | None = None, 

222 style: BaseStyle | None = None, 

223) -> Application[list[_T]]: 

224 """ 

225 Display a simple list of element the user can choose multiple values amongst. 

226 

227 Several elements can be selected at a time using Arrow keys and Enter. 

228 The focus can be moved between the list and the Ok/Cancel button with tab. 

229 """ 

230 if values is None: 

231 values = [] 

232 

233 def ok_handler() -> None: 

234 get_app().exit(result=cb_list.current_values) 

235 

236 cb_list = CheckboxList(values=values, default_values=default_values) 

237 

238 dialog = Dialog( 

239 title=title, 

240 body=HSplit( 

241 [Label(text=text, dont_extend_height=True), cb_list], 

242 padding=1, 

243 ), 

244 buttons=[ 

245 Button(text=ok_text, handler=ok_handler), 

246 Button(text=cancel_text, handler=_return_none), 

247 ], 

248 with_background=True, 

249 ) 

250 

251 return _create_app(dialog, style) 

252 

253 

254def progress_dialog( 

255 title: AnyFormattedText = "", 

256 text: AnyFormattedText = "", 

257 run_callback: Callable[[Callable[[int], None], Callable[[str], None]], None] = ( 

258 lambda *a: None 

259 ), 

260 style: BaseStyle | None = None, 

261) -> Application[None]: 

262 """ 

263 :param run_callback: A function that receives as input a `set_percentage` 

264 function and it does the work. 

265 """ 

266 progressbar = ProgressBar() 

267 text_area = TextArea( 

268 focusable=False, 

269 # Prefer this text area as big as possible, to avoid having a window 

270 # that keeps resizing when we add text to it. 

271 height=D(preferred=10**10), 

272 ) 

273 

274 dialog = Dialog( 

275 body=HSplit( 

276 [ 

277 Box(Label(text=text)), 

278 Box(text_area, padding=D.exact(1)), 

279 progressbar, 

280 ] 

281 ), 

282 title=title, 

283 with_background=True, 

284 ) 

285 app = _create_app(dialog, style) 

286 

287 def set_percentage(value: int) -> None: 

288 progressbar.percentage = int(value) 

289 app.invalidate() 

290 

291 def log_text(text: str) -> None: 

292 loop = app.loop 

293 if loop is not None: 

294 loop.call_soon_threadsafe(text_area.buffer.insert_text, text) 

295 app.invalidate() 

296 

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() 

304 

305 def pre_run() -> None: 

306 run_in_executor_with_context(start) 

307 

308 app.pre_run_callables.append(pre_run) 

309 

310 return app 

311 

312 

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) 

318 

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 ) 

326 

327 

328def _return_none() -> None: 

329 "Button handler that returns None." 

330 get_app().exit()