191 lines
8.3 KiB
Python
191 lines
8.3 KiB
Python
|
|
"""
|
|||
|
|
Главный контроллер приложения - связывает модель и представление
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from PySide6.QtCore import QObject, Signal
|
|||
|
|
from datetime import datetime
|
|||
|
|
from pathlib import Path
|
|||
|
|
import numpy as np
|
|||
|
|
|
|||
|
|
from models.api_model import HelioviewerAPI
|
|||
|
|
from models.image_model import ImageModel
|
|||
|
|
from views.main_window import MainWindow
|
|||
|
|
from utils.image_processor import ImageProcessor
|
|||
|
|
from utils.metadata_parser import MetadataParser
|
|||
|
|
|
|||
|
|
|
|||
|
|
class AppController(QObject):
|
|||
|
|
"""Главный контроллер приложения"""
|
|||
|
|
|
|||
|
|
def __init__(self):
|
|||
|
|
super().__init__()
|
|||
|
|
self.image_model = ImageModel()
|
|||
|
|
self.api = HelioviewerAPI()
|
|||
|
|
self.main_window = None
|
|||
|
|
self.timelapse_controller = None
|
|||
|
|
|
|||
|
|
# Подключаем сигналы модели
|
|||
|
|
self.image_model.layer_added.connect(self.on_layer_added)
|
|||
|
|
self.image_model.layer_removed.connect(self.on_layer_removed)
|
|||
|
|
self.image_model.layer_visibility_changed.connect(self.on_layer_visibility_changed)
|
|||
|
|
self.image_model.layer_opacity_changed.connect(self.on_layer_opacity_changed)
|
|||
|
|
self.image_model.layer_selected.connect(self.on_layer_selected)
|
|||
|
|
|
|||
|
|
# Папка для сохранения по умолчанию
|
|||
|
|
self.download_folder = Path.home() / "SolarImages"
|
|||
|
|
self.download_folder.mkdir(exist_ok=True)
|
|||
|
|
|
|||
|
|
def show_main_window(self):
|
|||
|
|
"""Показывает главное окно"""
|
|||
|
|
self.main_window = MainWindow(self)
|
|||
|
|
self.main_window.show()
|
|||
|
|
|
|||
|
|
# Подключаем сигналы от панели управления
|
|||
|
|
control_panel = self.main_window.get_control_panel()
|
|||
|
|
control_panel.load_image_requested.connect(self.load_image_from_api)
|
|||
|
|
|
|||
|
|
def load_image_from_api(self, source_id: int, date: datetime):
|
|||
|
|
"""Загружает изображение из API"""
|
|||
|
|
self.main_window.update_status(f"Загрузка изображения: {date.strftime('%Y-%m-%d %H:%M')} UTC")
|
|||
|
|
|
|||
|
|
# Скачиваем изображение
|
|||
|
|
filepath = self.api.download_image(source_id, date, self.download_folder)
|
|||
|
|
|
|||
|
|
if filepath:
|
|||
|
|
# Загружаем данные изображения
|
|||
|
|
img_data = ImageProcessor.load_jp2(str(filepath))
|
|||
|
|
|
|||
|
|
if img_data is not None:
|
|||
|
|
# Извлекаем метаданные
|
|||
|
|
metadata = MetadataParser.extract_metadata(str(filepath))
|
|||
|
|
|
|||
|
|
# Получаем информацию о спектре
|
|||
|
|
source_info = self.api.SOURCES.get(source_id, {})
|
|||
|
|
wavelength = source_info.get("wavelength", "Unknown")
|
|||
|
|
|
|||
|
|
# Добавляем слой в модель
|
|||
|
|
layer_id = self.image_model.add_layer(
|
|||
|
|
filepath, source_id, date, wavelength, img_data, metadata
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
self.main_window.update_status(f"✓ Загружено: {filepath.name}")
|
|||
|
|
else:
|
|||
|
|
self.main_window.update_status("✗ Ошибка обработки изображения")
|
|||
|
|
else:
|
|||
|
|
self.main_window.update_status("✗ Ошибка загрузки изображения")
|
|||
|
|
|
|||
|
|
def on_layer_added(self, layer):
|
|||
|
|
"""Обработчик добавления слоя"""
|
|||
|
|
self.main_window.update_layer_list(self.image_model.get_all_layers())
|
|||
|
|
self.main_window.canvas.set_image(layer.id, layer.image_data)
|
|||
|
|
|
|||
|
|
def on_layer_removed(self, layer_id):
|
|||
|
|
"""Обработчик удаления слоя"""
|
|||
|
|
self.main_window.layer_widget.remove_layer(layer_id)
|
|||
|
|
self.main_window.canvas.remove_layer(layer_id)
|
|||
|
|
|
|||
|
|
if not self.image_model.get_all_layers():
|
|||
|
|
self.main_window.metadata_viewer.clear()
|
|||
|
|
|
|||
|
|
def on_layer_visibility_changed(self, layer_id, visible):
|
|||
|
|
"""Обработчик изменения видимости слоя"""
|
|||
|
|
self.main_window.canvas.set_layer_visibility(layer_id, visible)
|
|||
|
|
|
|||
|
|
def on_layer_opacity_changed(self, layer_id, opacity):
|
|||
|
|
"""Обработчик изменения прозрачности слоя"""
|
|||
|
|
self.main_window.canvas.set_layer_opacity(layer_id, opacity)
|
|||
|
|
|
|||
|
|
def on_layer_selected(self, layer_id):
|
|||
|
|
"""Обработчик выбора слоя"""
|
|||
|
|
for layer in self.image_model.get_all_layers():
|
|||
|
|
if layer.id == layer_id:
|
|||
|
|
if layer.metadata:
|
|||
|
|
self.main_window.metadata_viewer.display_metadata(layer.metadata)
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
def set_layer_visibility(self, layer_id, visible):
|
|||
|
|
self.image_model.set_layer_visibility(layer_id, visible)
|
|||
|
|
|
|||
|
|
def set_layer_opacity(self, layer_id, opacity):
|
|||
|
|
self.image_model.set_layer_opacity(layer_id, opacity)
|
|||
|
|
|
|||
|
|
def clear_all_layers(self):
|
|||
|
|
self.image_model.clear()
|
|||
|
|
self.main_window.canvas.clear_all_layers()
|
|||
|
|
self.main_window.metadata_viewer.clear()
|
|||
|
|
|
|||
|
|
# Добавьте эти методы в класс AppController:
|
|||
|
|
|
|||
|
|
def create_timelapse(self, source_id: int, start_date: datetime,
|
|||
|
|
end_date: datetime, output_path: Path,
|
|||
|
|
fps: int = 10, output_format: str = "mp4"):
|
|||
|
|
"""Создает таймлапс"""
|
|||
|
|
from controllers.timelapse_controller import TimelapseController
|
|||
|
|
self.timelapse_controller = TimelapseController()
|
|||
|
|
|
|||
|
|
# Подключаем сигналы
|
|||
|
|
self.timelapse_controller.progress_updated.connect(self.on_timelapse_progress)
|
|||
|
|
self.timelapse_controller.log_message.connect(self.on_timelapse_log)
|
|||
|
|
self.timelapse_controller.finished.connect(self.on_timelapse_finished)
|
|||
|
|
|
|||
|
|
# Запускаем
|
|||
|
|
self.timelapse_controller.create_timelapse(
|
|||
|
|
source_id, start_date, end_date, output_path, fps, output_format
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
def cancel_timelapse(self):
|
|||
|
|
"""Отменяет создание таймлапса"""
|
|||
|
|
if hasattr(self, 'timelapse_controller'):
|
|||
|
|
self.timelapse_controller.cancel()
|
|||
|
|
|
|||
|
|
def on_timelapse_progress(self, current, total, message):
|
|||
|
|
"""Прогресс таймлапса"""
|
|||
|
|
if self.main_window:
|
|||
|
|
self.main_window.update_status(f"Таймлапс: {message}")
|
|||
|
|
|
|||
|
|
def on_timelapse_log(self, message):
|
|||
|
|
"""Лог таймлапса"""
|
|||
|
|
print(f"[Timelapse] {message}")
|
|||
|
|
|
|||
|
|
def on_timelapse_finished(self, success, message):
|
|||
|
|
"""Завершение таймлапса"""
|
|||
|
|
if self.main_window:
|
|||
|
|
if success:
|
|||
|
|
self.main_window.update_status(f"✅ Таймлапс создан")
|
|||
|
|
from PySide6.QtWidgets import QMessageBox
|
|||
|
|
QMessageBox.information(self.main_window, "Готово", f"Таймлапс сохранен:\n{message}")
|
|||
|
|
else:
|
|||
|
|
self.main_window.update_status(f"❌ Ошибка: {message}")
|
|||
|
|
|
|||
|
|
# Добавьте метод:
|
|||
|
|
def create_timelapse(self, source_id, start_date, end_date, output_path, fps=10, output_format="mp4"):
|
|||
|
|
"""Создает таймлапс"""
|
|||
|
|
print(f"[DEBUG] AppController.create_timelapse вызван")
|
|||
|
|
from controllers.timelapse_controller import TimelapseController
|
|||
|
|
|
|||
|
|
self.timelapse_controller = TimelapseController()
|
|||
|
|
|
|||
|
|
# Подключаем сигналы
|
|||
|
|
self.timelapse_controller.progress_updated.connect(self.on_timelapse_progress)
|
|||
|
|
self.timelapse_controller.log_message.connect(self.on_timelapse_log)
|
|||
|
|
self.timelapse_controller.finished.connect(self.on_timelapse_finished)
|
|||
|
|
|
|||
|
|
# Запускаем
|
|||
|
|
self.timelapse_controller.create_timelapse(
|
|||
|
|
source_id, start_date, end_date, output_path, fps, output_format
|
|||
|
|
)
|
|||
|
|
print(f"[DEBUG] TimelapseController создан и запущен")
|
|||
|
|
|
|||
|
|
def on_timelapse_log(self, message):
|
|||
|
|
"""Лог таймлапса"""
|
|||
|
|
print(f"[TIMELAPSE] {message}")
|
|||
|
|
if self.main_window:
|
|||
|
|
self.main_window.update_status(f"Таймлапс: {message}")
|
|||
|
|
|
|||
|
|
def create_timelapse(self, source_id, start_date, end_date, output_path, fps=10, output_format="mp4"):
|
|||
|
|
"""Создает таймлапс - заглушка, реальный вызов из диалога"""
|
|||
|
|
# Реальная реализация в диалоге
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
def cancel_timelapse(self):
|
|||
|
|
pass
|