From e0705ad6b58f696e221ef0dc87ed2185bb33c3ce Mon Sep 17 00:00:00 2001 From: LowaSC Date: Tue, 18 Feb 2025 00:35:14 +0300 Subject: [PATCH] Add advanced TerminalWidget with enhanced text display and formatting - Implement TerminalWidget class with color-coded message types - Add support for error, warning, info, and command message styles - Include automatic scrollbar and text formatting - Replace CustomText with TerminalWidget in interactive tabs - Enhance text appending methods with type-specific display - Improve terminal readability with command separators --- ComConfigCopy.py | 138 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 119 insertions(+), 19 deletions(-) diff --git a/ComConfigCopy.py b/ComConfigCopy.py index 3ea9c58..12eb5bb 100644 --- a/ComConfigCopy.py +++ b/ComConfigCopy.py @@ -670,6 +670,86 @@ def send_command_and_process_response( logging.error(msg) return False, None +# Класс для терминального виджета с расширенной функциональностью +class TerminalWidget(CustomText): + def __init__(self, master, *args, **kwargs): + super().__init__(master, *args, **kwargs) + + # Настройка цветов для разных типов сообщений + self.tag_configure("error", foreground="red") + self.tag_configure("warning", foreground="orange") + self.tag_configure("info", foreground="blue") + self.tag_configure("command", foreground="green") + self.tag_configure("separator", foreground="gray") + + # Добавляем скроллбар + self.scrollbar = ttk.Scrollbar(self, command=self.yview) + self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) + self.config(yscrollcommand=self.scrollbar.set) + + # Настройка отступов и переносов + self.config( + wrap=tk.WORD, + padx=5, + pady=5, + spacing1=2, # Отступ перед абзацем + spacing2=2, # Межстрочный интервал + spacing3=2 # Отступ после абзаца + ) + + # Счетчик команд для разделителей + self.command_counter = 0 + + def append_text(self, text, message_type=None): + """ + Добавление текста с определенным типом сообщения + message_type может быть: 'error', 'warning', 'info', 'command' + """ + # Добавляем разделитель между командами + if message_type == "command": + if self.command_counter > 0: + self.insert(tk.END, "\n" + "─" * 80 + "\n", "separator") + self.command_counter += 1 + + # Добавляем текст + if not text.endswith('\n'): + text += '\n' + + start_index = self.index(tk.END) + self.insert(tk.END, text) + end_index = self.index(tk.END) + + # Применяем тег в зависимости от типа сообщения + if message_type: + self.tag_add(message_type, start_index, end_index) + + # Автоматическая прокрутка к концу + self.see(tk.END) + + # Обновляем виджет + self.update_idletasks() + + def append_error(self, text): + """Добавление сообщения об ошибке""" + self.append_text(text, "error") + + def append_warning(self, text): + """Добавление предупреждения""" + self.append_text(text, "warning") + + def append_info(self, text): + """Добавление информационного сообщения""" + self.append_text(text, "info") + + def append_command(self, text): + """Добавление команды""" + self.append_text(text, "command") + + def clear(self): + """Очистка терминала""" + self.delete("1.0", tk.END) + self.command_counter = 0 + # Основной класс для графического интерфейса class SerialAppGUI(tk.Tk): def __init__(self, settings): @@ -849,7 +929,8 @@ class SerialAppGUI(tk.Tk): ) disconnect_btn.pack(side=LEFT, padx=5) - self.interactive_text = CustomText(frame, wrap="word", height=20) + # Используем новый TerminalWidget вместо CustomText + self.interactive_text = TerminalWidget(frame, height=20) self.interactive_text.pack(fill=BOTH, expand=True, padx=5, pady=5) input_frame = ttk.Frame(frame) @@ -876,10 +957,10 @@ class SerialAppGUI(tk.Tk): return self.connection = create_connection(self.settings) if self.connection: - self.append_interactive_text("[INFO] Подключение установлено.\n") + self.interactive_text.append_info("[INFO] Подключение установлено.") self.update_status_bar() # Обновляем статус бар else: - self.append_interactive_text("[ERROR] Не удалось установить соединение.\n") + self.interactive_text.append_error("[ERROR] Не удалось установить соединение.") # Отключение от устройства def disconnect_device(self): @@ -889,7 +970,7 @@ class SerialAppGUI(tk.Tk): except Exception: pass self.connection = None - self.append_interactive_text("[INFO] Соединение закрыто.\n") + self.interactive_text.append_info("[INFO] Соединение закрыто.") self.update_status_bar() # Обновляем статус бар else: messagebox.showinfo("Информация", "Соединение не установлено.") @@ -902,7 +983,7 @@ class SerialAppGUI(tk.Tk): cmd = self.command_entry.get().strip() if not cmd: return - self.append_interactive_text(f"[INFO] Отправка команды: {cmd}\n") + self.interactive_text.append_command(f"[CMD] {cmd}") threading.Thread(target=self.process_command, args=(cmd,), daemon=True).start() # Обработка команды @@ -919,15 +1000,22 @@ class SerialAppGUI(tk.Tk): is_gui=True ) except SerialException as e: - self.append_interactive_text(f"[ERROR] Ошибка при отправке команды: {e}\n") + self.interactive_text.append_error(f"[ERROR] Ошибка при отправке команды: {e}") logging.error(f"Ошибка отправки команды: {e}", exc_info=True) except Exception as e: - self.append_interactive_text(f"[ERROR] Неизвестная ошибка: {e}\n") + self.interactive_text.append_error(f"[ERROR] Неизвестная ошибка: {e}") logging.error(f"Неизвестная ошибка: {e}", exc_info=True) # Добавление текста в текстовое поле def append_interactive_text(self, text): - append_text_to_widget(self.interactive_text, text) + if "[ERROR]" in text: + self.interactive_text.append_error(text) + elif "[WARNING]" in text or "[WARN]" in text: + self.interactive_text.append_warning(text) + elif "[INFO]" in text: + self.interactive_text.append_info(text) + else: + self.interactive_text.append_text(text) # Создание вкладки "Выполнить команды из файла" def create_file_exec_tab(self, frame): @@ -938,9 +1026,21 @@ class SerialAppGUI(tk.Tk): CustomEntry(file_frame, textvariable=self.file_exec_var, width=40).pack(side=LEFT, padx=5) ttk.Button(file_frame, text="Выбрать", command=self.select_config_file_fileexec).pack(side=LEFT, padx=5) ttk.Button(frame, text="Выполнить команды", command=self.execute_file_commands).pack(pady=5) - self.file_exec_text = CustomText(frame, wrap="word", height=15) + + # Используем новый TerminalWidget вместо CustomText + self.file_exec_text = TerminalWidget(frame, height=15) self.file_exec_text.pack(fill=BOTH, expand=True, padx=5, pady=5) + def append_file_exec_text(self, text): + if "[ERROR]" in text: + self.file_exec_text.append_error(text) + elif "[WARNING]" in text or "[WARN]" in text: + self.file_exec_text.append_warning(text) + elif "[INFO]" in text: + self.file_exec_text.append_info(text) + else: + self.file_exec_text.append_text(text) + # Выбор файла конфигурации для выполнения команд def select_config_file_fileexec(self): select_config_file(self, self.file_exec_var) @@ -974,9 +1074,6 @@ class SerialAppGUI(tk.Tk): daemon=True, ).start() - def append_file_exec_text(self, text): - append_text_to_widget(self.file_exec_text, text) - # Создание вкладки "Редактор конфигурационного файла" def create_config_editor_tab(self, frame): top_frame = ttk.Frame(frame) @@ -1084,13 +1181,9 @@ class SerialAppGUI(tk.Tk): log_frame = ttk.LabelFrame(tftp_frame, text="Лог сервера") log_frame.pack(fill=BOTH, expand=True, padx=5, pady=5) - self.tftp_log_text = CustomText(log_frame, wrap=tk.WORD, height=10) + # Используем новый TerminalWidget вместо CustomText + self.tftp_log_text = TerminalWidget(log_frame, height=10) self.tftp_log_text.pack(fill=BOTH, expand=True, padx=5, pady=5) - - # Добавляем скроллбар для лога - scrollbar = ttk.Scrollbar(self.tftp_log_text, command=self.tftp_log_text.yview) - scrollbar.pack(side=tk.RIGHT, fill=tk.Y) - self.tftp_log_text.config(yscrollcommand=scrollbar.set) # Статус передач transfers_frame = ttk.LabelFrame(tftp_frame, text="Активные передачи") @@ -1242,7 +1335,14 @@ class SerialAppGUI(tk.Tk): self.stop_tftp_button.config(state="normal") def append_tftp_log(self, text): - append_text_to_widget(self.tftp_log_text, text) + if "[ERROR]" in text: + self.tftp_log_text.append_error(text) + elif "[WARNING]" in text or "[WARN]" in text: + self.tftp_log_text.append_warning(text) + elif "[INFO]" in text: + self.tftp_log_text.append_info(text) + else: + self.tftp_log_text.append_text(text) # Обновление информации об активных передачах def update_transfers_info(self):