Files
test-loderweb/ui.py
unknown be7aadf218 new
2026-02-09 14:28:39 +05:00

407 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Интерфейс (UI) для приложения автоматизации загрузки файлов
"""
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import threading
import logging
from pathlib import Path
from services.processor import FileProcessorService
from services.browser_service import BrowserService
class UIManager:
"""Менеджер интерфейса приложения"""
def __init__(self, root):
"""Инициализирует интерфейс."""
self.root = root
self.root.title("🔄 Автоматизация загрузки файлов")
self.root.geometry("700x800")
self.root.configure(bg="#2C3E50")
self.root.minsize(500, 400)
self.font_title = ("Arial", 14, "bold")
self.font_label = ("Arial", 11)
self.font_button = ("Arial", 10, "bold")
self.processor = None
self.processing = False
self._setup_logging()
self._create_ui()
def _setup_logging(self):
"""Настраивает логирование в текстовое поле."""
# Обычное логирование в консоль
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
def _create_ui(self):
"""Создает элементы интерфейса."""
# Главный фрейм
main_frame = tk.Frame(self.root, bg="#2C3E50")
main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
# Заголовок
title_label = tk.Label(
main_frame,
text="📁 Автоматизация загрузки файлов XLSX",
font=self.font_title,
bg="#2C3E50",
fg="#ECF0F1"
)
title_label.pack(pady=(0, 20))
# ===== ПОЛЕ 1: ДИРЕКТОРИЯ С ФАЙЛАМИ =====
dir_label = tk.Label(
main_frame,
text="📂 Директория с файлами .xlsx:",
font=self.font_label,
bg="#2C3E50",
fg="#ECF0F1"
)
dir_label.pack(anchor="w", pady=(10, 5))
dir_frame = tk.Frame(main_frame, bg="#2C3E50")
dir_frame.pack(fill=tk.X, pady=(0, 15))
self.dir_entry = tk.Entry(dir_frame, font=self.font_label, width=60)
self.dir_entry.pack(side="left", fill=tk.X, expand=True)
self.dir_entry.bind("<Button-1>", lambda e: self._select_directory())
dir_button = tk.Button(
dir_frame,
text="🗂️ Выбрать",
command=self._select_directory,
font=self.font_button,
bg="#3498DB",
fg="white",
padx=10
)
dir_button.pack(side="left", padx=(10, 0))
# ===== ПОЛЕ 2: URL СТРАНИЦЫ =====
url_label = tk.Label(
main_frame,
text="🌐 URL страницы для загрузки:",
font=self.font_label,
bg="#2C3E50",
fg="#ECF0F1"
)
url_label.pack(anchor="w", pady=(10, 5))
# Кнопка Вставить рядом с полем URL
url_frame = tk.Frame(main_frame, bg="#2C3E50")
url_frame.pack(fill=tk.X, pady=(0, 15))
self.url_entry = tk.Entry(url_frame, font=self.font_label)
self.url_entry.pack(side="left", fill=tk.X, expand=True)
insert_button = tk.Button(
url_frame,
text="Вставить",
command=self._insert_from_clipboard,
font=self.font_button,
bg="#27AE60",
fg="white",
padx=10
)
insert_button.pack(side="left", padx=(10, 0))
# ===== ПОЛЕ 3: ВАРИАНТЫ КНОПОК =====
buttons_label = tk.Label(
main_frame,
text="🔘 Варианты текста кнопок (через запятую):",
font=self.font_label,
bg="#2C3E50",
fg="#ECF0F1"
)
buttons_label.pack(anchor="w", pady=(10, 5))
self.upload_buttons_entry = tk.Entry(main_frame, font=self.font_label)
self.upload_buttons_entry.pack(anchor="w", fill=tk.X, pady=(0, 5))
self.upload_buttons_entry.insert(0, "Загрузить файл, Выбрать файл, Добавить файл")
upload_label = tk.Label(
main_frame,
text="(Кнопка для загрузки файла)",
font=("Arial", 9),
bg="#2C3E50",
fg="#BDC3C7"
)
upload_label.pack(anchor="w", pady=(0, 10))
self.process_buttons_entry = tk.Entry(main_frame, font=self.font_label)
self.process_buttons_entry.pack(anchor="w", fill=tk.X, pady=(0, 5))
self.process_buttons_entry.insert(0, "Загрузить, Обработать, Преобразовать, Изменить, Конвертировать")
process_label = tk.Label(
main_frame,
text="(Кнопка для обработки/загрузки файла)",
font=("Arial", 9),
bg="#2C3E50",
fg="#BDC3C7"
)
process_label.pack(anchor="w", pady=(0, 15))
# ===== ЛОГИРОВАНИЕ =====
log_label = tk.Label(
main_frame,
text="📋 Логирование процесса:",
font=self.font_label,
bg="#2C3E50",
fg="#ECF0F1"
)
log_label.pack(anchor="w", pady=(10, 5))
# Frame для логов с прокруткой
log_frame = tk.Frame(main_frame, bg="#34495E")
log_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 15))
scrollbar = tk.Scrollbar(log_frame)
scrollbar.pack(side="right", fill="y")
self.log_text = tk.Text(
log_frame,
font=("Courier", 9),
bg="#1C2833",
fg="#82E0AA",
height=10,
yscrollcommand=scrollbar.set
)
self.log_text.pack(fill=tk.BOTH, expand=True)
scrollbar.config(command=self.log_text.yview)
# ===== КНОПКИ =====
button_frame = tk.Frame(main_frame, bg="#2C3E50")
button_frame.pack(fill=tk.X)
self.start_button = tk.Button(
button_frame,
text="🚀 Начать загрузку",
command=self._start_processing,
font=self.font_button,
bg="#27AE60",
fg="white",
padx=20,
pady=12
)
self.start_button.pack(side="right", padx=(10, 0))
self.clear_log_button = tk.Button(
button_frame,
text="🗑️ Очистить логи",
command=self._clear_logs,
font=self.font_button,
bg="#E74C3C",
fg="white",
padx=15,
pady=12
)
self.clear_log_button.pack(side="right")
# Перенаправление логов в текстовое поле
self._setup_log_handler()
# ===== ВЫБОР ВКЛАДКИ =====
tabs_label = tk.Label(
main_frame,
text="📑 Выбор вкладки браузера:",
font=self.font_label,
bg="#2C3E50",
fg="#ECF0F1"
)
tabs_label.pack(anchor="w", pady=(10, 5))
self.tab_selector = TabSelectorApp(main_frame, BrowserService(download_dir="./downloads"))
self.tab_selector.pack(fill=tk.X, pady=(0, 15))
def _setup_log_handler(self):
"""Настраивает обработчик логов в текстовое поле."""
# Создаем кастомный обработчик для логирования в UI
class TextHandler(logging.Handler):
def __init__(self, text_widget):
super().__init__()
self.text_widget = text_widget
def emit(self, record):
msg = self.format(record)
self.text_widget.insert(tk.END, msg + "\n")
self.text_widget.see(tk.END)
self.text_widget.update()
handler = TextHandler(self.log_text)
handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', datefmt='%H:%M:%S'))
logging.getLogger().addHandler(handler)
def _select_directory(self):
"""Открывает диалог выбора директории."""
directory = filedialog.askdirectory(title="Выберите директорию с .xlsx файлами")
if directory:
self.dir_entry.delete(0, tk.END)
self.dir_entry.insert(0, directory)
logging.info(f"✅ Выбрана директория: {directory}")
def _clear_logs(self):
"""Очищает логи."""
self.log_text.delete(1.0, tk.END)
logging.info("🗑️ Логи очищены")
def _start_processing(self):
"""Запускает процесс обработки в отдельном потоке."""
if self.processing:
messagebox.showwarning("⚠️ Внимание", "Процесс уже запущен!")
return
# Валидация входных данных
directory = self.dir_entry.get().strip()
url = self.url_entry.get().strip()
if not directory:
messagebox.showerror("❌ Ошибка", "Выберите директорию с файлами!")
return
if not Path(directory).exists():
messagebox.showerror("❌ Ошибка", "Указанная директория не существует!")
return
if not url or url == "https://":
messagebox.showerror("❌ Ошибка", "Введите URL страницы!")
return
# Парсим варианты кнопок
upload_texts = [t.strip() for t in self.upload_buttons_entry.get().split(",")]
process_texts = [t.strip() for t in self.process_buttons_entry.get().split(",")]
# Отключаем кнопку и запускаем обработку в потоке
self.processing = True
self.start_button.config(state="disabled", bg="#95A5A6")
thread = threading.Thread(
target=self._processing_thread,
args=(directory, url, upload_texts, process_texts),
daemon=True
)
thread.start()
def _processing_thread(self, directory, url, upload_texts, process_texts):
"""Потоковая функция обработки."""
try:
logging.info("\n" + "="*70)
logging.info("🔄 ЗАПУСК АВТОМАТИЗАЦИИ ЗАГРУЗКИ ФАЙЛОВ")
logging.info("="*70)
# Получаем путь для загрузок (рядом с программой)
download_dir = str(Path(__file__).parent / "downloads")
Path(download_dir).mkdir(exist_ok=True)
# Создаем процессор
processor = FileProcessorService(directory, url, download_dir)
# Проверка вкладок перед началом загрузки
browser_service = BrowserService(download_dir="./downloads")
browser_service.set_tabs(["Вкладка 1", "Вкладка 2", "Вкладка 3"])
# Пример: выбор вкладки по индексу
if not browser_service.select_tab_from_list(0):
logging.error("Не удалось выбрать вкладку. Проверьте массив вкладок.")
return
# Запускаем обработку
results = processor.start_processing(upload_texts, process_texts)
# Показываем результаты
self._show_results(results)
except Exception as e:
logging.error(f"❌ Критическая ошибка: {e}")
messagebox.showerror("❌ Ошибка", f"Произошла ошибка:\n{str(e)}")
finally:
self.processing = False
self.start_button.config(state="normal", bg="#27AE60")
def _show_results(self, results):
"""Показывает результаты обработки."""
result_text = (
f"✅ ОБРАБОТКА ЗАВЕРШЕНА\n\n"
f"📊 Результаты:\n"
f"Всего файлов найдено: {results['total_files']}\n"
f"• Обработано успешно: {results['processed_files']}\n"
f"• Ошибок: {len(results['failed_files'])}\n"
)
if results['failed_files']:
result_text += f"\n❌ Файлы с ошибками:\n"
for file in results['failed_files'][:5]:
result_text += f"{file}\n"
if len(results['failed_files']) > 5:
result_text += f" ... и еще {len(results['failed_files']) - 5}"
if results['errors']:
result_text += f"\n⚠️ Системные ошибки:\n"
for error in results['errors']:
result_text += f"{error}\n"
messagebox.showinfo("Результаты", result_text)
def _insert_from_clipboard(self):
"""Обработчик для вставки текста из буфера обмена."""
try:
clipboard_text = self.root.clipboard_get()
self.url_entry.delete(0, tk.END) # Очистить поле
self.url_entry.insert(0, clipboard_text) # Вставить текст из буфера
except tk.TclError:
messagebox.showerror("Ошибка", "Буфер обмена пуст или недоступен.")
class TabSelectorApp:
def __init__(self, parent, browser_service):
self.browser_service = browser_service
self.frame = tk.Frame(parent, bg="#2C3E50")
# Выпадающий список
self.combo = ttk.Combobox(self.frame, state="readonly")
self.combo.pack(pady=10)
# Кнопка для выбора вкладки
self.select_button = tk.Button(self.frame, text="Выбрать вкладку", command=self.select_tab)
self.select_button.pack(pady=10)
def pack(self, **kwargs):
"""Упрощает упаковку фрейма."""
self.frame.pack(**kwargs)
def set_tabs(self, tabs):
"""Устанавливает массив вкладок и обновляет выпадающий список."""
self.tabs = tabs
self.combo["values"] = self.tabs
if self.tabs:
self.combo.current(0) # Устанавливаем первый элемент по умолчанию
def select_tab(self):
"""Выбирает вкладку из выпадающего списка."""
selected_index = self.combo.current()
if selected_index == -1:
print("❌ Вкладка не выбрана.")
return
success = self.browser_service.select_tab_from_list(selected_index)
if success:
print(f"✅ Вкладка '{self.tabs[selected_index]}' выбрана.")
else:
print(f"Не удалось выбрать вкладку '{self.tabs[selected_index]}'." )
def main():
"""Главная функция."""
root = tk.Tk()
app = UIManager(root)
root.mainloop()
if __name__ == "__main__":
main()