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

303 lines
14 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 json
from typing import Dict, Any, Callable
from PySide6.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QTextEdit,
QPushButton, QGroupBox, QGridLayout, QFrame, QScrollArea
)
from PySide6.QtGui import QFont, QPalette, QColor
from PySide6.QtCore import Qt, QTimer
class PreviewWidget(QWidget):
"""Виджет предпросмотра конфига в удобочитаемом формате с темной темой"""
def __init__(self, config_manager):
super().__init__()
self.config_manager = config_manager
self.get_config_callback = None
self.init_ui()
# Подписываемся на обновления конфига
self.config_manager.add_observer(self.update_preview)
# Используем QTimer для отложенного обновления после полной инициализации
QTimer.singleShot(100, self.initial_update)
def init_ui(self):
layout = QVBoxLayout()
# Заголовок
title_label = QLabel("Предпросмотр конфигурации")
title_label.setAlignment(Qt.AlignCenter)
layout.addWidget(title_label)
# Создаем область прокрутки для текущего конфига
current_group = QGroupBox("📁 Текущая конфигурация (из файла)")
current_layout = QVBoxLayout()
current_layout.setSpacing(5)
# Область прокрутки для текущего конфига
current_scroll = QScrollArea()
current_scroll.setWidgetResizable(True)
current_scroll.setFrameShape(QFrame.NoFrame)
current_scroll.setMaximumHeight(250)
self.current_content = QWidget()
self.current_content_layout = QVBoxLayout(self.current_content)
self.current_content_layout.setAlignment(Qt.AlignTop)
self.current_content_layout.setSpacing(5)
self.current_content_layout.setContentsMargins(5, 5, 5, 5)
current_scroll.setWidget(self.current_content)
current_layout.addWidget(current_scroll)
current_group.setLayout(current_layout)
layout.addWidget(current_group)
# Разделитель
separator = QFrame()
separator.setFrameShape(QFrame.HLine)
separator.setFrameShadow(QFrame.Sunken)
layout.addWidget(separator)
# Создаем область прокрутки для нового конфига
new_group = QGroupBox("✨ Новая конфигурация (будет сохранена)")
new_layout = QVBoxLayout()
new_layout.setSpacing(5)
# Область прокрутки для нового конфига
new_scroll = QScrollArea()
new_scroll.setWidgetResizable(True)
new_scroll.setFrameShape(QFrame.NoFrame)
new_scroll.setMaximumHeight(250)
self.new_content = QWidget()
self.new_content_layout = QVBoxLayout(self.new_content)
self.new_content_layout.setAlignment(Qt.AlignTop)
self.new_content_layout.setSpacing(5)
self.new_content_layout.setContentsMargins(5, 5, 5, 5)
new_scroll.setWidget(self.new_content)
new_layout.addWidget(new_scroll)
new_group.setLayout(new_layout)
layout.addWidget(new_group)
# Информационная панель
info_frame = QFrame()
info_layout = QHBoxLayout(info_frame)
info_layout.setContentsMargins(10, 5, 10, 5)
info_icon = QLabel("ℹ️")
info_layout.addWidget(info_icon)
self.info_label = QLabel("Загрузка конфигурации...")
info_layout.addWidget(self.info_label)
info_layout.addStretch()
layout.addWidget(info_frame)
# Кнопка сохранения
self.save_button = QPushButton("💾 Сохранить конфигурацию")
layout.addWidget(self.save_button)
self.setLayout(layout)
def initial_update(self):
"""Первоначальное обновление при загрузке виджета"""
if self.get_config_callback:
self.update_preview()
self.info_label.setText("✓ Конфигурация загружена")
def set_get_config_callback(self, callback: Callable[[], Dict[str, Any]]):
"""Устанавливает функцию для получения текущей конфигурации"""
self.get_config_callback = callback
# Сразу обновляем preview после установки колбэка
QTimer.singleShot(50, self.initial_update)
def format_value(self, key: str, value: Any) -> str:
"""Форматирует значение в зависимости от типа и ключа"""
if isinstance(value, bool):
return "✅ Да" if value else "❌ Нет"
elif isinstance(value, int):
if key in ['light-version', 'electronic-version']:
return f"⚡ Версия {value}"
return str(value)
elif isinstance(value, str):
if key == 'sensor-positive-value':
return "🔌 Нормально разомкнутый" if value == '0' else "🔌 Нормально замкнутый"
elif key == 'serial':
return f"🔢 {value}"
elif key == 'server-password':
if value:
return "🔑 " + "•" * len(value)
return "🔑 (не установлен)"
return value
return str(value)
def get_russian_key(self, key: str) -> str:
"""Возвращает русское название параметра"""
translations = {
'serial': 'Серийный номер',
'light-version': 'Версия подсветки',
'electronic-version': 'Версия электроники',
'sensor-positive-value': 'Тип датчика хоппера',
'start-server': 'Запуск сайт-панели',
'server-password': 'Пароль администратора'
}
return translations.get(key, key)
def create_parameter_widget(self, key: str, value: Any, is_new: bool = False, is_changed: bool = False) -> QWidget:
"""Создает виджет для отображения одного параметра с темной темой"""
widget = QWidget()
# Определяем стиль в зависимости от типа (новый/текущий и измененный)
if is_new:
if is_changed:
# Измененный параметр в новой конфигурации
bg_color = "#1e3b2a"
border_color = "#f39c12"
border_style = "2px solid"
else:
# Новый параметр без изменений
bg_color = "#1a2f2a"
border_color = "#27ae60"
border_style = "1px solid"
else:
# Текущая конфигурация
bg_color = "#2c3e50"
border_color = "#7f8c8d"
border_style = "1px solid"
layout = QHBoxLayout(widget)
layout.setContentsMargins(10, 8, 10, 8)
layout.setSpacing(10)
# Название параметра
name_label = QLabel(self.get_russian_key(key))
name_label.setAlignment(Qt.AlignLeft)
layout.addWidget(name_label)
# Разделитель
separator = QLabel(":")
layout.addWidget(separator)
# Значение
value_label = QLabel(self.format_value(key, value))
value_label.setWordWrap(True)
value_label.setAlignment(Qt.AlignLeft)
layout.addWidget(value_label, 1)
# Если параметр изменен, добавляем индикатор
if is_changed:
change_indicator = QLabel("✏️ изменено")
change_indicator.setAlignment(Qt.AlignRight)
layout.addWidget(change_indicator)
return widget
def update_preview(self):
"""Обновляет предпросмотр в удобочитаемом формате с темной темой"""
# Очищаем текущее содержимое
self.clear_layout(self.current_content_layout)
self.clear_layout(self.new_content_layout)
# Отображаем текущий конфиг
current_config = self.config_manager.current_config
if current_config and current_config != {}:
# Сортируем ключи для консистентного отображения
keys_order = ['serial', 'light-version', 'electronic-version',
'sensor-positive-value', 'start-server', 'server-password']
for key in keys_order:
if key in current_config:
param_widget = self.create_parameter_widget(
key, current_config[key], is_new=False, is_changed=False
)
self.current_content_layout.addWidget(param_widget)
else:
# Сообщение об отсутствии конфига
empty_widget = QWidget()
empty_layout = QHBoxLayout(empty_widget)
empty_label = QLabel("📭 Файл конфигурации не найден или пуст")
empty_label.setAlignment(Qt.AlignCenter)
empty_layout.addWidget(empty_label)
self.current_content_layout.addWidget(empty_widget)
# Добавляем растяжку в конец текущего конфига
self.current_content_layout.addStretch()
# Отображаем новый конфиг
if self.get_config_callback:
new_config = self.get_config_callback()
if new_config:
# Сортируем ключи для консистентного отображения
keys_order = ['serial', 'light-version', 'electronic-version',
'sensor-positive-value', 'start-server', 'server-password']
changes_count = 0
for key in keys_order:
if key in new_config:
# Проверяем, отличается ли значение от текущего
is_different = (
current_config and
key in current_config and
current_config[key] != new_config[key]
)
if is_different:
changes_count += 1
param_widget = self.create_parameter_widget(
key, new_config[key], is_new=True, is_changed=is_different
)
self.new_content_layout.addWidget(param_widget)
# Добавляем растяжку в конец нового конфига
self.new_content_layout.addStretch()
# Обновляем информационную панель
if current_config and current_config != {}:
if changes_count > 0:
self.info_label.setText(f"📊 Обнаружено изменений: {changes_count}")
else:
self.info_label.setText("✓ Изменений нет")
else:
self.info_label.setText("🆕 Будет создан новый файл конфигурации")
else:
# Пустой новый конфиг
empty_new_widget = QWidget()
empty_new_layout = QHBoxLayout(empty_new_widget)
empty_new_label = QLabel("⚙️ Настройте параметры на других вкладках")
empty_new_label.setAlignment(Qt.AlignCenter)
empty_new_layout.addWidget(empty_new_label)
self.new_content_layout.addWidget(empty_new_widget)
self.new_content_layout.addStretch()
self.info_label.setText("👆 Настройте параметры на других вкладках")
def clear_layout(self, layout):
"""Очищает layout от всех виджетов"""
while layout.count():
item = layout.takeAt(0)
widget = item.widget()
if widget:
widget.deleteLater()
def showEvent(self, event):
"""Переопределяем событие показа виджета для обновления при каждом открытии вкладки"""
super().showEvent(event)
# Обновляем preview при каждом показе вкладки
self.update_preview()