diff --git a/.gitmodules b/.gitmodules index 98f8bb5..b10e70e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "report"] path = report url = https://git.mount-mockery.de/pylib/report.git +[submodule "mytui"] + path = mytui + url = https://git.mount-mockery.de/pylib/mytui.git diff --git a/loggy.py b/loggy.py index 1823edb..615152a 100644 --- a/loggy.py +++ b/loggy.py @@ -10,6 +10,8 @@ from textual.app import App, ComposeResult from textual.containers import Vertical from textual.widgets import Footer, Header, Input, RichLog, Checkbox, Select +from mytui import MultiSelect + # Mapping von Log-Level-Namen zu Farben für die Anzeige LEVEL_STYLES = { "CRITICAL": "bold white on red", @@ -56,73 +58,6 @@ class LogReceiver(threading.Thread): logging.error(f"LogReceiver-Fehler: {e}", exc_info=True) -class OptionSelectList(dict): - def __init__(self): - super().__init__(self) - # - self.__default_value__ = True - self.__selection_regex__ = "" - - def __sorted_keys__(self): - rv = list(self.keys()) - rv.sort() - return rv - - def SetSelectionRegEx(self, regex: str) -> None: - self.__selection_regex__ = regex - - def AddEntry(self, entry) -> None: - if entry not in self: - self[entry] = self.__default_value__ - - def Toggle(self, entry_num): - if entry_num < 0: - if not self.__selection_regex__: - self.__default_value__ = entry_num == -1 - for key in self: - if self.__match__(key): - self[key] = entry_num == -1 - elif entry_num > len(self): - raise ValueError(f"The Entry '{entry} is not in the list") - else: - entry = self.__sorted_keys__()[entry_num] - self[entry] = not self[entry] - - def __match__(self, key): - try: - match = len(re.findall(self.__selection_regex__, key)) > 0 - except re.error: - match = True # No valid regular expression - return match - - def GetSelectList(self): - rv = [] - if len(self) > 2: - rv.append(('All', -1)) - rv.append(('None', -2)) - for index, key in enumerate(self.__sorted_keys__()): - if self.__match__(key): - prefix = "\\[X] " if self[key] else "\\[-] " - rv.append((prefix + key, index)) - return rv - - def IsSelected(self, entry): - return self.get(entry, False) - - -class MySelect(Select): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.__new_options__ = [] - - def set_options(self, options): - self.__new_options__ = options - - def _on_enter(self, event): - super().set_options(self.__new_options__) - return super()._on_enter(event) - - class LogViewerApp(App): """Eine Textual-App zum Anzeigen und Filtern von Python-Logs.""" @@ -137,10 +72,8 @@ class LogViewerApp(App): super().__init__() # self.all_logs = [] - self.__module_select_list__ = OptionSelectList() - self.__module_selection__ = MySelect((), prompt="Module", id="module_filter") - self.__level_select_list__ = OptionSelectList() - self.__level_selection__ = MySelect((), prompt="Level", id="level_filter") + self.__module_selection__ = MultiSelect((), prompt="Module", id="module_filter") + self.__level_selection__ = MultiSelect((), prompt="Level", id="level_filter") self.force_critical = True self.force_error = True self.force_warning = False @@ -167,10 +100,8 @@ class LogViewerApp(App): def add_log(self, record: logging.LogRecord) -> None: """Fügt einen neuen Log-Eintrag hinzu und aktualisiert die Anzeige.""" - self.__module_select_list__.AddEntry(record.name) - self.__module_selection__.set_options(self.__module_select_list__.GetSelectList()) - self.__level_select_list__.AddEntry(record.levelname) - self.__level_selection__.set_options(self.__level_select_list__.GetSelectList()) + self.__module_selection__.AddEntry(record.name) + self.__level_selection__.AddEntry(record.levelname) self.all_logs.append(record) if len(self.all_logs) > self.MAX_LOGS: self.all_logs = self.all_logs[-self.MAX_LOGS:] @@ -185,8 +116,8 @@ class LogViewerApp(App): def _apply_filters_to_log(self, record: logging.LogRecord): """Prüft einen einzelnen Log-Eintrag gegen die Filter und zeigt ihn ggf. an.""" - module_match = self.__module_select_list__.IsSelected(record.name) - level_match = self.__level_select_list__.IsSelected(record.levelname) + module_match = self.__module_selection__.IsSelected(record.name) + level_match = self.__level_selection__.IsSelected(record.levelname) if self._force(record.levelname.lower()) or (module_match and level_match): level_style = LEVEL_STYLES.get(record.levelname, "white") @@ -226,23 +157,11 @@ class LogViewerApp(App): def on_input_changed(self, message: Input.Changed) -> None: """Update the tui inputs and execute task, if requireed.""" if message.input.id == "select_filter": - self.__module_select_list__.SetSelectionRegEx(message.value) - self.__module_selection__.set_options(self.__module_select_list__.GetSelectList()) - self.__module_selection__.prompt = "Full" if not message.value else "Filtered" + self.__module_selection__.SetSelectionRegEx(message.value) def on_select_changed(self, message: Select.Changed) -> None: - if message.select.id == 'module_filter': - if type(message.value) is int: - self.__module_select_list__.Toggle(message.value) - self.__module_selection__.clear() - self.__module_selection__.set_options(self.__module_select_list__.GetSelectList()) - elif message.select.id == 'level_filter': - if type(message.value) is int: - self.__level_select_list__.Toggle(message.value) - self.__level_selection__.clear() - self.__level_selection__.set_options(self.__level_select_list__.GetSelectList()) - - self._update_display() + if message.select.id in ('module_filter', 'level_filter'): + self._update_display() if __name__ == "__main__": diff --git a/mytui b/mytui new file mode 160000 index 0000000..bd390d2 --- /dev/null +++ b/mytui @@ -0,0 +1 @@ +Subproject commit bd390d27f00147c1305e244fbd6d0650a6bdcec9