Mercurial > libervia-backend
comparison sat_frontends/jp/xmlui_manager.py @ 3568:04283582966f
core, frontends: fix invalid translatable strings.
Some f-strings where used in translatable text, this has been fixed by using explicit
`format()` call (using a script based on `tokenize`).
As tokenize messes with spaces, a reformating tool (`black`) has been applied to some
files afterwards.
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 14 Jun 2021 18:35:12 +0200 |
parents | be6d91572633 |
children | 53a8b50d69ca |
comparison
equal
deleted
inserted
replaced
3567:a240748ed686 | 3568:04283582966f |
---|---|
172 def no_select(self): | 172 def no_select(self): |
173 return "noselect" in self.style | 173 return "noselect" in self.style |
174 | 174 |
175 | 175 |
176 class EmptyWidget(xmlui_base.EmptyWidget, Widget): | 176 class EmptyWidget(xmlui_base.EmptyWidget, Widget): |
177 | |
178 def __init__(self, xmlui_parent): | 177 def __init__(self, xmlui_parent): |
179 Widget.__init__(self, xmlui_parent) | 178 Widget.__init__(self, xmlui_parent) |
180 | 179 |
181 async def show(self): | 180 async def show(self): |
182 self.host.disp('') | 181 self.host.disp("") |
183 | 182 |
184 | 183 |
185 class TextWidget(xmlui_base.TextWidget, ValueWidget): | 184 class TextWidget(xmlui_base.TextWidget, ValueWidget): |
186 type = "text" | 185 type = "text" |
187 | 186 |
197 try: | 196 try: |
198 return self._xmlui_for_name | 197 return self._xmlui_for_name |
199 except AttributeError: | 198 except AttributeError: |
200 return None | 199 return None |
201 | 200 |
202 async def show(self, end='\n', ansi=""): | 201 async def show(self, end="\n", ansi=""): |
203 """show label | 202 """show label |
204 | 203 |
205 @param end(str): same as for [JP.disp] | 204 @param end(str): same as for [JP.disp] |
206 @param ansi(unicode): ansi escape code to print before label | 205 @param ansi(unicode): ansi escape code to print before label |
207 """ | 206 """ |
208 self.disp(A.color(ansi, self.value), end=end) | 207 self.disp(A.color(ansi, self.value), end=end) |
209 | 208 |
210 | 209 |
211 class JidWidget(xmlui_base.JidWidget, TextWidget): | 210 class JidWidget(xmlui_base.JidWidget, TextWidget): |
212 type = "jid" | 211 type = "jid" |
212 | |
213 | 213 |
214 class StringWidget(xmlui_base.StringWidget, InputWidget): | 214 class StringWidget(xmlui_base.StringWidget, InputWidget): |
215 type = "string" | 215 type = "string" |
216 | 216 |
217 async def show(self): | 217 async def show(self): |
219 self.disp(self.value) | 219 self.disp(self.value) |
220 else: | 220 else: |
221 elems = [] | 221 elems = [] |
222 self.verboseName(elems) | 222 self.verboseName(elems) |
223 if self.value: | 223 if self.value: |
224 elems.append(_(f"(enter: {self.value})")) | 224 elems.append(_("(enter: {value})").format(value=self.value)) |
225 elems.extend([C.A_HEADER, "> "]) | 225 elems.extend([C.A_HEADER, "> "]) |
226 value = await self.host.ainput(A.color(*elems)) | 226 value = await self.host.ainput(A.color(*elems)) |
227 if value: | 227 if value: |
228 # TODO: empty value should be possible | 228 # TODO: empty value should be possible |
229 # an escape key should be used for default instead of enter with empty value | 229 # an escape key should be used for default instead of enter with empty value |
242 self.verboseName() | 242 self.verboseName() |
243 if self.read_only or self.root.read_only: | 243 if self.read_only or self.root.read_only: |
244 self.disp(self.value) | 244 self.disp(self.value) |
245 else: | 245 else: |
246 if self.value: | 246 if self.value: |
247 self.disp(A.color(C.A_HEADER, "↓ current value ↓\n", A.FG_CYAN, self.value, | 247 self.disp( |
248 "")) | 248 A.color(C.A_HEADER, "↓ current value ↓\n", A.FG_CYAN, self.value, "") |
249 ) | |
249 | 250 |
250 values = [] | 251 values = [] |
251 while True: | 252 while True: |
252 try: | 253 try: |
253 if not values: | 254 if not values: |
254 line = await self.host.ainput(A.color(C.A_HEADER, "[Ctrl-D to finish]> ")) | 255 line = await self.host.ainput( |
256 A.color(C.A_HEADER, "[Ctrl-D to finish]> ") | |
257 ) | |
255 else: | 258 else: |
256 line = await self.host.ainput() | 259 line = await self.host.ainput() |
257 values.append(line) | 260 values.append(line) |
258 except EOFError: | 261 except EOFError: |
259 break | 262 break |
260 | 263 |
261 self.value = '\n'.join(values).rstrip() | 264 self.value = "\n".join(values).rstrip() |
262 | 265 |
263 | 266 |
264 class XHTMLBoxWidget(xmlui_base.XHTMLBoxWidget, StringWidget): | 267 class XHTMLBoxWidget(xmlui_base.XHTMLBoxWidget, StringWidget): |
265 type = "xhtmlbox" | 268 type = "xhtmlbox" |
266 | 269 |
267 async def show(self): | 270 async def show(self): |
268 # FIXME: we use bridge in a blocking way as permitted by python-dbus | 271 # FIXME: we use bridge in a blocking way as permitted by python-dbus |
269 # this only for now to make it simpler, it must be refactored | 272 # this only for now to make it simpler, it must be refactored |
270 # to use async when jp will be fully async (expected for 0.8) | 273 # to use async when jp will be fully async (expected for 0.8) |
271 self.value = await self.host.bridge.syntaxConvert( | 274 self.value = await self.host.bridge.syntaxConvert( |
272 self.value, C.SYNTAX_XHTML, "markdown", False, self.host.profile) | 275 self.value, C.SYNTAX_XHTML, "markdown", False, self.host.profile |
276 ) | |
273 await super(XHTMLBoxWidget, self).show() | 277 await super(XHTMLBoxWidget, self).show() |
274 | 278 |
275 | 279 |
276 class ListWidget(xmlui_base.ListWidget, OptionsWidget): | 280 class ListWidget(xmlui_base.ListWidget, OptionsWidget): |
277 type = "list" | 281 type = "list" |
283 self.disp(self.value) | 287 self.disp(self.value) |
284 return | 288 return |
285 if not self.options: | 289 if not self.options: |
286 return | 290 return |
287 | 291 |
288 # list display | 292 # list display |
289 self.verboseName() | 293 self.verboseName() |
290 | 294 |
291 for idx, (value, label) in enumerate(self.options): | 295 for idx, (value, label) in enumerate(self.options): |
292 elems = [] | 296 elems = [] |
293 if not self.root.read_only: | 297 if not self.root.read_only: |
302 if len(self.options) == 1: | 306 if len(self.options) == 1: |
303 # we have only one option, no need to ask | 307 # we have only one option, no need to ask |
304 self.value = self.options[0][0] | 308 self.value = self.options[0][0] |
305 return | 309 return |
306 | 310 |
307 # we ask use to choose an option | 311 # we ask use to choose an option |
308 choice = None | 312 choice = None |
309 limit_max = len(self.options) - 1 | 313 limit_max = len(self.options) - 1 |
310 while choice is None or choice < 0 or choice > limit_max: | 314 while choice is None or choice < 0 or choice > limit_max: |
311 choice = await self.host.ainput( | 315 choice = await self.host.ainput( |
312 A.color(C.A_HEADER, _(f"your choice (0-{limit_max}): ")) | 316 A.color( |
317 C.A_HEADER, | |
318 _("your choice (0-{limit_max}): ").format(limit_max=limit_max), | |
319 ) | |
313 ) | 320 ) |
314 try: | 321 try: |
315 choice = int(choice) | 322 choice = int(choice) |
316 except ValueError: | 323 except ValueError: |
317 choice = None | 324 choice = None |
326 disp_true = A.color(A.FG_GREEN, "TRUE") | 333 disp_true = A.color(A.FG_GREEN, "TRUE") |
327 disp_false = A.color(A.FG_RED, "FALSE") | 334 disp_false = A.color(A.FG_RED, "FALSE") |
328 if self.read_only or self.root.read_only: | 335 if self.read_only or self.root.read_only: |
329 self.disp(disp_true if self.value else disp_false) | 336 self.disp(disp_true if self.value else disp_false) |
330 else: | 337 else: |
331 self.disp(A.color(C.A_HEADER, "0: ", | 338 self.disp( |
332 disp_false, A.RESET, | 339 A.color( |
333 " *" if not self.value else "")) | 340 C.A_HEADER, "0: ", disp_false, A.RESET, " *" if not self.value else "" |
334 self.disp(A.color(C.A_HEADER, "1: ", | 341 ) |
335 disp_true, A.RESET, | 342 ) |
336 " *" if self.value else "")) | 343 self.disp( |
344 A.color(C.A_HEADER, "1: ", disp_true, A.RESET, " *" if self.value else "") | |
345 ) | |
337 choice = None | 346 choice = None |
338 while choice not in ("0", "1"): | 347 while choice not in ("0", "1"): |
339 elems = [C.A_HEADER, _("your choice (0,1): ")] | 348 elems = [C.A_HEADER, _("your choice (0,1): ")] |
340 self.verboseName(elems) | 349 self.verboseName(elems) |
341 choice = await self.host.ainput(A.color(*elems)) | 350 choice = await self.host.ainput(A.color(*elems)) |
343 self.disp("") | 352 self.disp("") |
344 | 353 |
345 def _xmluiGetValue(self): | 354 def _xmluiGetValue(self): |
346 return C.boolConst(self.value) | 355 return C.boolConst(self.value) |
347 | 356 |
348 | 357 ## Containers ## |
349 ## Containers ## | |
350 | 358 |
351 | 359 |
352 class Container(Base): | 360 class Container(Base): |
353 category = "container" | 361 category = "container" |
354 | 362 |
381 class LabelContainer(xmlui_base.PairsContainer, Container): | 389 class LabelContainer(xmlui_base.PairsContainer, Container): |
382 type = "label" | 390 type = "label" |
383 | 391 |
384 async def show(self): | 392 async def show(self): |
385 for child in self.children: | 393 for child in self.children: |
386 end = '\n' | 394 end = "\n" |
387 # we check linked widget type | 395 # we check linked widget type |
388 # to see if we want the label on the same line or not | 396 # to see if we want the label on the same line or not |
389 if child.type == "label": | 397 if child.type == "label": |
390 for_name = child.for_name | 398 for_name = child.for_name |
391 if for_name: | 399 if for_name: |
394 if self.root.values_only or wid_type in ( | 402 if self.root.values_only or wid_type in ( |
395 "text", | 403 "text", |
396 "string", | 404 "string", |
397 "jid_input", | 405 "jid_input", |
398 ): | 406 ): |
399 end = ' ' | 407 end = " " |
400 elif wid_type == "bool" and for_widget.read_only: | 408 elif wid_type == "bool" and for_widget.read_only: |
401 end = ' ' | 409 end = " " |
402 await child.show(end=end, ansi=A.FG_CYAN) | 410 await child.show(end=end, ansi=A.FG_CYAN) |
403 else: | 411 else: |
404 await child.show() | 412 await child.show() |
405 | 413 |
406 | 414 ## Dialogs ## |
407 ## Dialogs ## | |
408 | 415 |
409 | 416 |
410 class Dialog(object): | 417 class Dialog(object): |
411 def __init__(self, xmlui_parent): | 418 def __init__(self, xmlui_parent): |
412 self.xmlui_parent = xmlui_parent | 419 self.xmlui_parent = xmlui_parent |
420 | 427 |
421 must be overriden by subclasses | 428 must be overriden by subclasses |
422 """ | 429 """ |
423 raise NotImplementedError(self.__class__) | 430 raise NotImplementedError(self.__class__) |
424 | 431 |
432 | |
425 class MessageDialog(xmlui_base.MessageDialog, Dialog): | 433 class MessageDialog(xmlui_base.MessageDialog, Dialog): |
426 | |
427 def __init__(self, xmlui_parent, title, message, level): | 434 def __init__(self, xmlui_parent, title, message, level): |
428 Dialog.__init__(self, xmlui_parent) | 435 Dialog.__init__(self, xmlui_parent) |
429 xmlui_base.MessageDialog.__init__(self, xmlui_parent) | 436 xmlui_base.MessageDialog.__init__(self, xmlui_parent) |
430 self.title, self.message, self.level = title, message, level | 437 self.title, self.message, self.level = title, message, level |
431 | 438 |
435 self.disp(A.color(C.A_HEADER, self.title)) | 442 self.disp(A.color(C.A_HEADER, self.title)) |
436 self.disp(self.message) | 443 self.disp(self.message) |
437 | 444 |
438 | 445 |
439 class NoteDialog(xmlui_base.NoteDialog, Dialog): | 446 class NoteDialog(xmlui_base.NoteDialog, Dialog): |
440 | |
441 def __init__(self, xmlui_parent, title, message, level): | 447 def __init__(self, xmlui_parent, title, message, level): |
442 Dialog.__init__(self, xmlui_parent) | 448 Dialog.__init__(self, xmlui_parent) |
443 xmlui_base.NoteDialog.__init__(self, xmlui_parent) | 449 xmlui_base.NoteDialog.__init__(self, xmlui_parent) |
444 self.title, self.message, self.level = title, message, level | 450 self.title, self.message, self.level = title, message, level |
445 | 451 |
454 msg = self.message | 460 msg = self.message |
455 self.disp(msg, error=error) | 461 self.disp(msg, error=error) |
456 | 462 |
457 | 463 |
458 class ConfirmDialog(xmlui_base.ConfirmDialog, Dialog): | 464 class ConfirmDialog(xmlui_base.ConfirmDialog, Dialog): |
459 | |
460 def __init__(self, xmlui_parent, title, message, level, buttons_set): | 465 def __init__(self, xmlui_parent, title, message, level, buttons_set): |
461 Dialog.__init__(self, xmlui_parent) | 466 Dialog.__init__(self, xmlui_parent) |
462 xmlui_base.ConfirmDialog.__init__(self, xmlui_parent) | 467 xmlui_base.ConfirmDialog.__init__(self, xmlui_parent) |
463 self.title, self.message, self.level, self.buttons_set = ( | 468 self.title, self.message, self.level, self.buttons_set = ( |
464 title, message, level, buttons_set) | 469 title, |
470 message, | |
471 level, | |
472 buttons_set, | |
473 ) | |
465 | 474 |
466 async def show(self): | 475 async def show(self): |
467 # TODO: handle buttons_set and level | 476 # TODO: handle buttons_set and level |
468 self.disp(self.message) | 477 self.disp(self.message) |
469 if self.title: | 478 if self.title: |
470 self.disp(A.color(C.A_HEADER, self.title)) | 479 self.disp(A.color(C.A_HEADER, self.title)) |
471 input_ = None | 480 input_ = None |
472 while input_ not in ('y', 'n'): | 481 while input_ not in ("y", "n"): |
473 input_ = await self.host.ainput(f"{self.message} (y/n)? ") | 482 input_ = await self.host.ainput(f"{self.message} (y/n)? ") |
474 input_ = input_.lower() | 483 input_ = input_.lower() |
475 if input_ == 'y': | 484 if input_ == "y": |
476 self._xmluiValidated() | 485 self._xmluiValidated() |
477 else: | 486 else: |
478 self._xmluiCancelled() | 487 self._xmluiCancelled() |
479 | 488 |
480 | 489 ## Factory ## |
481 ## Factory ## | |
482 | 490 |
483 | 491 |
484 class WidgetFactory(object): | 492 class WidgetFactory(object): |
485 def __getattr__(self, attr): | 493 def __getattr__(self, attr): |
486 if attr.startswith("create"): | 494 if attr.startswith("create"): |
494 read_only = False | 502 read_only = False |
495 values_only = False | 503 values_only = False |
496 workflow = None | 504 workflow = None |
497 _submit_cb = None | 505 _submit_cb = None |
498 | 506 |
499 def __init__(self, host, parsed_dom, title=None, flags=None, callback=None, | 507 def __init__( |
500 ignore=None, whitelist=None, profile=None): | 508 self, |
509 host, | |
510 parsed_dom, | |
511 title=None, | |
512 flags=None, | |
513 callback=None, | |
514 ignore=None, | |
515 whitelist=None, | |
516 profile=None, | |
517 ): | |
501 xmlui_base.XMLUIPanel.__init__( | 518 xmlui_base.XMLUIPanel.__init__( |
502 self, | 519 self, |
503 host, | 520 host, |
504 parsed_dom, | 521 parsed_dom, |
505 title=title, | 522 title=title, |
586 xmlui_raw = data["xmlui"] | 603 xmlui_raw = data["xmlui"] |
587 xmlui = create(self.host, xmlui_raw) | 604 xmlui = create(self.host, xmlui_raw) |
588 await xmlui.show() | 605 await xmlui.show() |
589 if xmlui.submit_id: | 606 if xmlui.submit_id: |
590 await xmlui.onFormSubmitted() | 607 await xmlui.onFormSubmitted() |
591 # TODO: handle data other than XMLUI | 608 # TODO: handle data other than XMLUI |
592 if not XMLUIPanel._actions: | 609 if not XMLUIPanel._actions: |
593 if self._submit_cb is None: | 610 if self._submit_cb is None: |
594 self.host.quit() | 611 self.host.quit() |
595 else: | 612 else: |
596 self._submit_cb() | 613 self._submit_cb() |
620 | 637 |
621 def _xmluiClose(self): | 638 def _xmluiClose(self): |
622 pass | 639 pass |
623 | 640 |
624 | 641 |
625 create = partial(xmlui_base.create, class_map={ | 642 create = partial( |
626 xmlui_base.CLASS_PANEL: XMLUIPanel, | 643 xmlui_base.create, |
627 xmlui_base.CLASS_DIALOG: XMLUIDialog}) | 644 class_map={xmlui_base.CLASS_PANEL: XMLUIPanel, xmlui_base.CLASS_DIALOG: XMLUIDialog}, |
645 ) |