Compare commits
7 Commits
main
...
f45f1bf556
| Author | SHA1 | Date | |
|---|---|---|---|
| f45f1bf556 | |||
| e6766660c6 | |||
| e0705ad6b5 | |||
| c0d8fd8d89 | |||
| ce81100150 | |||
| 48c9bd2d40 | |||
| e0f64060f5 |
277
ComConfigCopy.py
277
ComConfigCopy.py
@@ -38,7 +38,7 @@ import socket
|
||||
from update_checker import UpdateChecker
|
||||
|
||||
# Версия программы
|
||||
VERSION = "1.0.1"
|
||||
VERSION = "1.0.2"
|
||||
|
||||
# Создаем необходимые папки
|
||||
os.makedirs("Logs", exist_ok=True)
|
||||
@@ -286,7 +286,7 @@ def execute_commands_from_file(
|
||||
max_attempts = 3
|
||||
attempt = 0
|
||||
while attempt < max_attempts:
|
||||
msg = f"\nОтправка команды: {cmd} (Попытка {attempt+1} из {max_attempts})\n"
|
||||
msg = f"[CMD] {cmd}" # Изменено форматирование для команды
|
||||
if log_callback:
|
||||
log_callback(msg)
|
||||
serial_connection.write((cmd + "\n").encode())
|
||||
@@ -327,7 +327,7 @@ def execute_commands_from_file(
|
||||
elif copy_mode == "block":
|
||||
blocks = generate_command_blocks(lines, block_size)
|
||||
for block in blocks:
|
||||
msg = f"\nОтправка блока команд:\n{block}\n"
|
||||
msg = f"[CMD] Отправка блока команд:\n{block}" # Изменено форматирование для блока команд
|
||||
if log_callback:
|
||||
log_callback(msg)
|
||||
serial_connection.write((block + "\n").encode())
|
||||
@@ -350,7 +350,7 @@ def execute_commands_from_file(
|
||||
max_attempts = 3
|
||||
attempt = 0
|
||||
while attempt < max_attempts:
|
||||
sub_msg = f"\nОтправка команды: {cmd} (Попытка {attempt+1} из {max_attempts})\n"
|
||||
sub_msg = f"[CMD] {cmd}" # Изменено форматирование для команды
|
||||
if log_callback:
|
||||
log_callback(sub_msg)
|
||||
serial_connection.write((cmd + "\n").encode())
|
||||
@@ -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 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):
|
||||
"""Добавление команды с разделителем"""
|
||||
# Добавляем разделитель между командами
|
||||
if self.command_counter > 0:
|
||||
self.insert(tk.END, "\n" + "─" * 80 + "\n", "separator")
|
||||
self.command_counter += 1
|
||||
|
||||
# Добавляем команду
|
||||
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):
|
||||
@@ -700,6 +780,12 @@ class SerialAppGUI(tk.Tk):
|
||||
self.create_menu()
|
||||
self.create_tabs()
|
||||
|
||||
# Добавляем статус бар
|
||||
self.create_status_bar()
|
||||
|
||||
# Обновляем информацию в статус баре
|
||||
self.update_status_bar()
|
||||
|
||||
# Проверка первого запуска
|
||||
self.check_first_run()
|
||||
|
||||
@@ -763,6 +849,7 @@ class SerialAppGUI(tk.Tk):
|
||||
# Обработчик изменения настроек
|
||||
def on_settings_changed(self):
|
||||
self.settings = settings_load()
|
||||
self.update_status_bar() # Добавляем обновление статус бара
|
||||
|
||||
# Открытие окна настроек
|
||||
def open_settings(self):
|
||||
@@ -825,10 +912,25 @@ class SerialAppGUI(tk.Tk):
|
||||
def create_interactive_tab(self, frame):
|
||||
control_frame = ttk.Frame(frame)
|
||||
control_frame.pack(fill=X, pady=5)
|
||||
ttk.Button(control_frame, text="Подключиться", command=self.connect_device).pack(side=LEFT, padx=5)
|
||||
ttk.Button(control_frame, text="Отключиться", command=self.disconnect_device).pack(side=LEFT, padx=5)
|
||||
|
||||
# Кнопка подключения с иконкой
|
||||
connect_btn = ttk.Button(
|
||||
control_frame,
|
||||
text="⚡ Подключиться", # Unicode символ для "молнии"
|
||||
command=self.connect_device
|
||||
)
|
||||
connect_btn.pack(side=LEFT, padx=5)
|
||||
|
||||
# Кнопка отключения с иконкой
|
||||
disconnect_btn = ttk.Button(
|
||||
control_frame,
|
||||
text="✕ Отключиться", # Unicode символ для "крестика"
|
||||
command=self.disconnect_device
|
||||
)
|
||||
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)
|
||||
@@ -836,7 +938,17 @@ class SerialAppGUI(tk.Tk):
|
||||
ttk.Label(input_frame, text="Команда:").pack(side=LEFT, padx=5)
|
||||
self.command_entry = CustomEntry(input_frame, width=50)
|
||||
self.command_entry.pack(side=LEFT, padx=5)
|
||||
ttk.Button(input_frame, text="Отправить", command=self.send_command).pack(side=LEFT, padx=5)
|
||||
|
||||
# Привязываем обработчик нажатия Enter к полю ввода
|
||||
self.command_entry.bind('<Return>', lambda event: self.send_command())
|
||||
|
||||
# Кнопка отправки с иконкой
|
||||
send_btn = ttk.Button(
|
||||
input_frame,
|
||||
text="➤ Отправить", # Unicode символ для "стрелки"
|
||||
command=self.send_command
|
||||
)
|
||||
send_btn.pack(side=LEFT, padx=5)
|
||||
|
||||
# Подключение к устройству
|
||||
def connect_device(self):
|
||||
@@ -848,9 +960,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):
|
||||
@@ -860,7 +973,8 @@ 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("Информация", "Соединение не установлено.")
|
||||
|
||||
@@ -872,7 +986,8 @@ 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}")
|
||||
self.command_entry.delete(0, END) # Очищаем поле ввода
|
||||
threading.Thread(target=self.process_command, args=(cmd,), daemon=True).start()
|
||||
|
||||
# Обработка команды
|
||||
@@ -889,15 +1004,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):
|
||||
@@ -908,9 +1030,23 @@ 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)
|
||||
elif "[CMD]" in text: # Добавляем обработку команд
|
||||
self.file_exec_text.append_command(text)
|
||||
else:
|
||||
self.file_exec_text.append_text(text)
|
||||
|
||||
# Выбор файла конфигурации для выполнения команд
|
||||
def select_config_file_fileexec(self):
|
||||
select_config_file(self, self.file_exec_var)
|
||||
@@ -944,9 +1080,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)
|
||||
@@ -1054,13 +1187,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="Активные передачи")
|
||||
@@ -1149,6 +1278,9 @@ class SerialAppGUI(tk.Tk):
|
||||
# Запускаем периодическое обновление информации о передачах
|
||||
self.update_transfers_periodically()
|
||||
|
||||
# Обновляем статус бар после запуска сервера
|
||||
self.update_status_bar()
|
||||
|
||||
except Exception as e:
|
||||
self.append_tftp_log(f"[ERROR] Ошибка запуска сервера: {str(e)}")
|
||||
messagebox.showerror("Ошибка", f"Не удалось запустить TFTP сервер: {str(e)}")
|
||||
@@ -1197,6 +1329,9 @@ class SerialAppGUI(tk.Tk):
|
||||
for item in self.transfers_tree.get_children():
|
||||
self.transfers_tree.delete(item)
|
||||
|
||||
# Обновляем статус бар после остановки сервера
|
||||
self.update_status_bar()
|
||||
|
||||
except Exception as e:
|
||||
self.append_tftp_log(f"[ERROR] Ошибка остановки сервера: {str(e)}")
|
||||
messagebox.showerror("Ошибка", f"Не удалось остановить TFTP сервер: {str(e)}")
|
||||
@@ -1206,7 +1341,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):
|
||||
@@ -1264,6 +1406,89 @@ class SerialAppGUI(tk.Tk):
|
||||
if not self.tftp_ip_var.get() in adapters:
|
||||
self.tftp_ip_var.set(adapters[0])
|
||||
|
||||
# Добавляем новый метод для создания статус бара
|
||||
def create_status_bar(self):
|
||||
self.status_bar = ttk.Frame(self)
|
||||
self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
|
||||
|
||||
# Создаем фрейм для индикаторов
|
||||
indicators_frame = ttk.Frame(self.status_bar)
|
||||
indicators_frame.pack(side=tk.LEFT)
|
||||
|
||||
# Индикатор подключения к коммутатору
|
||||
self.connection_indicator_frame = ttk.Frame(indicators_frame)
|
||||
self.connection_indicator_frame.pack(side=tk.LEFT)
|
||||
|
||||
ttk.Label(self.connection_indicator_frame, text="COM:", padding=(5, 2)).pack(side=tk.LEFT)
|
||||
self.connection_indicator = ttk.Label(
|
||||
self.connection_indicator_frame,
|
||||
text="⬤",
|
||||
font=("Segoe UI", 10),
|
||||
width=2,
|
||||
anchor='center',
|
||||
padding=(2, 2)
|
||||
)
|
||||
self.connection_indicator.pack(side=tk.LEFT)
|
||||
|
||||
# Индикатор TFTP сервера
|
||||
self.tftp_indicator_frame = ttk.Frame(indicators_frame)
|
||||
self.tftp_indicator_frame.pack(side=tk.LEFT, padx=10)
|
||||
|
||||
ttk.Label(self.tftp_indicator_frame, text="TFTP:", padding=(5, 2)).pack(side=tk.LEFT)
|
||||
self.tftp_indicator = ttk.Label(
|
||||
self.tftp_indicator_frame,
|
||||
text="⬤",
|
||||
font=("Segoe UI", 10),
|
||||
width=2,
|
||||
anchor='center',
|
||||
padding=(2, 2)
|
||||
)
|
||||
self.tftp_indicator.pack(side=tk.LEFT)
|
||||
|
||||
ttk.Separator(self.status_bar, orient=tk.VERTICAL).pack(side=tk.LEFT, fill=tk.Y, padx=5)
|
||||
|
||||
# Остальные элементы статус бара...
|
||||
self.port_label = ttk.Label(self.status_bar, text="", padding=(5, 2))
|
||||
self.port_label.pack(side=tk.LEFT)
|
||||
|
||||
ttk.Separator(self.status_bar, orient=tk.VERTICAL).pack(side=tk.LEFT, fill=tk.Y, padx=5)
|
||||
|
||||
self.baudrate_label = ttk.Label(self.status_bar, text="", padding=(5, 2))
|
||||
self.baudrate_label.pack(side=tk.LEFT)
|
||||
|
||||
ttk.Separator(self.status_bar, orient=tk.VERTICAL).pack(side=tk.LEFT, fill=tk.Y, padx=5)
|
||||
|
||||
self.copy_mode_label = ttk.Label(self.status_bar, text="", padding=(5, 2))
|
||||
self.copy_mode_label.pack(side=tk.LEFT)
|
||||
|
||||
ttk.Separator(self.status_bar, orient=tk.VERTICAL).pack(side=tk.LEFT, fill=tk.Y, padx=5)
|
||||
|
||||
self.version_label = ttk.Label(self.status_bar, text=f"Версия: {VERSION}", padding=(5, 2))
|
||||
self.version_label.pack(side=tk.RIGHT)
|
||||
|
||||
# Обновляем метод update_status_bar
|
||||
def update_status_bar(self):
|
||||
# Обновляем индикатор подключения COM порта
|
||||
if self.connection:
|
||||
self.connection_indicator.configure(text="⬤", foreground='green')
|
||||
else:
|
||||
self.connection_indicator.configure(text="⬤", foreground='red')
|
||||
|
||||
# Обновляем индикатор TFTP сервера
|
||||
if self.tftp_server and self.tftp_server.running:
|
||||
self.tftp_indicator.configure(text="⬤", foreground='green')
|
||||
else:
|
||||
self.tftp_indicator.configure(text="⬤", foreground='red')
|
||||
|
||||
# Остальные обновления статус бара
|
||||
port = self.settings.get("port", "Не выбран")
|
||||
baudrate = self.settings.get("baudrate", "9600")
|
||||
copy_mode = "Построчный" if self.settings.get("copy_mode") == "line" else "Блочный"
|
||||
|
||||
self.port_label.config(text=f"Порт: {port}")
|
||||
self.baudrate_label.config(text=f"Скорость: {baudrate}")
|
||||
self.copy_mode_label.config(text=f"Режим: {copy_mode}")
|
||||
|
||||
# ==========================
|
||||
# Основной запуск приложения
|
||||
# ==========================
|
||||
|
||||
Reference in New Issue
Block a user