2026-07-14 11:14:13 +03:00

268 lines
10 KiB
Python
Raw Permalink 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.

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()