251 lines
9.7 KiB
Python
251 lines
9.7 KiB
Python
|
|
"""
|
|||
|
|
Главное окно приложения - содержит меню, панели и canvas
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from PySide6.QtWidgets import (
|
|||
|
|
QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
|||
|
|
QSplitter, QMenuBar, QMenu, QStatusBar, QMessageBox,
|
|||
|
|
QFileDialog, QToolBar, QFrame, QScrollArea, QLabel
|
|||
|
|
)
|
|||
|
|
from PySide6.QtCore import Qt, QSize
|
|||
|
|
from PySide6.QtGui import QAction, QKeySequence
|
|||
|
|
|
|||
|
|
from views.layer_widget import LayerWidget
|
|||
|
|
from views.canvas_widget import SolarCanvas
|
|||
|
|
from views.control_panel import ControlPanel
|
|||
|
|
from views.timelapse_dialog import TimelapseDialog
|
|||
|
|
from views.metadata_viewer import MetadataViewer
|
|||
|
|
|
|||
|
|
|
|||
|
|
class MainWindow(QMainWindow):
|
|||
|
|
"""Главное окно приложения"""
|
|||
|
|
|
|||
|
|
def __init__(self, controller):
|
|||
|
|
super().__init__()
|
|||
|
|
self.controller = controller
|
|||
|
|
self.init_ui()
|
|||
|
|
|
|||
|
|
def init_ui(self):
|
|||
|
|
"""Инициализация пользовательского интерфейса"""
|
|||
|
|
self.setWindowTitle("Helioviewer Solar Viewer - Профессиональный просмотрщик снимков Солнца")
|
|||
|
|
self.setMinimumSize(1200, 800)
|
|||
|
|
|
|||
|
|
# Центральный виджет
|
|||
|
|
central_widget = QWidget()
|
|||
|
|
self.setCentralWidget(central_widget)
|
|||
|
|
|
|||
|
|
# Основной layout
|
|||
|
|
main_layout = QHBoxLayout(central_widget)
|
|||
|
|
main_layout.setContentsMargins(0, 0, 0, 0)
|
|||
|
|
|
|||
|
|
# Создаем сплиттер для левой панели и canvas
|
|||
|
|
splitter = QSplitter(Qt.Orientation.Horizontal)
|
|||
|
|
main_layout.addWidget(splitter)
|
|||
|
|
|
|||
|
|
# Левая панель (с прокруткой)
|
|||
|
|
left_panel = self.create_left_panel_with_scroll()
|
|||
|
|
splitter.addWidget(left_panel)
|
|||
|
|
|
|||
|
|
# Устанавливаем начальную ширину сплиттера
|
|||
|
|
splitter.setSizes([400, self.width() - 400])
|
|||
|
|
|
|||
|
|
# Правая область (canvas)
|
|||
|
|
right_area = self.create_right_area()
|
|||
|
|
splitter.addWidget(right_area)
|
|||
|
|
|
|||
|
|
# Создаем меню
|
|||
|
|
self.create_menu_bar()
|
|||
|
|
|
|||
|
|
# Создаем статус бар
|
|||
|
|
self.status_bar = QStatusBar()
|
|||
|
|
self.setStatusBar(self.status_bar)
|
|||
|
|
self.status_bar.showMessage("Готов к работе")
|
|||
|
|
|
|||
|
|
# Добавляем тулбар для быстрого доступа
|
|||
|
|
self.create_toolbar()
|
|||
|
|
|
|||
|
|
def create_left_panel_with_scroll(self) -> QWidget:
|
|||
|
|
"""
|
|||
|
|
Создает левую панель с прокруткой, где все виджеты равномерно распределяют пространство
|
|||
|
|
"""
|
|||
|
|
# Внешний контейнер для скролла
|
|||
|
|
scroll_container = QWidget()
|
|||
|
|
scroll_layout = QVBoxLayout(scroll_container)
|
|||
|
|
scroll_layout.setContentsMargins(0, 0, 0, 0)
|
|||
|
|
|
|||
|
|
# Создаем QScrollArea
|
|||
|
|
scroll_area = QScrollArea()
|
|||
|
|
scroll_area.setWidgetResizable(True) # Позволяет виджету изменять размер
|
|||
|
|
scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
|||
|
|
scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
|||
|
|
scroll_area.setStyleSheet("""
|
|||
|
|
QScrollArea {
|
|||
|
|
border: none;
|
|||
|
|
background-color: #2b2b2b;
|
|||
|
|
}
|
|||
|
|
QScrollBar:vertical {
|
|||
|
|
background-color: #2b2b2b;
|
|||
|
|
width: 12px;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
}
|
|||
|
|
QScrollBar::handle:vertical {
|
|||
|
|
background-color: #555;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
min-height: 20px;
|
|||
|
|
}
|
|||
|
|
QScrollBar::handle:vertical:hover {
|
|||
|
|
background-color: #777;
|
|||
|
|
}
|
|||
|
|
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
|
|||
|
|
height: 0px;
|
|||
|
|
}
|
|||
|
|
""")
|
|||
|
|
|
|||
|
|
# Внутренний контейнер для всех виджетов
|
|||
|
|
content_widget = QWidget()
|
|||
|
|
content_widget.setStyleSheet("background-color: #2b2b2b;")
|
|||
|
|
layout = QVBoxLayout(content_widget)
|
|||
|
|
layout.setSpacing(10)
|
|||
|
|
layout.setContentsMargins(10, 10, 10, 10)
|
|||
|
|
|
|||
|
|
# Панель управления (выбор даты, спектра)
|
|||
|
|
self.control_panel = ControlPanel(self.controller)
|
|||
|
|
layout.addWidget(self.control_panel, 1) # stretch factor = 1
|
|||
|
|
|
|||
|
|
# Разделитель
|
|||
|
|
layout.addWidget(self.create_separator())
|
|||
|
|
|
|||
|
|
# Заголовок слоев
|
|||
|
|
title_layers = self.create_section_title("📁 Слои изображений")
|
|||
|
|
layout.addWidget(title_layers)
|
|||
|
|
|
|||
|
|
# Виджет слоев - будет занимать столько места, сколько нужно
|
|||
|
|
self.layer_widget = LayerWidget(self.controller)
|
|||
|
|
layout.addWidget(self.layer_widget, 2) # stretch factor = 2 (больше места)
|
|||
|
|
|
|||
|
|
# Разделитель
|
|||
|
|
layout.addWidget(self.create_separator())
|
|||
|
|
|
|||
|
|
# Заголовок метаданных
|
|||
|
|
title_metadata = self.create_section_title("📊 Метаданные FITS")
|
|||
|
|
layout.addWidget(title_metadata)
|
|||
|
|
|
|||
|
|
# Просмотрщик метаданных
|
|||
|
|
self.metadata_viewer = MetadataViewer()
|
|||
|
|
layout.addWidget(self.metadata_viewer, 1) # stretch factor = 1
|
|||
|
|
|
|||
|
|
# Растягивающийся спейсер внизу (опционально)
|
|||
|
|
layout.addStretch()
|
|||
|
|
|
|||
|
|
# Устанавливаем content_widget в scroll_area
|
|||
|
|
scroll_area.setWidget(content_widget)
|
|||
|
|
|
|||
|
|
# Добавляем scroll_area в контейнер
|
|||
|
|
scroll_layout.addWidget(scroll_area)
|
|||
|
|
|
|||
|
|
return scroll_container
|
|||
|
|
|
|||
|
|
def create_separator(self) -> QFrame:
|
|||
|
|
"""Создает линию-разделитель"""
|
|||
|
|
separator = QFrame()
|
|||
|
|
separator.setFrameShape(QFrame.Shape.HLine)
|
|||
|
|
separator.setFrameShadow(QFrame.Shadow.Sunken)
|
|||
|
|
separator.setStyleSheet("background-color: #555; max-height: 1px;")
|
|||
|
|
return separator
|
|||
|
|
|
|||
|
|
def create_section_title(self, title: str) -> QLabel:
|
|||
|
|
"""Создает заголовок секции"""
|
|||
|
|
label = QLabel(title)
|
|||
|
|
label.setStyleSheet("""
|
|||
|
|
font-weight: bold;
|
|||
|
|
font-size: 14px;
|
|||
|
|
padding: 8px 5px;
|
|||
|
|
background-color: #3c3c3c;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
margin-top: 5px;
|
|||
|
|
margin-bottom: 5px;
|
|||
|
|
""")
|
|||
|
|
label.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
|
|||
|
|
return label
|
|||
|
|
|
|||
|
|
def create_right_area(self) -> QWidget:
|
|||
|
|
"""Создает правую область с canvas"""
|
|||
|
|
area = QWidget()
|
|||
|
|
layout = QVBoxLayout(area)
|
|||
|
|
layout.setContentsMargins(0, 0, 0, 0)
|
|||
|
|
|
|||
|
|
# Canvas для отображения изображений
|
|||
|
|
self.canvas = SolarCanvas(self.controller)
|
|||
|
|
layout.addWidget(self.canvas)
|
|||
|
|
|
|||
|
|
return area
|
|||
|
|
|
|||
|
|
def create_menu_bar(self):
|
|||
|
|
"""Создает меню приложения"""
|
|||
|
|
menubar = self.menuBar()
|
|||
|
|
|
|||
|
|
# Меню Файл
|
|||
|
|
file_menu = menubar.addMenu("📁 Файл")
|
|||
|
|
|
|||
|
|
# Действия для меню Файл
|
|||
|
|
self.setup_action = QAction("⚙️ Настройки", self)
|
|||
|
|
self.exit_action = QAction("🚪 Выход", self)
|
|||
|
|
self.exit_action.setShortcut(QKeySequence.StandardKey.Quit)
|
|||
|
|
self.exit_action.triggered.connect(self.close)
|
|||
|
|
|
|||
|
|
file_menu.addAction(self.setup_action)
|
|||
|
|
file_menu.addSeparator()
|
|||
|
|
file_menu.addAction(self.exit_action)
|
|||
|
|
|
|||
|
|
# Меню Инструменты
|
|||
|
|
tools_menu = menubar.addMenu("🛠️ Инструменты")
|
|||
|
|
|
|||
|
|
self.timelapse_action = QAction("🎬 Таймлапс...", self)
|
|||
|
|
self.timelapse_action.triggered.connect(self.open_timelapse_dialog)
|
|||
|
|
tools_menu.addAction(self.timelapse_action)
|
|||
|
|
|
|||
|
|
# Меню Справка
|
|||
|
|
help_menu = menubar.addMenu("❓ Справка")
|
|||
|
|
|
|||
|
|
about_action = QAction("ℹ️ О программе", self)
|
|||
|
|
about_action.triggered.connect(self.show_about)
|
|||
|
|
help_menu.addAction(about_action)
|
|||
|
|
|
|||
|
|
def create_toolbar(self):
|
|||
|
|
"""Создает панель инструментов"""
|
|||
|
|
toolbar = QToolBar("Быстрый доступ")
|
|||
|
|
self.addToolBar(toolbar)
|
|||
|
|
toolbar.setIconSize(QSize(24, 24))
|
|||
|
|
|
|||
|
|
def open_timelapse_dialog(self):
|
|||
|
|
"""Открывает диалог создания таймлапса"""
|
|||
|
|
dialog = TimelapseDialog(self.controller, self)
|
|||
|
|
dialog.exec()
|
|||
|
|
|
|||
|
|
def show_about(self):
|
|||
|
|
"""Показывает информацию о программе"""
|
|||
|
|
QMessageBox.about(
|
|||
|
|
self,
|
|||
|
|
"О программе",
|
|||
|
|
"Helioviewer Solar Viewer v1.0\n\n"
|
|||
|
|
"Профессиональный просмотрщик снимков Солнца\n"
|
|||
|
|
"Использует данные Helioviewer API\n\n"
|
|||
|
|
"Возможности:\n"
|
|||
|
|
"• Загрузка снимков в различных спектрах\n"
|
|||
|
|
"• Многослойный режим с наложением\n"
|
|||
|
|
"• Создание таймлапс-анимаций\n"
|
|||
|
|
"• Просмотр FITS-метаданных\n\n"
|
|||
|
|
"© 2024 SolarViewer Team"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
def update_status(self, message: str, timeout: int = 3000):
|
|||
|
|
"""Обновляет статус в статус-баре"""
|
|||
|
|
self.status_bar.showMessage(message, timeout)
|
|||
|
|
|
|||
|
|
def update_layer_list(self, layers):
|
|||
|
|
"""Обновляет список слоев"""
|
|||
|
|
self.layer_widget.update_layers(layers)
|
|||
|
|
|
|||
|
|
def get_control_panel(self):
|
|||
|
|
"""Возвращает панель управления"""
|
|||
|
|
return self.control_panel
|