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