281 lines
10 KiB
Python
281 lines
10 KiB
Python
|
|
"""
|
|||
|
|
CalibrationDialog - главный диалог калибровки
|
|||
|
|
С выбором камеры, папки и типа кадров
|
|||
|
|
"""
|
|||
|
|
from PySide6.QtWidgets import (
|
|||
|
|
QDialog, QVBoxLayout, QHBoxLayout, QGridLayout,
|
|||
|
|
QLabel, QComboBox, QLineEdit, QPushButton, QFrame,
|
|||
|
|
QMessageBox, QFileDialog, QWidget
|
|||
|
|
)
|
|||
|
|
from PySide6.QtCore import Qt, QTimer
|
|||
|
|
from PySide6.QtGui import QFont
|
|||
|
|
|
|||
|
|
from services.config_service import ConfigService
|
|||
|
|
|
|||
|
|
|
|||
|
|
class CalibrationDialog(QDialog):
|
|||
|
|
"""Главное окно калибровки"""
|
|||
|
|
|
|||
|
|
def __init__(self, parent, config_service: ConfigService):
|
|||
|
|
super().__init__(parent)
|
|||
|
|
|
|||
|
|
self.config_service = config_service
|
|||
|
|
|
|||
|
|
self.setWindowTitle("🌑 Калибровочные кадры")
|
|||
|
|
self.setMinimumSize(600, 450)
|
|||
|
|
self.resize(650, 500)
|
|||
|
|
|
|||
|
|
self._create_ui()
|
|||
|
|
self._load_saved_settings()
|
|||
|
|
|
|||
|
|
# Таймер для мигания кнопки "Обзор"
|
|||
|
|
self._browse_blink_timer = None
|
|||
|
|
self._check_folder_path()
|
|||
|
|
|
|||
|
|
def _create_ui(self):
|
|||
|
|
layout = QVBoxLayout(self)
|
|||
|
|
layout.setSpacing(20)
|
|||
|
|
layout.setContentsMargins(25, 25, 25, 25)
|
|||
|
|
|
|||
|
|
# Заголовок
|
|||
|
|
title_label = QLabel("🌑 Калибровочные кадры")
|
|||
|
|
title_font = QFont()
|
|||
|
|
title_font.setPointSize(18)
|
|||
|
|
title_font.setBold(True)
|
|||
|
|
title_label.setFont(title_font)
|
|||
|
|
layout.addWidget(title_label)
|
|||
|
|
|
|||
|
|
# Основная сетка
|
|||
|
|
grid = QGridLayout()
|
|||
|
|
grid.setVerticalSpacing(15)
|
|||
|
|
grid.setHorizontalSpacing(15)
|
|||
|
|
|
|||
|
|
# Строка 0: Камера
|
|||
|
|
camera_label = QLabel("📷 Камера:")
|
|||
|
|
camera_label.setFont(QFont("", 10, QFont.Bold))
|
|||
|
|
grid.addWidget(camera_label, 0, 0)
|
|||
|
|
|
|||
|
|
self.camera_combo = QComboBox()
|
|||
|
|
self.camera_combo.setEditable(True)
|
|||
|
|
self.camera_combo.setMinimumWidth(250)
|
|||
|
|
grid.addWidget(self.camera_combo, 0, 1)
|
|||
|
|
|
|||
|
|
# Строка 1: Папка
|
|||
|
|
folder_label = QLabel("📁 Папка:")
|
|||
|
|
folder_label.setFont(QFont("", 10, QFont.Bold))
|
|||
|
|
grid.addWidget(folder_label, 1, 0)
|
|||
|
|
|
|||
|
|
folder_widget = QWidget()
|
|||
|
|
folder_layout = QHBoxLayout(folder_widget)
|
|||
|
|
folder_layout.setContentsMargins(0, 0, 0, 0)
|
|||
|
|
folder_layout.setSpacing(10)
|
|||
|
|
|
|||
|
|
self.folder_entry = QLineEdit()
|
|||
|
|
self.folder_entry.setPlaceholderText("Выберите папку для сохранения калибровочных кадров")
|
|||
|
|
folder_layout.addWidget(self.folder_entry)
|
|||
|
|
|
|||
|
|
self.browse_button = QPushButton("✨ Обзор")
|
|||
|
|
self.browse_button.setFixedWidth(100)
|
|||
|
|
self.browse_button.clicked.connect(self._browse_folder)
|
|||
|
|
folder_layout.addWidget(self.browse_button)
|
|||
|
|
|
|||
|
|
grid.addWidget(folder_widget, 1, 1)
|
|||
|
|
|
|||
|
|
layout.addLayout(grid)
|
|||
|
|
|
|||
|
|
# Разделитель
|
|||
|
|
separator = QFrame()
|
|||
|
|
separator.setFrameShape(QFrame.HLine)
|
|||
|
|
separator.setStyleSheet("background-color: #333333; max-height: 1px;")
|
|||
|
|
layout.addWidget(separator)
|
|||
|
|
|
|||
|
|
# Кнопки типов кадров
|
|||
|
|
types_layout = QHBoxLayout()
|
|||
|
|
types_layout.setSpacing(20)
|
|||
|
|
types_layout.setAlignment(Qt.AlignCenter)
|
|||
|
|
|
|||
|
|
self.bias_btn = QPushButton("⚪ BIAS")
|
|||
|
|
self.bias_btn.setFixedSize(120, 50)
|
|||
|
|
self.bias_btn.setStyleSheet("""
|
|||
|
|
QPushButton {
|
|||
|
|
background-color: #2196F3;
|
|||
|
|
color: white;
|
|||
|
|
font-weight: bold;
|
|||
|
|
font-size: 14px;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
}
|
|||
|
|
QPushButton:hover {
|
|||
|
|
background-color: #1976D2;
|
|||
|
|
}
|
|||
|
|
""")
|
|||
|
|
self.bias_btn.clicked.connect(lambda: self._open_calibration_type('bias'))
|
|||
|
|
|
|||
|
|
self.dark_btn = QPushButton("🌑 DARK")
|
|||
|
|
self.dark_btn.setFixedSize(120, 50)
|
|||
|
|
self.dark_btn.setStyleSheet("""
|
|||
|
|
QPushButton {
|
|||
|
|
background-color: #9C27B0;
|
|||
|
|
color: white;
|
|||
|
|
font-weight: bold;
|
|||
|
|
font-size: 14px;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
}
|
|||
|
|
QPushButton:hover {
|
|||
|
|
background-color: #7B1FA2;
|
|||
|
|
}
|
|||
|
|
""")
|
|||
|
|
self.dark_btn.clicked.connect(lambda: self._open_calibration_type('dark'))
|
|||
|
|
|
|||
|
|
self.flat_btn = QPushButton("📖 FLAT")
|
|||
|
|
self.flat_btn.setFixedSize(120, 50)
|
|||
|
|
self.flat_btn.setStyleSheet("""
|
|||
|
|
QPushButton {
|
|||
|
|
background-color: #4CAF50;
|
|||
|
|
color: white;
|
|||
|
|
font-weight: bold;
|
|||
|
|
font-size: 14px;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
}
|
|||
|
|
QPushButton:hover {
|
|||
|
|
background-color: #388E3C;
|
|||
|
|
}
|
|||
|
|
""")
|
|||
|
|
self.flat_btn.clicked.connect(lambda: self._open_calibration_type('flat'))
|
|||
|
|
|
|||
|
|
types_layout.addWidget(self.bias_btn)
|
|||
|
|
types_layout.addWidget(self.dark_btn)
|
|||
|
|
types_layout.addWidget(self.flat_btn)
|
|||
|
|
|
|||
|
|
layout.addLayout(types_layout)
|
|||
|
|
|
|||
|
|
# Совет
|
|||
|
|
tips_frame = QFrame()
|
|||
|
|
tips_frame.setStyleSheet("""
|
|||
|
|
QFrame {
|
|||
|
|
background-color: #2d2d2d;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
padding: 10px;
|
|||
|
|
}
|
|||
|
|
""")
|
|||
|
|
tips_layout = QVBoxLayout(tips_frame)
|
|||
|
|
|
|||
|
|
tips_title = QLabel("💡 Совет")
|
|||
|
|
tips_title.setFont(QFont("", 11, QFont.Bold))
|
|||
|
|
tips_layout.addWidget(tips_title)
|
|||
|
|
|
|||
|
|
self.tips_label = QLabel(
|
|||
|
|
"• BIAS снимаются один раз на месяц (можно дома)\n"
|
|||
|
|
"• DARK снимаются на месте съёмки при той же температуре\n"
|
|||
|
|
"• FLAT снимаются после сессии без изменения фокуса"
|
|||
|
|
)
|
|||
|
|
self.tips_label.setWordWrap(True)
|
|||
|
|
tips_layout.addWidget(self.tips_label)
|
|||
|
|
|
|||
|
|
layout.addWidget(tips_frame)
|
|||
|
|
|
|||
|
|
# Кнопки отмена/закрыть
|
|||
|
|
buttons_layout = QHBoxLayout()
|
|||
|
|
buttons_layout.addStretch()
|
|||
|
|
|
|||
|
|
cancel_btn = QPushButton("❌ Отмена")
|
|||
|
|
cancel_btn.clicked.connect(self.reject)
|
|||
|
|
buttons_layout.addWidget(cancel_btn)
|
|||
|
|
|
|||
|
|
layout.addLayout(buttons_layout)
|
|||
|
|
|
|||
|
|
def _load_saved_settings(self):
|
|||
|
|
"""Загружает сохранённые камеры"""
|
|||
|
|
cameras = self.config_service.get_cameras()
|
|||
|
|
if cameras:
|
|||
|
|
self.camera_combo.addItems(cameras)
|
|||
|
|
|
|||
|
|
last_camera = self.config_service.get_last_camera()
|
|||
|
|
if last_camera and last_camera in cameras:
|
|||
|
|
self.camera_combo.setCurrentText(last_camera)
|
|||
|
|
|
|||
|
|
def _browse_folder(self):
|
|||
|
|
"""Выбор папки для калибровочных кадров"""
|
|||
|
|
folder = QFileDialog.getExistingDirectory(self, "Выберите папку для калибровочных кадров")
|
|||
|
|
if folder:
|
|||
|
|
self.folder_entry.setText(folder)
|
|||
|
|
self._stop_browse_blinking()
|
|||
|
|
|
|||
|
|
def _check_folder_path(self):
|
|||
|
|
"""Проверяет, заполнено ли поле пути и запускает мигание если нет"""
|
|||
|
|
if not self.folder_entry.text():
|
|||
|
|
self._start_browse_blinking()
|
|||
|
|
else:
|
|||
|
|
self._stop_browse_blinking()
|
|||
|
|
|
|||
|
|
def _start_browse_blinking(self):
|
|||
|
|
"""Запускает мигание кнопки 'Обзор' зелёным цветом"""
|
|||
|
|
self._browse_blink_timer = QTimer()
|
|||
|
|
self._browse_blink_timer.timeout.connect(self._do_browse_blink)
|
|||
|
|
self._browse_blink_timer.start(500)
|
|||
|
|
|
|||
|
|
def _do_browse_blink(self):
|
|||
|
|
"""Мигание кнопки"""
|
|||
|
|
current_style = self.browse_button.styleSheet()
|
|||
|
|
if "background-color: #4CAF50" in current_style:
|
|||
|
|
self.browse_button.setStyleSheet("""
|
|||
|
|
QPushButton {
|
|||
|
|
background-color: #2196F3;
|
|||
|
|
color: white;
|
|||
|
|
font-weight: bold;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
}
|
|||
|
|
""")
|
|||
|
|
else:
|
|||
|
|
self.browse_button.setStyleSheet("""
|
|||
|
|
QPushButton {
|
|||
|
|
background-color: #4CAF50;
|
|||
|
|
color: white;
|
|||
|
|
font-weight: bold;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
}
|
|||
|
|
""")
|
|||
|
|
|
|||
|
|
def _stop_browse_blinking(self):
|
|||
|
|
"""Останавливает мигание кнопки"""
|
|||
|
|
if self._browse_blink_timer:
|
|||
|
|
self._browse_blink_timer.stop()
|
|||
|
|
self._browse_blink_timer = None
|
|||
|
|
self.browse_button.setStyleSheet("""
|
|||
|
|
QPushButton {
|
|||
|
|
background-color: #2196F3;
|
|||
|
|
color: white;
|
|||
|
|
font-weight: bold;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
}
|
|||
|
|
""")
|
|||
|
|
|
|||
|
|
def _open_calibration_type(self, cal_type: str):
|
|||
|
|
"""Открывает дочернее окно для выбранного типа калибровки"""
|
|||
|
|
if not self.folder_entry.text():
|
|||
|
|
QMessageBox.warning(self, "Внимание", "Сначала выберите папку для сохранения!")
|
|||
|
|
self._start_browse_blinking()
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
camera_name = self.camera_combo.currentText()
|
|||
|
|
if not camera_name:
|
|||
|
|
QMessageBox.warning(self, "Внимание", "Введите или выберите название камеры!")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
from ui.dialogs.calibration_type_dialog import CalibrationTypeDialog
|
|||
|
|
dialog = CalibrationTypeDialog(
|
|||
|
|
self,
|
|||
|
|
cal_type,
|
|||
|
|
self.folder_entry.text(),
|
|||
|
|
camera_name,
|
|||
|
|
self.config_service
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if dialog.exec():
|
|||
|
|
# После успешной съёмки
|
|||
|
|
QMessageBox.information(self, "Успех", f"Съёмка {cal_type.upper()} завершена!")
|
|||
|
|
|
|||
|
|
def reject(self):
|
|||
|
|
"""Закрытие диалога"""
|
|||
|
|
if hasattr(self, '_browse_blink_timer') and self._browse_blink_timer:
|
|||
|
|
self._browse_blink_timer.stop()
|
|||
|
|
super().reject()
|