progress_dialog.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. # -*- coding: utf-8 -*-
  2. """
  3. Progress-Dialog (Modal) fuer die TUI.
  4. Zeigt einen Fortschrittsbalken mit Cancel-Button waehrend
  5. lang laufender Aktionen (z.B. TTS-Generierung).
  6. """
  7. from __future__ import annotations
  8. from textual.screen import ModalScreen
  9. from textual.containers import Vertical
  10. from textual.widgets import Static, Button, ProgressBar
  11. from textual.app import ComposeResult
  12. class ProgressDialog(ModalScreen[bool]):
  13. """
  14. Modaler Progress-Dialog.
  15. Zeigt Titel, Statustext, Fortschrittsbalken und Cancel-Button.
  16. Gibt True zurueck wenn erfolgreich abgeschlossen, False bei Cancel.
  17. """
  18. DEFAULT_CSS = """
  19. ProgressDialog {
  20. align: center middle;
  21. }
  22. ProgressDialog > Vertical {
  23. width: 64;
  24. height: auto;
  25. max-height: 14;
  26. border: thick $accent;
  27. background: $surface;
  28. padding: 1 2;
  29. }
  30. ProgressDialog > Vertical > #progress-title {
  31. text-style: bold;
  32. margin-bottom: 1;
  33. }
  34. ProgressDialog > Vertical > #progress-status {
  35. margin-bottom: 1;
  36. }
  37. ProgressDialog > Vertical > ProgressBar {
  38. margin-bottom: 1;
  39. }
  40. ProgressDialog > Vertical > #progress-cancel {
  41. width: 100%;
  42. }
  43. """
  44. def __init__(self, title: str = "Generierung", **kwargs) -> None:
  45. super().__init__(**kwargs)
  46. self._title = title
  47. self._cancelled = False
  48. @property
  49. def cancelled(self) -> bool:
  50. """Ob der Benutzer abgebrochen hat."""
  51. return self._cancelled
  52. def compose(self) -> ComposeResult:
  53. with Vertical():
  54. yield Static(self._title, id="progress-title")
  55. yield Static("[yellow]Wird vorbereitet...[/yellow]", id="progress-status")
  56. yield ProgressBar(total=100, show_eta=False, id="progress-bar")
  57. yield Button("Abbrechen", variant="warning", id="progress-cancel")
  58. def update_progress(self, current: int, total: int, message: str = "") -> None:
  59. """Aktualisiert Fortschritt und Status-Text."""
  60. try:
  61. bar = self.query_one("#progress-bar", ProgressBar)
  62. bar.update(total=total, progress=current)
  63. except Exception:
  64. pass
  65. if message:
  66. try:
  67. status = self.query_one("#progress-status", Static)
  68. pct = int(current / max(total, 1) * 100)
  69. status.update(f"[yellow]{message} ({pct}%)[/yellow]")
  70. except Exception:
  71. pass
  72. def finish(self, message: str = "Fertig!") -> None:
  73. """Zeigt Abschluss-Nachricht und schliesst nach kurzer Pause."""
  74. try:
  75. status = self.query_one("#progress-status", Static)
  76. status.update(f"[green]{message}[/green]")
  77. bar = self.query_one("#progress-bar", ProgressBar)
  78. bar.update(total=100, progress=100)
  79. btn = self.query_one("#progress-cancel", Button)
  80. btn.label = "Schliessen"
  81. btn.variant = "primary"
  82. except Exception:
  83. pass
  84. def show_error(self, message: str) -> None:
  85. """Zeigt Fehlermeldung."""
  86. try:
  87. status = self.query_one("#progress-status", Static)
  88. status.update(f"[red]{message}[/red]")
  89. btn = self.query_one("#progress-cancel", Button)
  90. btn.label = "Schliessen"
  91. btn.variant = "error"
  92. except Exception:
  93. pass
  94. def on_button_pressed(self, event: Button.Pressed) -> None:
  95. """Cancel/Schliessen Button."""
  96. if event.button.id == "progress-cancel":
  97. self._cancelled = True
  98. self.dismiss(False)