Astro-Session-Watcher/ui/dialogs/calibration_dialog.py

180 lines
No EOL
6.9 KiB
Python

"""
CalibrationDialog - главный диалог калибровки (tkinter)
"""
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from pathlib import Path
class CalibrationDialog(tk.Toplevel):
"""Главное окно калибровки"""
def __init__(self, parent, config_service):
super().__init__(parent)
self.parent = parent
self.config_service = config_service
self._blink_active = False
self._blink_after_id = None
self.title("Calibration Frames")
self.geometry("650x500")
self.minsize(600, 450)
self.transient(parent)
self.grab_set()
self._create_ui()
self._load_saved_settings()
self._check_folder_path()
self._center_window()
def _create_ui(self):
# Main frame
main_frame = ttk.Frame(self, padding="25")
main_frame.pack(fill='both', expand=True)
# Title
title_label = ttk.Label(main_frame, text="Calibration Frames", font=('Segoe UI', 18, 'bold'))
title_label.pack(pady=(0, 15))
# Separator
ttk.Separator(main_frame, orient='horizontal').pack(fill='x', pady=(0, 15))
# Camera selection
camera_frame = ttk.Frame(main_frame)
camera_frame.pack(fill='x', pady=5)
ttk.Label(camera_frame, text="Camera:", font=('Segoe UI', 10, 'bold')).pack(side='left', padx=(0, 10))
self.camera_combo = ttk.Combobox(camera_frame, width=30)
self.camera_combo.pack(side='left', fill='x', expand=True)
# Folder selection
folder_frame = ttk.Frame(main_frame)
folder_frame.pack(fill='x', pady=5)
ttk.Label(folder_frame, text="Folder:", font=('Segoe UI', 10, 'bold')).pack(side='left', padx=(0, 10))
folder_input_frame = ttk.Frame(folder_frame)
folder_input_frame.pack(side='left', fill='x', expand=True)
self.folder_entry = ttk.Entry(folder_input_frame)
self.folder_entry.pack(side='left', fill='x', expand=True, padx=(0, 10))
self.browse_btn = tk.Button(folder_input_frame, text="Browse...", width=10,
bg='#3c3c3c', fg='#e0e0e0', activebackground='#4c4c4c',
relief='raised', borderwidth=1)
self.browse_btn.config(command=self._browse_folder)
self.browse_btn.pack(side='right')
# Separator
ttk.Separator(main_frame, orient='horizontal').pack(fill='x', pady=15)
# Type buttons
types_frame = ttk.Frame(main_frame)
types_frame.pack(pady=10)
self.bias_btn = tk.Button(types_frame, text="BIAS", font=('Segoe UI', 12, 'bold'),
bg='#2196F3', fg='white', activebackground='#1976D2',
width=12, height=2,
command=lambda: self._open_calibration_type('bias'))
self.bias_btn.pack(side='left', padx=10)
self.dark_btn = tk.Button(types_frame, text="DARK", font=('Segoe UI', 12, 'bold'),
bg='#9C27B0', fg='white', activebackground='#7B1FA2',
width=12, height=2,
command=lambda: self._open_calibration_type('dark'))
self.dark_btn.pack(side='left', padx=10)
self.flat_btn = tk.Button(types_frame, text="FLAT", font=('Segoe UI', 12, 'bold'),
bg='#4CAF50', fg='white', activebackground='#388E3C',
width=12, height=2,
command=lambda: self._open_calibration_type('flat'))
self.flat_btn.pack(side='left', padx=10)
# Tips frame
tips_frame = tk.Frame(main_frame, bg='#2d2d2d', relief='groove', bd=1)
tips_frame.pack(fill='x', pady=15, padx=10)
tk.Label(tips_frame, text="Tips:", font=('Segoe UI', 10, 'bold'),
bg='#2d2d2d', fg='#FFD700').pack(anchor='w', padx=10, pady=(10, 5))
self.tips_label = tk.Label(tips_frame,
text="• BIAS can be taken once a month (at home)\n• DARK must be taken on site at the same temperature\n• FLAT must be taken after session without changing focus",
bg='#2d2d2d', fg='#e0e0e0', justify='left', font=('Segoe UI', 9))
self.tips_label.pack(anchor='w', padx=10, pady=(0, 10))
# Cancel button
btn_frame = ttk.Frame(main_frame)
btn_frame.pack(pady=10)
ttk.Button(btn_frame, text="Cancel", command=self.destroy).pack()
def _load_saved_settings(self):
cameras = self.config_service.get_cameras()
if cameras:
self.camera_combo['values'] = cameras
last_camera = self.config_service.get_last_camera()
if last_camera and last_camera in cameras:
self.camera_combo.set(last_camera)
def _browse_folder(self):
folder = filedialog.askdirectory(title="Select folder for calibration frames")
if folder:
self.folder_entry.delete(0, tk.END)
self.folder_entry.insert(0, folder)
self._stop_blinking()
self.browse_btn.config(bg='#3c3c3c', fg='#e0e0e0')
def _check_folder_path(self):
if not self.folder_entry.get():
self._start_blinking()
else:
self._stop_blinking()
def _start_blinking(self):
self._blink_active = True
def blink():
if not self._blink_active:
return
if self.browse_btn.cget('bg') == '#3c3c3c':
self.browse_btn.config(bg='#f44336', fg='white')
else:
self.browse_btn.config(bg='#3c3c3c', fg='#e0e0e0')
self._blink_after_id = self.after(1500, blink)
blink()
def _stop_blinking(self):
self._blink_active = False
if self._blink_after_id:
self.after_cancel(self._blink_after_id)
self._blink_after_id = None
self.browse_btn.config(bg='#3c3c3c', fg='#e0e0e0')
def _center_window(self):
self.update_idletasks()
x = self.parent.winfo_x() + (self.parent.winfo_width() // 2) - (self.winfo_width() // 2)
y = self.parent.winfo_y() + (self.parent.winfo_height() // 2) - (self.winfo_height() // 2)
self.geometry(f'+{x}+{y}')
def _open_calibration_type(self, cal_type):
if not self.folder_entry.get():
messagebox.showwarning("Warning", "Please select a folder to save calibration frames!", parent=self)
self._start_blinking()
return
camera_name = self.camera_combo.get()
if not camera_name:
messagebox.showwarning("Warning", "Please enter or select a camera name!", parent=self)
return
from ui.dialogs.calibration_type_dialog import CalibrationTypeDialog
dialog = CalibrationTypeDialog(
self,
cal_type,
self.folder_entry.get(),
camera_name,
self.config_service
)
self.wait_window(dialog)