# modules/electronics_module.py import tkinter as tk from tkinter import ttk, messagebox from pathlib import Path import sys import os from PIL import Image, ImageTk # Добавляем родительскую директорию в путь для импорта sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) try: # Пытаемся импортировать из config.py import config DOCUMENTS_PATH = config.DOCUMENTS_PATH APP_STYLE = config.APP_STYLE except ImportError: # Локальные значения по умолчанию DOCUMENTS_PATH = Path.home() / "Documents" / "C4" APP_STYLE = { 'title_font': ("Segoe UI", 12, "bold"), 'entry_font': ("Consolas", 14), 'label_font': ("Segoe UI", 10), 'padding': {'padx': 20, 'pady': 10} } # Путь к файлам конфигурации электроники ELECTRO_FILE = DOCUMENTS_PATH / "elektro_version.rr" HOPPER_SENSOR_FILE = DOCUMENTS_PATH / "HopperSensor.rr" class ElectronicsModuleTab: """ Класс для создания вкладки конфигурации подключения электроники. """ def __init__(self, parent_frame): """ Инициализация компонента вкладки. Args: parent_frame: Родительский фрейм, куда будет помещен интерфейс """ self.parent = parent_frame self.current_version = None self.current_sensor_type = None self.image_cache = {} # Кэш загруженных изображений # Создаем основной контейнер для вкладки self.container = ttk.Frame(parent_frame, padding=20) self.container.pack(fill=tk.BOTH, expand=True) self._build_ui() # Загружаем сохраненные настройки при запуске self.load_saved_settings() def _build_ui(self): """Построение интерфейса вкладки""" # Заголовок вкладки title_frame = ttk.Frame(self.container) title_frame.pack(fill=tk.X, pady=(0, 20)) title_label = ttk.Label( title_frame, text='Конфигурация подключения электроники', font=APP_STYLE['title_font'] ) title_label.pack() # Разделитель ttk.Separator(self.container, orient="horizontal").pack( fill=tk.X, pady=(0, 20) ) # Фрейм для выбора версии электроники selector_frame = ttk.LabelFrame( self.container, text="Выбор версии электроники", padding=20 ) selector_frame.pack(fill=tk.X, pady=(0, 20)) # Описание desc_label = ttk.Label( selector_frame, text="Выберите версию электроники, установленной в аппарате:", font=APP_STYLE['label_font'], wraplength=600 ) desc_label.pack(anchor=tk.W, pady=(0, 20)) # Переменная для хранения выбранной версии self.version_var = tk.StringVar() # Фрейм для вариантов версий электроники versions_frame = ttk.Frame(selector_frame) versions_frame.pack(fill=tk.X, pady=(0, 20)) # Вариант 1 version1_frame = ttk.Frame(versions_frame) version1_frame.pack(fill=tk.X, pady=(0, 10)) version1_radio = ttk.Radiobutton( version1_frame, text="Версия 1.0 (старая)", variable=self.version_var, value="1", command=self._on_version_change ) version1_radio.pack(side=tk.LEFT, anchor=tk.W) # Кнопка просмотра схемы для версии 1 view1_btn = ttk.Button( version1_frame, text="Посмотреть схему", command=lambda: self._show_scheme("1"), width=30 ) view1_btn.pack(side=tk.LEFT, padx=(20, 0)) # Вариант 2 version2_frame = ttk.Frame(versions_frame) version2_frame.pack(fill=tk.X, pady=(0, 10)) version2_radio = ttk.Radiobutton( version2_frame, text="Версия 2.0 (новая)", variable=self.version_var, value="2", command=self._on_version_change ) version2_radio.pack(side=tk.LEFT, anchor=tk.W) # Кнопка просмотра схемы для версии 2 view2_btn = ttk.Button( version2_frame, text="Посмотреть схему", command=lambda: self._show_scheme("2"), width=30 ) view2_btn.pack(side=tk.LEFT, padx=(20, 0)) # ===== НОВЫЙ РАЗДЕЛ: ТИП ДАТЧИКА ХОППЕРА ===== # Фрейм для выбора типа датчика хоппера sensor_frame = ttk.LabelFrame( selector_frame, text="Тип датчика хоппера", padding=15 ) sensor_frame.pack(fill=tk.X, pady=(10, 20)) # Описание для датчика хоппера sensor_desc_label = ttk.Label( sensor_frame, text="Выберите тип контакта датчика хоппера:", font=APP_STYLE['label_font'] ) sensor_desc_label.pack(anchor=tk.W, pady=(0, 10)) # Переменная для хранения выбранного типа датчика self.sensor_var = tk.StringVar(value="1") # По умолчанию "Нормально разомкнутый" # Вариант 1: Нормально разомкнутый sensor1_radio = ttk.Radiobutton( sensor_frame, text="Нормально разомкнутый (по умолчанию)", variable=self.sensor_var, value="1", command=self._on_sensor_change ) sensor1_radio.pack(anchor=tk.W, pady=(0, 5)) # Вариант 2: Нормально замкнутый sensor2_radio = ttk.Radiobutton( sensor_frame, text="Нормально замкнутый", variable=self.sensor_var, value="2", command=self._on_sensor_change ) sensor2_radio.pack(anchor=tk.W) # ===== КОНЕЦ НОВОГО РАЗДЕЛА ===== # Кнопки управления button_frame = ttk.Frame(selector_frame) button_frame.pack(fill=tk.X) # Кнопка сохранения save_btn = ttk.Button( button_frame, text="Сохранить все настройки", command=self.save_all_settings, width=25 ) save_btn.pack(side=tk.LEFT, padx=(0, 10)) # Кнопка загрузки load_btn = ttk.Button( button_frame, text="Загрузить из файлов", command=self.load_saved_settings, width=20 ) load_btn.pack(side=tk.LEFT) # Информационная панель info_frame = ttk.LabelFrame( self.container, text="Информация", padding=15 ) info_frame.pack(fill=tk.X) # Создаем фрейм для информации о файлах с прокруткой info_canvas = tk.Canvas(info_frame, height=80) scrollbar = ttk.Scrollbar(info_frame, orient="vertical", command=info_canvas.yview) info_scrollable_frame = ttk.Frame(info_canvas) info_scrollable_frame.bind( "", lambda e: info_canvas.configure(scrollregion=info_canvas.bbox("all")) ) info_canvas.create_window((0, 0), window=info_scrollable_frame, anchor="nw") info_canvas.configure(yscrollcommand=scrollbar.set) info_canvas.pack(side="left", fill="both", expand=True, padx=(0, 5)) scrollbar.pack(side="right", fill="y") # Информация о файле электроники self.electro_info_label = ttk.Label( info_scrollable_frame, text="Электроника: не выбрано", font=APP_STYLE['label_font'] ) self.electro_info_label.pack(anchor=tk.W, pady=(0, 5)) # Информация о файле датчика хоппера self.sensor_info_label = ttk.Label( info_scrollable_frame, text="Датчик хоппера: не выбрано", font=APP_STYLE['label_font'] ) self.sensor_info_label.pack(anchor=tk.W, pady=(0, 5)) # Статус сохранения self.status_label = ttk.Label( info_frame, text="Статус: настройки не сохранены", font=APP_STYLE['label_font'] ) self.status_label.pack(anchor=tk.W, pady=(10, 0)) def _load_scheme_image(self, version): """Загрузить изображение схемы""" # Это заглушка - на практике здесь будет загрузка реальных изображений # Создаем тестовое изображение с текстом from PIL import Image, ImageDraw, ImageFont import io if version == "1": # Создаем изображение для схемы 1 img = Image.new('RGB', (600, 400), color='white') draw = ImageDraw.Draw(img) # Простой текст вместо изображения try: font = ImageFont.truetype("arial.ttf", 24) except: font = ImageFont.load_default() draw.text((50, 50), "Что то пошло не так", fill='black', font=font) draw.text((50, 100), "пните серегу", fill='blue', font=font) else: # version == "2" # Создаем изображение для схемы 2 img = Image.new('RGB', (600, 400), color='white') draw = ImageDraw.Draw(img) try: font = ImageFont.truetype("arial.ttf", 24) except: font = ImageFont.load_default() draw.text((50, 50), "Что то пошло не так v2", fill='black', font=font) draw.text((50, 100), "пните серегу", fill='blue', font=font) return ImageTk.PhotoImage(img) def _show_scheme(self, version): """Показать окно со схемой подключения""" scheme_window = tk.Toplevel(self.parent) if version == "1": scheme_window.title("Схема подключения - Версия 1.0") window_title = "СХЕМА ПОДКЛЮЧЕНИЯ v1.0" else: # version == "2" scheme_window.title("Схема подключения - Версия 2.0") window_title = "СХЕМА ПОДКЛЮЧЕНИЯ v2.0" # Устанавливаем размер окна scheme_window.geometry("1200x1000") scheme_window.resizable(True, True) # Создаем основной фрейм main_frame = ttk.Frame(scheme_window, padding=20) main_frame.pack(fill=tk.BOTH, expand=True) # Заголовок title_label = ttk.Label( main_frame, text=window_title, font=("Segoe UI", 16, "bold"), foreground="#2196F3" ) title_label.pack(pady=(0, 20)) # Фрейм для изображения image_frame = ttk.Frame(main_frame) image_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 20)) # Загружаем изображение try: # Пытаемся загрузить реальное изображение if version == "1": image_path = Path("schemes") / "scheme_v1.jpg" else: image_path = Path("schemes") / "scheme_v2.jpg" if image_path.exists(): # Загружаем реальное изображение pil_image = Image.open(image_path) # Масштабируем для отображения pil_image = pil_image.resize((1024, 1024), Image.LANCZOS) image = ImageTk.PhotoImage(pil_image) else: # Используем заглушку image = self._load_scheme_image(version) except Exception as e: # Если не удалось загрузить изображение, показываем заглушку print(f"Ошибка загрузки изображения: {e}") image = self._load_scheme_image(version) # Сохраняем ссылку на изображение, чтобы оно не было удалено сборщиком мусора scheme_window.image_ref = image # Отображаем изображение image_label = ttk.Label(image_frame, image=image) image_label.pack() # Центрируем окно scheme_window.update_idletasks() x = scheme_window.winfo_screenwidth() // 2 - scheme_window.winfo_width() // 2 y = scheme_window.winfo_screenheight() // 2 - scheme_window.winfo_height() // 2 scheme_window.geometry(f"+{x}+{y}") # Устанавливаем фокус на новое окно scheme_window.focus_set() scheme_window.grab_set() def _on_version_change(self): """Обработчик изменения выбранной версии электроники""" version = self.version_var.get() self.current_version = version # Обновляем информацию в интерфейсе if version == "1": self.electro_info_label.config( text="Электроника: выбрана версия 1.0", foreground="orange" ) elif version == "2": self.electro_info_label.config( text="Электроника: выбрана версия 2.0", foreground="orange" ) self._update_status() def _on_sensor_change(self): """Обработчик изменения типа датчика хоппера""" sensor_type = self.sensor_var.get() self.current_sensor_type = sensor_type # Обновляем информацию в интерфейсе if sensor_type == "1": self.sensor_info_label.config( text="Датчик хоппера: Нормально разомкнутый", foreground="orange" ) elif sensor_type == "2": self.sensor_info_label.config( text="Датчик хоппера: Нормально замкнутый", foreground="orange" ) self._update_status() def _update_status(self): """Обновить статус сохранения""" version_selected = bool(self.version_var.get()) sensor_selected = bool(self.sensor_var.get()) if version_selected and sensor_selected: self.status_label.config( text="Статус: все настройки выбраны (готово к сохранению)", foreground="blue" ) elif version_selected or sensor_selected: self.status_label.config( text="Статус: часть настроек выбрана", foreground="orange" ) else: self.status_label.config( text="Статус: настройки не выбраны", foreground="red" ) def _save_electro_version(self): """Сохранить версию электроники в файл""" version = self.version_var.get() if not version: return False, "Версия электроники не выбрана" try: # Создаем директорию, если её нет DOCUMENTS_PATH.mkdir(parents=True, exist_ok=True) # Записываем версию в файл with ELECTRO_FILE.open("w", encoding="utf-8") as f: f.write(version + "\n") return True, f"Версия электроники {version}.0 сохранена" except Exception as e: return False, f"Ошибка сохранения электроники: {e}" def _save_hopper_sensor(self): """Сохранить тип датчика хоппера в файл""" sensor_type = self.sensor_var.get() if not sensor_type: return False, "Тип датчика хоппера не выбран" try: # Создаем директорию, если её нет DOCUMENTS_PATH.mkdir(parents=True, exist_ok=True) # Записываем тип датчика в файл with HOPPER_SENSOR_FILE.open("w", encoding="utf-8") as f: f.write(sensor_type + "\n") # Определяем текст для пользователя sensor_text = "Нормально разомкнутый" if sensor_type == "1" else "Нормально замкнутый" return True, f"Тип датчика хоппера '{sensor_text}' сохранен" except Exception as e: return False, f"Ошибка сохранения датчика хоппера: {e}" def save_all_settings(self): """Сохранить все настройки (версию электроники и тип датчика хоппера)""" # Проверяем, что выбрана версия электроники if not self.version_var.get(): messagebox.showerror( "Ошибка", "Выберите версию электроники перед сохранением." ) return # Проверяем, что выбран тип датчика хоппера if not self.sensor_var.get(): messagebox.showerror( "Ошибка", "Выберите тип датчика хоппера перед сохранением." ) return results = [] errors = [] # Сохраняем версию электроники success_electro, message_electro = self._save_electro_version() if success_electro: results.append(message_electro) else: errors.append(message_electro) # Сохраняем тип датчика хоппера success_sensor, message_sensor = self._save_hopper_sensor() if success_sensor: results.append(message_sensor) else: errors.append(message_sensor) # Формируем итоговое сообщение if errors: # Были ошибки error_message = "Не все настройки удалось сохранить:\n\n" error_message += "\n".join(results) if results else "" error_message += "\n\nОшибки:\n" error_message += "\n".join(errors) messagebox.showerror("Ошибка сохранения", error_message) # Обновляем статус self.status_label.config( text="Статус: ошибка сохранения", foreground="red" ) else: # Все успешно сохранено success_message = "Все настройки успешно сохранены!\n\n" success_message += "\n".join(results) messagebox.showinfo("Готово", success_message) # Обновляем информацию в интерфейсе version = self.version_var.get() sensor_type = self.sensor_var.get() sensor_text = "Нормально разомкнутый" if sensor_type == "1" else "Нормально замкнутый" self.electro_info_label.config( text=f"Электроника: ✓ версия {version}.0 (сохранено)", foreground="green" ) self.sensor_info_label.config( text=f"Датчик хоппера: ✓ {sensor_text} (сохранено)", foreground="green" ) self.status_label.config( text="Статус: ✓ все настройки сохранены", foreground="green" ) def load_saved_settings(self): """Загрузить сохраненные настройки из файлов""" results = [] warnings = [] # Загружаем версию электроники try: if ELECTRO_FILE.exists(): with ELECTRO_FILE.open("r", encoding="utf-8") as f: version = f.readline().strip() if version in ["1", "2"]: self.version_var.set(version) self.current_version = version version_text = f"Версия {version}.0" results.append(f"Электроника: {version_text}") if version == "1": self.electro_info_label.config( text=f"Электроника: {version_text} (загружено)", foreground="green" ) else: self.electro_info_label.config( text=f"Электроника: {version_text} (загружено)", foreground="green" ) else: warnings.append("Неверный формат файла электроники") else: warnings.append("Файл электроники не найден") except Exception as e: warnings.append(f"Ошибка загрузки электроники: {e}") # Загружаем тип датчика хоппера try: if HOPPER_SENSOR_FILE.exists(): with HOPPER_SENSOR_FILE.open("r", encoding="utf-8") as f: sensor_type = f.readline().strip() if sensor_type in ["1", "2"]: self.sensor_var.set(sensor_type) self.current_sensor_type = sensor_type sensor_text = "Нормально разомкнутый" if sensor_type == "1" else "Нормально замкнутый" results.append(f"Датчик хоппера: {sensor_text}") self.sensor_info_label.config( text=f"Датчик хоппера: {sensor_text} (загружено)", foreground="green" ) else: warnings.append("Неверный формат файла датчика хоппера") else: # Используем значение по умолчанию (нормально разомкнутый) self.sensor_var.set("1") self.current_sensor_type = "1" results.append("Датчик хоппера: Нормально разомкнутый (по умолчанию)") self.sensor_info_label.config( text="Датчик хоппера: Нормально разомкнутый (по умолчанию)", foreground="blue" ) except Exception as e: warnings.append(f"Ошибка загрузки датчика хоппера: {e}") # Обновляем статус if results and not warnings: self.status_label.config( text="Статус: ✓ настройки загружены", foreground="green" ) elif warnings: warning_text = "\n".join(warnings) self.status_label.config( text="Статус: загружены с ошибками", foreground="orange" ) if results: # Показываем сообщение с предупреждениями message_text = "Загружены не все настройки:\n\n" message_text += "\n".join(results) message_text += "\n\nПредупреждения:\n" message_text += warning_text messagebox.showwarning("Предупреждение", message_text) # Обновляем общий статус self._update_status() return len(warnings) == 0 def get_current_version(self): """Получить текущую выбранную версию электроники (для использования другими модулями)""" return self.current_version def get_current_sensor_type(self): """Получить текущий выбранный тип датчика хоппера (для использования другими модулями)""" return self.current_sensor_type def save_electro_config(self): """Сохранить конфигурацию электроники (интерфейсный метод для main)""" self.save_all_settings()