2026-05-07 21:13:00 +03:00
"""
2026-05-09 19:44:16 +03:00
CalibrationDialog - главный диалог калибровки ( tkinter )
2026-05-07 21:13:00 +03:00
"""
2026-05-09 19:44:16 +03:00
import tkinter as tk
from tkinter import ttk , messagebox , filedialog
from pathlib import Path
2026-05-07 21:13:00 +03:00
2026-05-09 19:44:16 +03:00
class CalibrationDialog ( tk . Toplevel ) :
2026-05-07 21:13:00 +03:00
""" Главное окно калибровки """
2026-05-09 19:44:16 +03:00
def __init__ ( self , parent , config_service ) :
2026-05-07 21:13:00 +03:00
super ( ) . __init__ ( parent )
2026-05-09 19:44:16 +03:00
self . parent = parent
2026-05-07 21:13:00 +03:00
self . config_service = config_service
2026-05-09 19:44:16 +03:00
self . _blink_active = False
self . _blink_after_id = None
2026-05-07 21:13:00 +03:00
2026-05-09 19:44:16 +03:00
self . title ( " Calibration Frames " )
self . geometry ( " 650x500 " )
self . minsize ( 600 , 450 )
self . transient ( parent )
self . grab_set ( )
2026-05-07 21:13:00 +03:00
self . _create_ui ( )
self . _load_saved_settings ( )
self . _check_folder_path ( )
2026-05-09 19:44:16 +03:00
self . _center_window ( )
2026-05-07 21:13:00 +03:00
def _create_ui ( self ) :
2026-05-09 19:44:16 +03:00
# 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 )
2026-05-07 21:13:00 +03:00
2026-05-09 19:44:16 +03:00
self . folder_entry = ttk . Entry ( folder_input_frame )
self . folder_entry . pack ( side = ' left ' , fill = ' x ' , expand = True , padx = ( 0 , 10 ) )
2026-05-07 21:13:00 +03:00
2026-05-09 19:44:16 +03:00
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 ' )
2026-05-07 21:13:00 +03:00
2026-05-09 19:44:16 +03:00
# Separator
ttk . Separator ( main_frame , orient = ' horizontal ' ) . pack ( fill = ' x ' , pady = 15 )
2026-05-07 21:13:00 +03:00
2026-05-09 19:44:16 +03:00
# 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 ( )
2026-05-07 21:13:00 +03:00
def _load_saved_settings ( self ) :
cameras = self . config_service . get_cameras ( )
if cameras :
2026-05-09 19:44:16 +03:00
self . camera_combo [ ' values ' ] = cameras
2026-05-07 21:13:00 +03:00
last_camera = self . config_service . get_last_camera ( )
if last_camera and last_camera in cameras :
2026-05-09 19:44:16 +03:00
self . camera_combo . set ( last_camera )
2026-05-07 21:13:00 +03:00
def _browse_folder ( self ) :
2026-05-09 19:44:16 +03:00
folder = filedialog . askdirectory ( title = " Select folder for calibration frames " )
2026-05-07 21:13:00 +03:00
if folder :
2026-05-09 19:44:16 +03:00
self . folder_entry . delete ( 0 , tk . END )
self . folder_entry . insert ( 0 , folder )
self . _stop_blinking ( )
self . browse_btn . config ( bg = ' #3c3c3c ' , fg = ' #e0e0e0 ' )
2026-05-07 21:13:00 +03:00
def _check_folder_path ( self ) :
2026-05-09 19:44:16 +03:00
if not self . folder_entry . get ( ) :
self . _start_blinking ( )
2026-05-07 21:13:00 +03:00
else :
2026-05-09 19:44:16 +03:00
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 ( )
2026-05-07 21:13:00 +03:00
return
2026-05-09 19:44:16 +03:00
camera_name = self . camera_combo . get ( )
2026-05-07 21:13:00 +03:00
if not camera_name :
2026-05-09 19:44:16 +03:00
messagebox . showwarning ( " Warning " , " Please enter or select a camera name! " , parent = self )
2026-05-07 21:13:00 +03:00
return
from ui . dialogs . calibration_type_dialog import CalibrationTypeDialog
dialog = CalibrationTypeDialog (
self ,
cal_type ,
2026-05-09 19:44:16 +03:00
self . folder_entry . get ( ) ,
2026-05-07 21:13:00 +03:00
camera_name ,
self . config_service
)
2026-05-09 19:44:16 +03:00
self . wait_window ( dialog )