268 lines
10 KiB
Python
268 lines
10 KiB
Python
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("<Visibility>", 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()
|