303 lines
14 KiB
Python
303 lines
14 KiB
Python
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() |