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
This commit is contained in:
2025-02-18 00:35:14 +03:00
parent c0d8fd8d89
commit e0705ad6b5

View File

@@ -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):