-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathapp.py
More file actions
144 lines (120 loc) · 5.52 KB
/
Copy pathapp.py
File metadata and controls
144 lines (120 loc) · 5.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import argparse
import sys, os, logging
from pathlib import Path
from datetime import datetime
from PyQt6.QtWidgets import QApplication
from PyQt6.QtCore import QTimer
from interface import AppUI, UIMethods
from instruments import get_camera_backend
from acquisitions.live_stream_handler import LiveStreamHandler
from interface.status_bar.update_notif import set_main_window
from utils import PopupNotifManager
from utils.last_session import save_last_session, load_last_session
from instruments.SLM.udp_holo import close_udp_client
# Configure global logging
def setup_logging():
base_dir = Path(__file__).resolve().parent
logs_dir = base_dir / "logs"
logs_dir.mkdir(exist_ok=True)
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
log_file = logs_dir / f"microTool_{timestamp}.log"
logging.basicConfig(
level=logging.DEBUG,
format='%(levelname)s - %(threadName)s - %(filename)s - %(name)s:%(funcName)s() - %(message)s',
handlers=[
logging.FileHandler(str(log_file)),
logging.StreamHandler() # Also log to console
]
)
logging.info("Starting microTool application")
class microTool():
def __init__(self, camera_backend="xicam", qt_args=None):
# Passing sys.argv so any command-line arguments are forwarded.
self.app = QApplication(qt_args if qt_args is not None else sys.argv)
self.window = AppUI()
"""Camera Control"""
CameraControl, CameraSequences = get_camera_backend(camera_backend)
self.camera_control = CameraControl()
self.camera_sequences = CameraSequences(self.camera_control)
self.camera_sequences.connect_camera()
self.stream_camera = LiveStreamHandler(self.camera_control)
"""UI Methods"""
self.ui_methods = UIMethods(self.window, self.stream_camera)
# Expose ui_methods on the window so AppUI helpers can
# access it (e.g. to update spot markers on the camera view).
self.window.ui_methods = self.ui_methods
"""Connect the UI methods to the image container"""
self.window.image_container.ui_methods = self.ui_methods
"""Set the main window"""
set_main_window(self.window)
# Restore last-session settings (ROI, exposure, acquisition
# configuration) now that the UI and camera controls are
# initialised.
try:
load_last_session(self.window, self.camera_control)
except Exception as e:
logging.error(f"Error loading last session settings: {e}")
# Create a timer to update UI at a reasonable rate
self.ui_update_timer = QTimer()
self.ui_update_timer.timeout.connect(self.ui_methods.update_img_display)
self.ui_update_timer.start(8) # ~30 FPS for UI updates
"""Connect the window close event to our cleanup method"""
self.window.closeEvent = self.cleanup
"""Connect all signals"""
self.window.start_stream.triggered.connect(self.stream_camera.start_stream)
self.window.stop_stream.triggered.connect(self.stream_camera.stop_stream)
self.window.snapshot.triggered.connect(self.ui_methods.handle_snapshot)
self.window.start_recording.triggered.connect(self.ui_methods.handle_recording)
# Experiment acquisition: fixed number of frames
if hasattr(self.window, "start_experiment"):
self.window.start_experiment.triggered.connect(self.ui_methods.handle_experiment)
# TODO: ui_methods should be an attribute of window
self.ui_methods.status_bar_manager.update_all()
def cleanup(self, event):
try:
# Persist front-panel settings for the next run
try:
save_last_session(self.window, self.camera_control)
except Exception as e:
logging.error(f"Error saving last session settings: {e}")
if hasattr(self, 'stream_camera'):
self.stream_camera.cleanup()
if hasattr(self, 'camera_sequences'):
self.camera_sequences.disconnect_camera()
close_udp_client()
event.accept()
logging.info("Resources cleaned up.")
except Exception as e:
logging.error(f"Error during cleanup: {e}")
event.accept()
def __del__(self):
"""Backup cleanup method, but closeEvent handler is the primary cleanup method"""
try:
if hasattr(self, 'stream_camera'):
self.stream_camera.cleanup()
if hasattr(self, 'camera_sequences'):
self.camera_sequences.disconnect_camera()
close_udp_client()
logging.info("Resources cleaned up.")
except Exception as e:
logging.error(f"Error during __del__ cleanup: {e}")
def run(self):
# Start the application in full screen
self.window.showFullScreen()
sys.exit(self.app.exec())
def parse_command_line(argv=None):
"""Parse microTool options and leave unknown options for Qt."""
parser = argparse.ArgumentParser(description="Run microTool")
parser.add_argument(
"--camera",
type=str.lower,
choices=("xicam", "nocam"),
default="xicam",
help="camera backend to use (default: xicam)",
)
return parser.parse_known_args(argv)
if __name__ == "__main__":
args, qt_args = parse_command_line()
setup_logging()
app = microTool(camera_backend=args.camera, qt_args=[sys.argv[0], *qt_args])
app.run()