647 lines
27 KiB
Python
647 lines
27 KiB
Python
# 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(
|
||
"<Configure>",
|
||
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() |