import tkinter as tk from tkinter import ttk, messagebox from pathlib import Path import sys import os # Добавляем родительскую директорию в путь для импорта sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # DOCUMENTS_PATH как в других вкладках try: import config DOCUMENTS_PATH = config.DOCUMENTS_PATH except ImportError: DOCUMENTS_PATH = Path(os.environ.get("USERPROFILE", str(Path.home()))) / "Documents" / "C4" class ServerModuleTab: """ Вкладка управления веб-сервером (сайт-панелью): - чекбокс "Запускать сайт-панель" => Documents\\C4\\serverOn.rr (1/0) - пароль админа (до 10 символов) => AppData\\LocalLow\\Robotic Retailers\\Robonect4\\admin_password.txt - снизу статус: состояние сервера и текущий пароль (или предупреждение о дефолтном 1234) """ def __init__(self, parent_frame): self.parent = parent_frame # Пути self.server_on_file = DOCUMENTS_PATH / "serverOn.rr" user_home = Path(os.environ.get("USERPROFILE", str(Path.home()))) self.password_file = ( user_home / "AppData" / "LocalLow" / "Robotic Retailers" / "Robonect4" / "admin_password.txt" ) # Vars self.server_enabled_var = tk.BooleanVar(value=False) self.password_var = tk.StringVar(value="") # UI container self.container = ttk.Frame(parent_frame, padding=20) self.container.pack(fill=tk.BOTH, expand=True) self._build_ui() # Обновлять статус при открытии вкладки # (в Notebook при переключении вкладок обычно триггерится Visibility) self.parent.bind("", lambda e: self.update_status()) # Загрузим текущее состояние self.load_state() self.update_status() # ---------------- UI ---------------- def _build_ui(self): # Заголовок title_frame = ttk.Frame(self.container) title_frame.pack(fill=tk.X, pady=(0, 20)) ttk.Label( title_frame, text="Управление веб-сервером", font=("Segoe UI", 12, "bold"), ).pack() ttk.Separator(self.container, orient="horizontal").pack(fill=tk.X, pady=(0, 20)) # Основной блок настроек settings_frame = ttk.LabelFrame(self.container, text="Настройки", padding=15) settings_frame.pack(fill=tk.X, pady=(0, 15)) # 1) Чекбокс self.server_check = ttk.Checkbutton( settings_frame, text="Запускать сайт-панель", variable=self.server_enabled_var, command=self._on_server_toggle, ) self.server_check.pack(anchor=tk.W, pady=(0, 12)) # 2) Пароль pass_row = ttk.Frame(settings_frame) pass_row.pack(fill=tk.X, pady=(0, 8)) ttk.Label(pass_row, text="Пароль администратора (до 10 символов):").pack( side=tk.LEFT, padx=(0, 10) ) vcmd = (self.container.register(self._validate_password_len), "%P") self.password_entry = ttk.Entry( pass_row, textvariable=self.password_var, width=20, show="*", validate="key", validatecommand=vcmd, ) self.password_entry.pack(side=tk.LEFT) # Кнопки btn_row = ttk.Frame(settings_frame) btn_row.pack(fill=tk.X, pady=(10, 0)) ttk.Button( btn_row, text="Сохранить пароль", command=self._save_password, width=25, ).pack(side=tk.LEFT, padx=(0, 10)) ttk.Button( btn_row, text="Обновить статус", command=self.update_status, width=25, ).pack(side=tk.LEFT) # Статус снизу self.status_frame = ttk.LabelFrame(self.container, text="Статус", padding=15) self.status_frame.pack(fill=tk.X) self.server_status_label = ttk.Label( self.status_frame, text="Сайт-панель: —", font=("Segoe UI", 10), ) self.server_status_label.pack(anchor=tk.W, pady=(0, 6)) self.password_status_label = ttk.Label( self.status_frame, text="Пароль: —", font=("Segoe UI", 10), ) self.password_status_label.pack(anchor=tk.W, pady=(0, 10)) # Пути (серым) self.paths_label = ttk.Label( self.status_frame, text=f"serverOn.rr: {self.server_on_file}\npassword: {self.password_file}", font=("Segoe UI", 9), foreground="gray", ) self.paths_label.pack(anchor=tk.W) # ---------------- Logic ---------------- def _validate_password_len(self, proposed_value: str) -> bool: """Разрешаем ввод максимум 10 символов.""" return len(proposed_value) <= 10 def load_state(self): """Загрузить состояние из файлов в UI.""" # serverOn.rr enabled = self._read_server_enabled() self.server_enabled_var.set(enabled) # password pwd = self._read_password() # Если вдруг в файле длиннее 10 — в поле покажем первые 10, а статус покажет как есть self.password_var.set(pwd[:10] if pwd else "") def _on_server_toggle(self): """Сразу пишем 1/0 в serverOn.rr при клике по чекбоксу.""" try: self._write_server_enabled(self.server_enabled_var.get()) self.update_status() except Exception as e: messagebox.showerror("Ошибка", f"Не удалось записать serverOn.rr:\n{e}") # Откатим UI к факту из файла self.server_enabled_var.set(self._read_server_enabled()) self.update_status() def _write_server_enabled(self, enabled: bool): DOCUMENTS_PATH.mkdir(parents=True, exist_ok=True) with open(self.server_on_file, "w", encoding="utf-8") as f: f.write("1" if enabled else "0") def _read_server_enabled(self) -> bool: try: if not self.server_on_file.exists(): return False content = self.server_on_file.read_text(encoding="utf-8").strip() return content == "1" except Exception: return False def _save_password(self): """Сохранить пароль в admin_password.txt""" try: pwd = (self.password_var.get() or "").strip() if len(pwd) > 10: messagebox.showerror("Ошибка", "Пароль должен быть не длиннее 10 символов.") return # Если пусто — считаем что пароль НЕ задан (удалим файл, если был) if pwd == "": if self.password_file.exists(): try: self.password_file.unlink() except Exception: # если не получилось удалить — просто перезапишем пустым self.password_file.parent.mkdir(parents=True, exist_ok=True) self.password_file.write_text("", encoding="utf-8") self.update_status() messagebox.showinfo("Готово", "Пароль очищен (не задан).") return self.password_file.parent.mkdir(parents=True, exist_ok=True) self.password_file.write_text(pwd, encoding="utf-8") self.update_status() messagebox.showinfo("Готово", "Пароль сохранён.") except Exception as e: messagebox.showerror("Ошибка", f"Не удалось сохранить пароль:\n{e}") def _read_password(self) -> str: try: if not self.password_file.exists(): return "" return self.password_file.read_text(encoding="utf-8").strip() except Exception: return "" def update_status(self): """Обновить статус-фрейм: состояние сервера + пароль.""" enabled = self._read_server_enabled() pwd = self._read_password() # Сервер if enabled: self.server_status_label.config( text="Сайт-панель: ВКЛЮЧЕНА (будет запускаться)", foreground="green", ) else: self.server_status_label.config( text="Сайт-панель: ВЫКЛЮЧЕНА (не будет запускаться)", foreground="gray", ) # Пароль if not pwd: self.password_status_label.config( text="Пароль: пароль не задан. Пароль по умолчанию 1234", foreground="red", ) else: self.password_status_label.config( text=f"Пароль: {pwd}", foreground="green", ) # Для совместимости с main.save_all (если у тебя есть общая кнопка "сохранить всё") def save_server_config(self): """Сохранить текущее состояние из UI в файлы.""" self._write_server_enabled(self.server_enabled_var.get()) # пароль сохраняем как по кнопке (включая “очистку”) self._save_password()