working logic+working watching files+added calibration feature+instructions
This commit is contained in:
parent
09d181eba8
commit
97ed8217bf
25 changed files with 1743 additions and 192 deletions
281
ui/dialogs/calibration_dialog.py
Normal file
281
ui/dialogs/calibration_dialog.py
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
"""
|
||||
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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue