From 1d4e65f8ac04fd128f254dfb063fce145c276da4 Mon Sep 17 00:00:00 2001 From: Thomas Ales Date: Tue, 28 Jul 2026 10:02:36 -0500 Subject: [PATCH] Phase 1a: delete dead parallel app stack (~4,600 lines) app.py was an abandoned skeleton (15 'pass # TODO' handlers, loads a deleted .qss); everything else was reachable only from it: ui_mainwindow.py (pyuic6 artifact), sc3-new.ui, motion_worker.py, genesis_worker.py, coherent_hops_laser.py (stubs), scanning/ (dead C# port + unused plan generator), config.json, plus helios_diagnostic.py (sends wrong protocol commands) and helios_terminal.py (worse duplicate of helios_test_app's Terminal tab). hardware/__init__.py no longer wildcard-imports every driver, so the stage driver imports without the uEye camera SDK installed. Co-Authored-By: Claude Fable 5 --- app.py | 615 ------ config.json | 31 - genesis_worker.py | 193 -- hardware/__init__.py | 13 +- hardware/coherent_hops_laser.py | 45 - helios_diagnostic.py | 217 -- helios_terminal.py | 71 - motion_worker.py | 395 ---- sc3-new.ui | 2635 ------------------------- scanning/__init__.py | 3 - scanning/sc3_scan_model.py | 638 ------ scanning/stage_scan_plan_generator.py | 117 -- ui_mainwindow.py | 1321 ------------- 13 files changed, 6 insertions(+), 6288 deletions(-) delete mode 100755 app.py delete mode 100755 config.json delete mode 100755 genesis_worker.py delete mode 100755 hardware/coherent_hops_laser.py delete mode 100755 helios_diagnostic.py delete mode 100755 helios_terminal.py delete mode 100755 motion_worker.py delete mode 100755 sc3-new.ui delete mode 100755 scanning/__init__.py delete mode 100755 scanning/sc3_scan_model.py delete mode 100755 scanning/stage_scan_plan_generator.py delete mode 100755 ui_mainwindow.py diff --git a/app.py b/app.py deleted file mode 100755 index a2e8ca5..0000000 --- a/app.py +++ /dev/null @@ -1,615 +0,0 @@ -#!/usr/bin/env python3 -""" -Scanengine 3 Main Application -""" - -import sys -import json -from pathlib import Path -from PyQt6 import QtWidgets, QtCore -from typing import Optional -import serial.tools.list_ports - -from hardware.coherent_hops_laser import CoherentHOPSLaser, DummyLaser -from hardware.helios_laser import HeliosLaser, PulseMode -from hardware.uc480_camera import UC480Camera, CameraStreamThread -from hardware.pybbd202 import ThorlabsServoDriver, AXIS_X, AXIS_Y, TriggerBitsServo -from hardware.t3r_driver import T3RDriver -from motion_worker import MotionWorker -from scanning.stage_scan_plan_generator import StageScanPlanGenerator -from genesis_worker import GenesisWorker, GenesisCommand -from t3r_control_panel import T3RControlPanel -from ui_mainwindow import Ui_MainWindow - -# Page indices in stackedWidget -PAGE_START = 0 -PAGE_OPTIONS = 1 -PAGE_NEWSCAN = 2 -PAGE_CONTINUESCAN = 3 -PAGE_SCAN_PROGRESS = 4 - -CONFIG_PATH = Path(__file__).parent / "config.json" -DEFAULT_CONFIG = { - "stage": { - "serial_port": "", - "trigger": "Disabled", - "scan_velocity_mm_s": 200.0, - "scan_acceleration_mm_s2": 500.0, - "optical_axis_x_mm": 0.0, - "optical_axis_y_mm": 0.0, - }, - "fpga": { - "serial_port": "", - "pulse_divider": 1, - "rowpack_enabled": False, - }, - "t3r": { - "serial_port": "", - "t_axis_current_ma": 0.0, - "gr_axis_current_ma": 0.0, - "t_axis_microstepping": "Full Step", - "gr_axis_microstepping": "Full Step", - }, - "oscilloscope": { - "ip_address": "", - }, - "generation_laser": { - "serial_port": "", - "pulse_frequency_hz": 125000, - "diode_pump_current_ma": 0.0, - }, - "detection_laser": { - "power_mw": 0.0, - }, - "genesis_laser": { - "com_port": "/dev/ttyUSB0", - }, -} - -# Fixed option lists for combo boxes -TRIGGER_OPTIONS = [ - "Disabled", - "Trigger Out: In Motion", - "Trigger Out: Motion Complete", - "Trigger Out: Max Velocity", - "Trigger Out: High at Max Velocity", -] - -MICROSTEPPING_OPTIONS = [ - "Full Step", - "Half Step", - "1/4 Step", - "1/8 Step", - "1/16 Step", - "1/32 Step", -] - - -class ScanWorker(QtCore.QObject): - """Worker object for handling scanning in a separate thread.""" - - scan_started = QtCore.pyqtSignal() - scan_completed = QtCore.pyqtSignal() - scan_failed = QtCore.pyqtSignal(str) - angle_started = QtCore.pyqtSignal(int, int) - line_started = QtCore.pyqtSignal(int, int, float) - current_progress = QtCore.pyqtSignal(int) - overall_progress = QtCore.pyqtSignal(int) - status_message = QtCore.pyqtSignal(str) - - def __init__(self, scan_params, motion_worker, t3r_driver=None): - super().__init__() - self.scan_params = scan_params - self.motion_worker = motion_worker - self.t3r_driver = t3r_driver - self.should_stop = False - - @QtCore.pyqtSlot() - def run_scan(self): - """Execute the full scanning process.""" - if self.motion_worker: - self.motion_worker.scanning_active = True - try: - self.scan_started.emit() - num_angles = self.scan_params.get("num_angles", 1) - angle_step = 360.0 / num_angles if num_angles > 1 else 0.0 - gr_microsteps = self.scan_params.get("gr_axis_microsteps", 16) - - for angle_idx in range(num_angles): - if self.should_stop: - break - self.angle_started.emit(angle_idx, num_angles) - self.status_message.emit( - f"Scanning angle {angle_idx + 1}/{num_angles}") - # TODO: execute scan lines for this angle via motion_worker - - if angle_idx < num_angles - 1 and angle_step and self.t3r_driver: - if self.t3r_driver.is_open: - self.status_message.emit( - f"Rotating stage {angle_step:.3f}° for next angle…") - self.t3r_driver.rotate_stage( - angle_step, - gr_microsteps, - self.scan_params.get("rotation_velocity", 8000), - self.scan_params.get("rotation_accel", 4000), - ) - # TODO: wait for MOTION_DONE event before proceeding - - self.scan_completed.emit() - except Exception as e: - self.scan_failed.emit(str(e)) - finally: - if self.motion_worker: - self.motion_worker.scanning_active = False - - def stop(self): - self.should_stop = True - - -class MainWindow(QtWidgets.QMainWindow): - def __init__(self): - super().__init__() - self.ui = Ui_MainWindow() - self.ui.setupUi(self) - - self.config = self._load_config() - - # Hardware objects - self.motion_worker: Optional[MotionWorker] = None - self.motion_thread: Optional[QtCore.QThread] = None - self.genesis_worker: Optional[GenesisWorker] = None - self.genesis_thread: Optional[QtCore.QThread] = None - self.camera: Optional[UC480Camera] = None - self.camera_stream: Optional[CameraStreamThread] = None - self.vis_laser: Optional[CoherentHOPSLaser] = None - self.ir_laser: Optional[HeliosLaser] = None - self.scan_worker: Optional[ScanWorker] = None - self.scan_thread: Optional[QtCore.QThread] = None - - # T3R focusing / rotation driver (lives in main thread; reader runs internally) - self.t3r_driver = T3RDriver(self) - self.t3r_panel: Optional[T3RControlPanel] = None - - self._connect_signals() - self._init_genesis_worker() - self._init_t3r_menu() - self.ui.stackedWidget.setCurrentIndex(PAGE_START) - - # ------------------------------------------------------------------ - # Config - # ------------------------------------------------------------------ - - def _load_config(self) -> dict: - if CONFIG_PATH.exists(): - try: - with open(CONFIG_PATH) as f: - cfg = json.load(f) - for section, values in DEFAULT_CONFIG.items(): - cfg.setdefault(section, {}) - for key, val in values.items(): - cfg[section].setdefault(key, val) - return cfg - except Exception: - pass - return {k: dict(v) for k, v in DEFAULT_CONFIG.items()} - - def _save_config(self): - with open(CONFIG_PATH, "w") as f: - json.dump(self.config, f, indent=2) - - # ------------------------------------------------------------------ - # Signal wiring - # ------------------------------------------------------------------ - - def _connect_signals(self): - # Start page - self.ui.start_new_scan_btn.clicked.connect(self._go_to_newscan) - self.ui.resume_scan_btn.clicked.connect(self._go_to_continuescan) - self.ui.edit_options_btn.clicked.connect(self._go_to_options) - - # Options page - self.ui.options_save_settings_btn.clicked.connect(self._on_options_save) - self.ui.options_cancel_btn.clicked.connect(self._go_to_start) - self.ui.stage_test_connection_btn.clicked.connect(self._on_test_stage_connection) - self.ui.fpga_connect_button.clicked.connect(self._on_fpga_connect) - self.ui.fpga_refresh_ports_btn.clicked.connect(self._on_fpga_refresh_ports) - self.ui.refresh_serial_ports_btn.clicked.connect(self._on_refresh_serial_ports) - self.ui.scope_connect_btn.clicked.connect(self._on_scope_connect) - self.ui.generation_connect_button.clicked.connect(self._on_generation_connect) - self.ui.detection_test_btn.clicked.connect(self._on_detection_test) - self.ui.t3r_connect_btn.clicked.connect(self._on_t3r_connect) - self.ui.t3r_refresh_ports_btn.clicked.connect(self._on_t3r_refresh_ports) - - # New scan page - self.ui.newscan_browse_folders_btn.clicked.connect(self._on_newscan_browse) - self.ui.newscan_set_current_as_start_btn.clicked.connect(self._on_newscan_set_start) - self.ui.newscan_get_delta_from_current_btn.clicked.connect(self._on_newscan_get_delta) - self.ui.newscan_toggle_vis_laser_btn.clicked.connect(self._on_newscan_toggle_vis_laser) - self.ui.newscan_continue_to_next_btn.clicked.connect(self._on_newscan_start_scan) - self.ui.newscan_jog_x_pos_btn.pressed.connect(self._on_jog_x_pos_pressed) - self.ui.newscan_jog_x_pos_btn.released.connect(self._on_jog_stop) - self.ui.newscan_jog_x_neg_btn.pressed.connect(self._on_jog_x_neg_pressed) - self.ui.newscan_jog_x_neg_btn.released.connect(self._on_jog_stop) - self.ui.newscan_jog_y_pos_btn.pressed.connect(self._on_jog_y_pos_pressed) - self.ui.newscan_jog_y_pos_btn.released.connect(self._on_jog_stop) - self.ui.newscan_jog_y_neg_btn.pressed.connect(self._on_jog_y_neg_pressed) - self.ui.newscan_jog_y_neg_btn.released.connect(self._on_jog_stop) - - # Continue scan page - self.ui.continuescan_resume_scans.clicked.connect(self._on_resume_scan) - - # Scan progress page - self.ui.abort_scan_button.clicked.connect(self._on_abort_scan) - - # ------------------------------------------------------------------ - # Navigation - # ------------------------------------------------------------------ - - def _go_to_start(self): - self.ui.stackedWidget.setCurrentIndex(PAGE_START) - - def _go_to_options(self): - self._populate_options_page() - self.ui.stackedWidget.setCurrentIndex(PAGE_OPTIONS) - - def _go_to_newscan(self): - self._populate_newscan_page() - self.ui.stackedWidget.setCurrentIndex(PAGE_NEWSCAN) - - def _go_to_continuescan(self): - self._populate_continuescan_page() - self.ui.stackedWidget.setCurrentIndex(PAGE_CONTINUESCAN) - - def _go_to_scan_progress(self): - self.ui.stackedWidget.setCurrentIndex(PAGE_SCAN_PROGRESS) - - # ------------------------------------------------------------------ - # Options page - # ------------------------------------------------------------------ - - def _get_serial_ports(self) -> list[str]: - return sorted(p.device for p in serial.tools.list_ports.comports()) - - def _populate_combo(self, combo: QtWidgets.QComboBox, items: list[str], current: str): - """Refill a combo box, re-selecting `current` if present.""" - combo.blockSignals(True) - combo.clear() - combo.addItems(items) - idx = combo.findText(current) - if idx >= 0: - combo.setCurrentIndex(idx) - elif current: - combo.insertItem(0, current) - combo.setCurrentIndex(0) - combo.blockSignals(False) - - def _populate_options_page(self): - cfg = self.config - ports = self._get_serial_ports() - - # ---- Kinematics tab ---- - self.ui.scan_velocity_edit.setText(str(cfg["stage"]["scan_velocity_mm_s"])) - self.ui.scan_accel_edit.setText(str(cfg["stage"]["scan_acceleration_mm_s2"])) - self.ui.optical_axis_x_edit.setText(str(cfg["stage"]["optical_axis_x_mm"])) - self.ui.optical_axis_y_edit.setText(str(cfg["stage"]["optical_axis_y_mm"])) - self.ui.stage_serial_edit.setText(cfg["stage"]["serial_port"]) - self._populate_combo(self.ui.stage_trigger_combo, TRIGGER_OPTIONS, cfg["stage"]["trigger"]) - - # ---- Detection / VIS tab ---- - self.ui.detection_power_edit.setText(str(cfg["detection_laser"]["power_mw"])) - - # ---- Generation / IR tab ---- - self._populate_combo(self.ui.comboBox, ports, cfg["generation_laser"]["serial_port"]) - self.ui.generation_pulse_freq_edit.setText(str(cfg["generation_laser"]["pulse_frequency_hz"])) - self.ui.diode_pump_current_edit.setText(str(cfg["generation_laser"]["diode_pump_current_ma"])) - - # ---- PulseDecimator tab ---- - self._populate_combo(self.ui.fpga_serial_port, ports, cfg["fpga"]["serial_port"]) - self.ui.fpga_divider_value_edit.setText(str(cfg["fpga"]["pulse_divider"])) - self.ui.checkBox.setChecked(cfg["fpga"]["rowpack_enabled"]) - - # ---- T3R-SL tab ---- - self._populate_combo(self.ui.t3r_serial_port_edit, ports, cfg["t3r"]["serial_port"]) - self.ui.lineEdit.setText(str(cfg["t3r"]["t_axis_current_ma"])) - self.ui.lineEdit_2.setText(str(cfg["t3r"]["gr_axis_current_ma"])) - self._populate_combo(self.ui.comboBox_2, MICROSTEPPING_OPTIONS, cfg["t3r"]["t_axis_microstepping"]) - self._populate_combo(self.ui.comboBox_3, MICROSTEPPING_OPTIONS, cfg["t3r"]["gr_axis_microstepping"]) - - # ---- Oscilloscope tab ---- - self.ui.scope_ip_address_edit.setText(cfg["oscilloscope"]["ip_address"]) - - def _on_options_save(self): - try: - # Kinematics - self.config["stage"]["scan_velocity_mm_s"] = float(self.ui.scan_velocity_edit.text()) - self.config["stage"]["scan_acceleration_mm_s2"] = float(self.ui.scan_accel_edit.text()) - self.config["stage"]["optical_axis_x_mm"] = float(self.ui.optical_axis_x_edit.text()) - self.config["stage"]["optical_axis_y_mm"] = float(self.ui.optical_axis_y_edit.text()) - self.config["stage"]["serial_port"] = self.ui.stage_serial_edit.text().strip() - self.config["stage"]["trigger"] = self.ui.stage_trigger_combo.currentText() - - # Detection / VIS - self.config["detection_laser"]["power_mw"] = float(self.ui.detection_power_edit.text()) - - # Generation / IR - self.config["generation_laser"]["serial_port"] = self.ui.comboBox.currentText() - self.config["generation_laser"]["pulse_frequency_hz"] = int(self.ui.generation_pulse_freq_edit.text()) - self.config["generation_laser"]["diode_pump_current_ma"] = float(self.ui.diode_pump_current_edit.text()) - - # PulseDecimator - self.config["fpga"]["serial_port"] = self.ui.fpga_serial_port.currentText() - self.config["fpga"]["pulse_divider"] = int(self.ui.fpga_divider_value_edit.text()) - self.config["fpga"]["rowpack_enabled"] = self.ui.checkBox.isChecked() - - # T3R-SL - self.config["t3r"]["serial_port"] = self.ui.t3r_serial_port_edit.currentText() - self.config["t3r"]["t_axis_current_ma"] = float(self.ui.lineEdit.text()) - self.config["t3r"]["gr_axis_current_ma"] = float(self.ui.lineEdit_2.text()) - self.config["t3r"]["t_axis_microstepping"] = self.ui.comboBox_2.currentText() - self.config["t3r"]["gr_axis_microstepping"] = self.ui.comboBox_3.currentText() - - # Oscilloscope - self.config["oscilloscope"]["ip_address"] = self.ui.scope_ip_address_edit.text().strip() - - except ValueError as e: - QtWidgets.QMessageBox.warning(self, "Invalid input", str(e)) - return - - self._save_config() - self._go_to_start() - - def _refresh_serial_ports_for_combos(self, *combos: QtWidgets.QComboBox): - """Re-populate serial port combos, preserving current selections.""" - ports = self._get_serial_ports() - for combo in combos: - self._populate_combo(combo, ports, combo.currentText()) - - def _on_refresh_serial_ports(self): - self._refresh_serial_ports_for_combos(self.ui.comboBox) - - def _on_fpga_refresh_ports(self): - self._refresh_serial_ports_for_combos(self.ui.fpga_serial_port) - - def _on_t3r_refresh_ports(self): - self._refresh_serial_ports_for_combos(self.ui.t3r_serial_port_edit) - - def _on_test_stage_connection(self): - pass # TODO - - def _on_fpga_connect(self): - pass # TODO - - def _on_scope_connect(self): - pass # TODO - - def _on_generation_connect(self): - pass # TODO - - def _on_detection_test(self): - pass # TODO - - def _on_t3r_connect(self): - port = self.ui.t3r_serial_port_edit.currentText().split(" ")[0] - self._show_t3r_panel() - if port and not self.t3r_driver.is_open: - try: - self.t3r_driver.connect(port) - except Exception as exc: - QtWidgets.QMessageBox.warning(self, "T3R Connect", str(exc)) - - # ------------------------------------------------------------------ - # New scan page - # ------------------------------------------------------------------ - - def _populate_newscan_page(self): - self.ui.newscan_save_directory_edit.setText(str(Path.home() / "scans")) - - def _on_newscan_browse(self): - directory = QtWidgets.QFileDialog.getExistingDirectory(self, "Select save directory") - if directory: - self.ui.newscan_save_directory_edit.setText(directory) - - def _on_newscan_set_start(self): - pass # TODO: capture current stage position as scan start - - def _on_newscan_get_delta(self): - pass # TODO: capture current stage position as scan end (compute delta) - - def _on_newscan_toggle_vis_laser(self): - pass # TODO: toggle vis laser on/off - - def _on_newscan_start_scan(self): - scan_params = self._build_scan_params() - if scan_params is None: - return - self._start_scan(scan_params) - - def _build_scan_params(self) -> Optional[dict]: - """Read newscan page widgets and return scan parameter dict, or None on error.""" - try: - x_start = float(self.ui.newscan_start_x_coord_edit.text()) - y_start = float(self.ui.newscan_start_y_coord_edit.text()) - x_delta = float(self.ui.newscan_delta_x_coord_edit.text()) - y_delta = float(self.ui.newscan_delta_y_coord_edit.text()) - except ValueError: - QtWidgets.QMessageBox.warning(self, "Invalid input", "Scan coordinates must be numbers.") - return None - - pixel_size_map = { - self.ui.newscan_50_micron_radio: 0.05, - self.ui.newscan_100_micron_radio: 0.10, - self.ui.newscan_250_micron_radio: 0.25, - } - row_spacing = next( - (v for btn, v in pixel_size_map.items() if btn.isChecked()), 0.10 - ) - - return { - "x_start_mm": x_start, - "y_start_mm": y_start, - "x_delta_mm": x_delta, - "y_delta_mm": y_delta, - "row_spacing_mm": row_spacing, - "num_angles": int(self.ui.newscan_num_angles_combo.currentText()), - "friendly_name": self.ui.newscan_friendly_name_edit.text(), - "file_prefix": self.ui.newcsan_file_prefix_edit.text(), - "save_directory": self.ui.newscan_save_directory_edit.text(), - "scan_velocity_mm_s": self.config["stage"]["scan_velocity_mm_s"], - "scan_acceleration_mm_s2": self.config["stage"]["scan_acceleration_mm_s2"], - # T3R rotation between angles (GR-axis, ch1) - "gr_axis_microsteps": 16, - "rotation_velocity": 8000, - "rotation_accel": 4000, - } - - # ------------------------------------------------------------------ - # Jog controls - # ------------------------------------------------------------------ - - def _on_jog_x_pos_pressed(self): - pass # TODO - - def _on_jog_x_neg_pressed(self): - pass # TODO - - def _on_jog_y_pos_pressed(self): - pass # TODO - - def _on_jog_y_neg_pressed(self): - pass # TODO - - def _on_jog_stop(self): - pass # TODO - - # ------------------------------------------------------------------ - # Continue scan page - # ------------------------------------------------------------------ - - def _populate_continuescan_page(self): - pass # TODO: populate list of interrupted scans - - def _on_resume_scan(self): - pass # TODO: resume selected scan - - # ------------------------------------------------------------------ - # Scan execution - # ------------------------------------------------------------------ - - def _start_scan(self, scan_params: dict): - self.scan_thread = QtCore.QThread() - self.scan_worker = ScanWorker(scan_params, self.motion_worker, self.t3r_driver) - self.scan_worker.moveToThread(self.scan_thread) - - self.scan_thread.started.connect(self.scan_worker.run_scan) - self.scan_worker.scan_started.connect(self._on_scan_started) - self.scan_worker.scan_completed.connect(self._on_scan_completed) - self.scan_worker.scan_failed.connect(self._on_scan_failed) - self.scan_worker.current_progress.connect(self.ui.scanning_scan_progbar.setValue) - self.scan_worker.overall_progress.connect(self.ui.scanning_overall_progbar.setValue) - self.scan_worker.status_message.connect(self.ui.scanning_stage_state_label.setText) - - self._go_to_scan_progress() - self.scan_thread.start() - - @QtCore.pyqtSlot() - def _on_scan_started(self): - self.ui.abort_scan_button.setEnabled(True) - - @QtCore.pyqtSlot() - def _on_scan_completed(self): - self._cleanup_scan_thread() - QtWidgets.QMessageBox.information(self, "Scan complete", "Scan finished successfully.") - self._go_to_start() - - @QtCore.pyqtSlot(str) - def _on_scan_failed(self, error: str): - self._cleanup_scan_thread() - QtWidgets.QMessageBox.critical(self, "Scan failed", error) - self._go_to_start() - - def _on_abort_scan(self): - if self.scan_worker: - self.scan_worker.stop() - - def _cleanup_scan_thread(self): - if self.scan_thread: - self.scan_thread.quit() - self.scan_thread.wait() - self.scan_thread = None - self.scan_worker = None - - # ------------------------------------------------------------------ - # T3R focusing / rotation panel - # ------------------------------------------------------------------ - - def _init_t3r_menu(self): - """Add a Hardware menu with a T3R panel toggle action.""" - hw_menu = self.menuBar().addMenu("Hardware") - self._t3r_action = hw_menu.addAction("T3R Focusing && Rotation…") - self._t3r_action.setCheckable(True) - self._t3r_action.setShortcut("Ctrl+T") - self._t3r_action.triggered.connect(self._on_t3r_action_toggled) - - def _show_t3r_panel(self): - if self.t3r_panel is None: - self.t3r_panel = T3RControlPanel(self.t3r_driver, self) - self.t3r_panel.finished.connect( - lambda: self._t3r_action.setChecked(False)) - self.t3r_panel.show() - self.t3r_panel.raise_() - self._t3r_action.setChecked(True) - - def _on_t3r_action_toggled(self, checked: bool): - if checked: - self._show_t3r_panel() - elif self.t3r_panel is not None: - self.t3r_panel.hide() - - # ------------------------------------------------------------------ - # Genesis laser worker - # ------------------------------------------------------------------ - - def _init_genesis_worker(self): - com_port = self.config.get("genesis_laser", {}).get("com_port", "/dev/ttyUSB0") - self.genesis_worker = GenesisWorker(com_port) - self.genesis_thread = QtCore.QThread() - self.genesis_worker.moveToThread(self.genesis_thread) - self.genesis_thread.started.connect(self.genesis_worker.run) - self.genesis_thread.start() - - def _cleanup_genesis_worker(self): - if self.genesis_worker: - self.genesis_worker.stop() - if self.genesis_thread: - self.genesis_thread.quit() - self.genesis_thread.wait() - self.genesis_worker = None - self.genesis_thread = None - - # ------------------------------------------------------------------ - # Lifecycle - # ------------------------------------------------------------------ - - def closeEvent(self, event): - if self.t3r_driver.is_open: - self.t3r_driver.disconnect() - self._cleanup_genesis_worker() - self._cleanup_scan_thread() - if self.motion_thread: - self.motion_thread.quit() - self.motion_thread.wait() - super().closeEvent(event) - - -def main(): - app = QtWidgets.QApplication(sys.argv) - qss_path = Path(__file__).parent / "app_style.qss" - if qss_path.exists(): - app.setStyleSheet(qss_path.read_text()) - window = MainWindow() - window.show() - sys.exit(app.exec()) - - -if __name__ == "__main__": - main() diff --git a/config.json b/config.json deleted file mode 100755 index cc76fdc..0000000 --- a/config.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "genesis_laser": { - "com_port": "/dev/ttyUSB0" - }, - "detection_laser": { - "scan_power_mw": "125" - }, - "generation_laser": { - "com_port": "/dev/ttyACM0", - "frequency_hz": "20000", - "pump_diode_current_ma": "750", - "focusing_frequency_hz": "20000", - "focusing_pump_current_ma": "300" - }, - "scanning_stage": { - "scan_velocity_mm_s": "200", - "scan_acceleration_mm_s2": "1500", - "x_trigger_mode": 6, - "y_trigger_mode": 0, - "optical_axis_x_mm": "55", - "optical_axis_y_mm": "37.5" - }, - "t3r": { - "com_port": "/dev/ttyUSB0" - }, - "oscilloscope": { - "socket_address": "192.168.0.1", - "scratch_directory": "/opt/", - "save_location": "pc" - } -} \ No newline at end of file diff --git a/genesis_worker.py b/genesis_worker.py deleted file mode 100755 index b1d9995..0000000 --- a/genesis_worker.py +++ /dev/null @@ -1,193 +0,0 @@ -""" -Genesis Laser Worker Thread - -Manages Genesis laser connection in a separate thread to keep the UI responsive. -Provides async querying and status monitoring via Qt signals. -""" - -from PyQt6 import QtCore -from hardware.genesis_core import SerialComm, I2CProtocol, I2CDevices, LaserControl -import queue -import time -from typing import Optional - - -class GenesisCommand: - """Represents a genesis laser command""" - def __init__(self, cmd_type: str, **kwargs): - self.cmd_type = cmd_type - self.params = kwargs - - -class GenesisWorker(QtCore.QObject): - """ - Worker object for handling Genesis laser control in a separate thread. - - Signals: - connected: Emitted when laser connects successfully - disconnected: Emitted when laser disconnects - connection_failed: Emitted when connection fails (error_msg: str) - laser_info_updated: Emitted with laser status - query_completed: Emitted when a query operation completes (result: dict) - error_occurred: Emitted when an error occurs (error_msg: str) - """ - - # Signals - connected = QtCore.pyqtSignal() - disconnected = QtCore.pyqtSignal() - connection_failed = QtCore.pyqtSignal(str) - laser_info_updated = QtCore.pyqtSignal(dict) # Status information - query_completed = QtCore.pyqtSignal(dict) # Query result - error_occurred = QtCore.pyqtSignal(str) # Error message - - def __init__(self, port: str = "/dev/ttyUSB0", baudrate: int = 9600): - super().__init__() - self.serial_comm = SerialComm() - self.i2c_protocol = I2CProtocol(self.serial_comm) - self.i2c_devices = I2CDevices(self.i2c_protocol) - self.laser_control = LaserControl(self.i2c_devices) - - self.port = port - self.baudrate = baudrate - self.is_connected = False - self.command_queue = queue.Queue() - self.running = True - - # Last known laser state - self.last_laser_state = {} - - # Update interval for status polling - self.last_status_update_time = 0 - self.status_update_interval = 1.0 # seconds - - @QtCore.pyqtSlot() - def run(self): - """Main worker loop - processes commands from queue""" - print(f"Genesis laser worker thread started - connecting to {self.port}") - - # Try to connect on startup - if self.connect(): - self.connected.emit() - else: - error_msg = f"Failed to connect to Genesis laser on {self.port}" - print(error_msg) - self.connection_failed.emit(error_msg) - - while self.running: - try: - # Check for commands with timeout to allow periodic status updates - try: - cmd = self.command_queue.get(timeout=0.05) # 50ms timeout - self.process_command(cmd) - except queue.Empty: - pass - - # Periodically update status if connected - if self.is_connected: - current_time = time.time() - if current_time - self.last_status_update_time >= self.status_update_interval: - self.update_laser_status() - self.last_status_update_time = current_time - - except Exception as e: - print(f"Error in genesis worker loop: {e}") - self.error_occurred.emit(str(e)) - - # Cleanup on exit - self.disconnect() - print("Genesis laser worker thread stopped") - - def connect(self) -> bool: - """Establish connection to the laser""" - try: - if self.serial_comm.connect(self.port, self.baudrate): - self.is_connected = True - print(f"Connected to Genesis laser on {self.port}") - return True - else: - print(f"Failed to open serial port {self.port}") - return False - except Exception as e: - print(f"Connection error: {e}") - return False - - def disconnect(self): - """Disconnect from the laser""" - if self.is_connected: - self.serial_comm.disconnect() - self.is_connected = False - self.disconnected.emit() - print("Disconnected from Genesis laser") - - def process_command(self, cmd: GenesisCommand): - """Process a command from the queue""" - if not self.is_connected: - self.error_occurred.emit("Laser not connected") - return - - try: - if cmd.cmd_type == "query_all": - result = self.query_all_status() - self.query_completed.emit(result) - elif cmd.cmd_type == "query_current": - result = {"current": self.laser_control.get_current_actual()} - self.query_completed.emit(result) - elif cmd.cmd_type == "query_interlock": - result = {"interlock": self.laser_control.get_interlock_status()} - self.query_completed.emit(result) - elif cmd.cmd_type == "set_current": - value = cmd.params.get("value", 0) - success = self.laser_control.set_current(int(value)) - self.query_completed.emit({"success": success}) - elif cmd.cmd_type == "set_shutter": - state = cmd.params.get("state", False) - success = self.laser_control.set_shutter(state) - self.query_completed.emit({"success": success}) - else: - self.error_occurred.emit(f"Unknown command: {cmd.cmd_type}") - except Exception as e: - self.error_occurred.emit(f"Command execution error: {e}") - - def update_laser_status(self): - """Query and emit current laser status""" - if not self.is_connected: - return - - try: - status = { - "connected": True, - "current_actual": self.laser_control.get_current_actual(), - "interlock_status": self.laser_control.get_interlock_status(), - "ldd_enable_status": self.laser_control.get_ldd_enable_status(), - "psglue_in_status": self.laser_control.get_psglue_in_status(), - "psglue_out_status": self.laser_control.get_psglue_out_status(), - "head_dio_status": self.laser_control.get_head_dio_status(), - } - - # Only emit if something changed - if status != self.last_laser_state: - self.last_laser_state = status - self.laser_info_updated.emit(status) - - except Exception as e: - print(f"Error updating laser status: {e}") - - def query_all_status(self) -> dict: - """Query all laser status information""" - return { - "connected": True, - "current_actual": self.laser_control.get_current_actual(), - "interlock_status": self.laser_control.get_interlock_status(), - "ldd_enable_status": self.laser_control.get_ldd_enable_status(), - "psglue_in_status": self.laser_control.get_psglue_in_status(), - "psglue_out_status": self.laser_control.get_psglue_out_status(), - "head_dio_status": self.laser_control.get_head_dio_status(), - } - - def queue_command(self, cmd: GenesisCommand): - """Queue a command for execution""" - self.command_queue.put(cmd) - - def stop(self): - """Stop the worker thread""" - self.running = False diff --git a/hardware/__init__.py b/hardware/__init__.py index 5d8a544..f264ae7 100755 --- a/hardware/__init__.py +++ b/hardware/__init__.py @@ -1,7 +1,6 @@ -"""Hardware driver modules for ScanEngine-3""" -from .pybbd202 import ThorlabsServoDriver, TriggerBitsServo, AXIS_X, AXIS_Y, CONTROLLER -from .t3r_driver import T3RDriver -from .uc480_camera import * -from .tektronix_base import * -from .coherent_hops_laser import * -from .genesis_core import * +"""Hardware driver modules for ScanEngine-3. + +Import drivers by module (e.g. ``from hardware.t3r_driver import T3RDriver``); +nothing is re-exported here so that importing one driver never drags in +another driver's SDK (the uEye camera stack in particular). +""" diff --git a/hardware/coherent_hops_laser.py b/hardware/coherent_hops_laser.py deleted file mode 100755 index aaf0c1d..0000000 --- a/hardware/coherent_hops_laser.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -Coherent HOPS Laser Driver - Stub Module -This is a temporary stub to allow testing camera integration. -""" - - -class CoherentHOPSLaser: - """Stub class for Coherent HOPS Laser""" - pass - - -class DummyLaser: - """Dummy laser for testing without hardware""" - - def connect(self): - """Simulate connection""" - pass - - def disconnect(self): - """Simulate disconnection""" - pass - - def get_hardware_id(self): - """Return simulated hardware ID""" - return "SIM-12345" - - def get_laser_model(self): - """Return simulated model""" - return "Genesis Simulator" - - def get_interlock_status(self): - """Return simulated interlock status""" - return "OK" - - def get_key_switch_status(self): - """Return simulated key switch status""" - return "ON" - - def get_temperature_main(self): - """Return simulated main temperature""" - return 25.5 - - def get_temperature_eta(self): - """Return simulated ETA temperature""" - return 26.3 diff --git a/helios_diagnostic.py b/helios_diagnostic.py deleted file mode 100755 index 0a93e32..0000000 --- a/helios_diagnostic.py +++ /dev/null @@ -1,217 +0,0 @@ -#!/usr/bin/env python3 -""" -Helios Laser Serial Communication Diagnostic Tool -Helps troubleshoot communication issues with the Helios laser. -""" - -import serial -import time -import sys - -def test_port(port, baudrate=9600): - """Test basic communication on a serial port.""" - print(f"\n{'='*60}") - print(f"Testing {port} at {baudrate} baud") - print(f"{'='*60}") - - try: - ser = serial.Serial( - port=port, - baudrate=baudrate, - bytesize=serial.EIGHTBITS, - parity=serial.PARITY_NONE, - stopbits=serial.STOPBITS_ONE, - timeout=1.0 - ) - print(f"✓ Port opened successfully") - time.sleep(0.1) - - # Try to query the controller serial number - print("\nSending: 'SN?'") - ser.write(b'SN?\r') - time.sleep(0.5) - - response = ser.readline().decode('ascii', errors='replace').strip() - print(f"Response: '{response}'") - - if response: - print(f"✓ Got response: {response}") - return True, response - else: - print(f"✗ No response received") - - # Try head serial number - print("\nSending: 'HSN?'") - ser.write(b'HSN?\r') - time.sleep(0.5) - - response = ser.readline().decode('ascii', errors='replace').strip() - print(f"Response: '{response}'") - - if response: - print(f"✓ Got response: {response}") - ser.close() - return True, response - else: - print(f"✗ No response received") - - # Try laser enable status - print("\nSending: 'LE?'") - ser.write(b'LE?\r') - time.sleep(0.5) - - response = ser.readline().decode('ascii', errors='replace').strip() - print(f"Response: '{response}'") - - if response: - print(f"✓ Got response: {response}") - ser.close() - return True, response - else: - print(f"✗ No response received") - - ser.close() - return False, "No response to any query" - - except Exception as e: - print(f"✗ Error: {e}") - return False, str(e) - - -def test_raw_communication(port, baudrate=9600): - """Test raw serial communication and display hex.""" - print(f"\n{'='*60}") - print(f"Raw Communication Test: {port} at {baudrate} baud") - print(f"{'='*60}") - - try: - ser = serial.Serial( - port=port, - baudrate=baudrate, - bytesize=serial.EIGHTBITS, - parity=serial.PARITY_NONE, - stopbits=serial.STOPBITS_ONE, - timeout=2.0 - ) - print(f"✓ Port opened successfully") - time.sleep(0.2) - - # Send a simple query - command = b'SN?\r' - print(f"\nSending command (hex): {command.hex()}") - print(f"Sending command (ascii): {command}") - - ser.write(command) - time.sleep(0.5) - - # Read response byte by byte - response = b'' - while True: - byte = ser.read(1) - if not byte: - break - response += byte - if byte == b'\n' or byte == b'\r': - break - - print(f"\nRaw response (hex): {response.hex()}") - print(f"Raw response (ascii): {response}") - print(f"Response length: {len(response)} bytes") - - # Check for common issues - if not response: - print("✗ No response - device may not be responding or wrong baud rate") - elif response == b'\r' or response == b'\n': - print("⚠ Only got line terminator - device may be echoing but not responding to command") - else: - print("✓ Got a response!") - - ser.close() - return True - - except Exception as e: - print(f"✗ Error: {e}") - return False - - -def test_echo(port, baudrate=9600): - """Test if the device echoes commands back.""" - print(f"\n{'='*60}") - print(f"Echo Test: {port} at {baudrate} baud") - print(f"{'='*60}") - - try: - ser = serial.Serial( - port=port, - baudrate=baudrate, - bytesize=serial.EIGHTBITS, - parity=serial.PARITY_NONE, - stopbits=serial.STOPBITS_ONE, - timeout=1.0 - ) - - # Send a test character - test_char = b'T' - print(f"Sending test character: {test_char}") - ser.write(test_char) - time.sleep(0.1) - - echo = ser.read(1) - if echo == test_char: - print(f"✓ Device echoes input") - elif echo: - print(f"⚠ Device sent something but not the same: {echo}") - else: - print(f"✗ No echo") - - ser.close() - return True - - except Exception as e: - print(f"✗ Error: {e}") - return False - - -def main(): - """Run diagnostic tests.""" - port = "/dev/ttyUSB2" - - if len(sys.argv) > 1: - port = sys.argv[1] - - print(f"\n{'#'*60}") - print(f"# Helios Laser Serial Diagnostic Tool") - print(f"# Testing port: {port}") - print(f"{'#'*60}") - - # Test standard baud rate - success, response = test_port(port, 9600) - - if not success: - print("\n" + "="*60) - print("Standard baud rate (9600) failed. Trying alternatives...") - print("="*60) - - # Try other common baud rates - for baudrate in [115200, 19200, 4800, 2400]: - success, response = test_port(port, baudrate) - if success: - print(f"\n✓ SUCCESS! Device responds at {baudrate} baud") - break - else: - print(f"\n✓ SUCCESS! Device responds at 9600 baud") - - # Run additional diagnostics - print("\n") - test_raw_communication(port, 9600) - - print("\n") - test_echo(port, 9600) - - print(f"\n{'#'*60}") - print("# Diagnostic Tests Complete") - print(f"{'#'*60}\n") - - -if __name__ == "__main__": - main() diff --git a/helios_terminal.py b/helios_terminal.py deleted file mode 100755 index c4157c1..0000000 --- a/helios_terminal.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python3 -""" -Simple serial terminal for manual Helios laser testing. -Allows sending raw commands and viewing responses. -""" - -import serial -import sys -from threading import Thread -import time - -def read_from_port(ser): - """Read data from serial port and display it.""" - while True: - try: - if ser.in_waiting: - data = ser.read(ser.in_waiting) - print(f"\n[RX] {data.decode('ascii', errors='replace')}", end='') - sys.stdout.flush() - except: - break - time.sleep(0.01) - -def main(): - """Run interactive serial terminal.""" - port = "/dev/ttyUSB2" - - if len(sys.argv) > 1: - port = sys.argv[1] - - try: - ser = serial.Serial( - port=port, - baudrate=9600, - bytesize=serial.EIGHTBITS, - parity=serial.PARITY_NONE, - stopbits=serial.STOPBITS_ONE, - timeout=0.1 - ) - print(f"Connected to {port} at 9600 baud") - print("Type commands and press Enter. Type 'quit' to exit.\n") - - # Start reader thread - reader_thread = Thread(target=read_from_port, args=(ser,), daemon=True) - reader_thread.start() - - while True: - try: - user_input = input("[TX] ") - if user_input.lower() == 'quit': - break - - # Send command with carriage return - command = user_input + '\r' - ser.write(command.encode('ascii')) - time.sleep(0.1) - - except KeyboardInterrupt: - break - except Exception as e: - print(f"Error: {e}") - - ser.close() - print("\nDisconnected") - - except Exception as e: - print(f"Failed to open {port}: {e}") - sys.exit(1) - -if __name__ == "__main__": - main() diff --git a/motion_worker.py b/motion_worker.py deleted file mode 100755 index 56757a1..0000000 --- a/motion_worker.py +++ /dev/null @@ -1,395 +0,0 @@ -""" -Motion Controller Worker Thread - -Handles all motion control operations in a separate thread to keep the UI responsive. -Provides async command queueing and position updates via Qt signals. -""" - -from PyQt6 import QtCore -from hardware.pybbd202 import ThorlabsServoDriver, AXIS_X, AXIS_Y -import queue -import time -from typing import Optional, Dict, Any - - -class MotionCommand: - """Represents a motion command""" - def __init__(self, cmd_type: str, **kwargs): - self.cmd_type = cmd_type - self.params = kwargs - - -class MotionWorker(QtCore.QObject): - """ - Worker object for handling motion control in a separate thread. - - Signals: - connected: Emitted when controller connects successfully - disconnected: Emitted when controller disconnects - connection_failed: Emitted when connection fails (error_msg: str) - position_updated: Emitted when position changes (x: float, y: float) - homed_status: Emitted with home status (x_homed: bool, y_homed: bool) - move_completed: Emitted when a move completes (axis: str) - error_occurred: Emitted when an error occurs (error_msg: str) - """ - - # Signals - connected = QtCore.pyqtSignal() - disconnected = QtCore.pyqtSignal() - connection_failed = QtCore.pyqtSignal(str) - position_updated = QtCore.pyqtSignal(float, float) # x, y in mm - homed_status = QtCore.pyqtSignal(bool, bool) # x_homed, y_homed - motion_status = QtCore.pyqtSignal(bool, bool) # x_moving, y_moving - move_completed = QtCore.pyqtSignal(str) # axis name - error_occurred = QtCore.pyqtSignal(str) # error message - - def __init__(self): - super().__init__() - self.controller: Optional[ThorlabsServoDriver] = None - self.is_connected = False - self.command_queue = queue.Queue() - self.running = True - - # Default parameters - self.jog_speed = 20.0 # mm/s - self.acceleration = 50.0 # mm/s^2 - self.step_size = 1.0 # mm - - # Position tracking - self.last_x = None - self.last_y = None - - # Status tracking - self.last_x_homed = None - self.last_y_homed = None - self.last_x_moving = None - self.last_y_moving = None - - # Position update throttling - self.last_position_update_time = 0 - self.position_update_interval = 0.2 # seconds between position reads - - # Flag to pause polling during scanning (scan worker handles its own position queries) - self.scanning_active = False - - @QtCore.pyqtSlot() - def run(self): - """Main worker loop - processes commands from queue""" - print("Motion worker thread started") - - while self.running: - try: - # Check for commands with timeout to allow periodic position updates - try: - cmd = self.command_queue.get(timeout=0.05) # 50ms timeout - self.process_command(cmd) - except queue.Empty: - pass - - # Periodically update position and status if connected - # Skip updates during scanning - scan worker handles its own position queries - if self.is_connected and self.controller and not self.scanning_active: - self.update_position() - self.update_home_status() - self.update_motion_status() - - except Exception as e: - print(f"Error in motion worker loop: {e}") - self.error_occurred.emit(str(e)) - - # Cleanup on exit - if self.controller: - try: - self.controller.disconnect() - except: - pass - - print("Motion worker thread stopped") - - def process_command(self, cmd: MotionCommand): - """Process a motion command""" - try: - if cmd.cmd_type == 'connect': - self.do_connect() - elif cmd.cmd_type == 'disconnect': - self.do_disconnect() - elif cmd.cmd_type == 'jog': - self.do_jog(cmd.params['axis'], cmd.params['direction']) - elif cmd.cmd_type == 'home': - self.do_home(cmd.params['axis']) - elif cmd.cmd_type == 'set_velocity': - self.do_set_velocity(cmd.params['speed'], cmd.params['accel']) - elif cmd.cmd_type == 'set_step_size': - self.step_size = cmd.params['step_size'] - elif cmd.cmd_type == 'set_axis_enable': - self.do_set_axis_enable(cmd.params['axis'], cmd.params['enabled']) - elif cmd.cmd_type == 'stop': - self.running = False - - except Exception as e: - print(f"Error processing command {cmd.cmd_type}: {e}") - self.error_occurred.emit(f"Command '{cmd.cmd_type}' failed: {str(e)}") - - def do_connect(self): - """Connect to the motion controller""" - try: - self.controller = ThorlabsServoDriver() - self.controller.connect() - - # Enable channels - self.controller.enable_axis(AXIS_X) - self.controller.enable_axis(AXIS_Y) - - # Start polling to populate cached state (positions, homed, moving, errors) - self.controller.start_polling(interval=0.2) - - # Wait for first polling cycle to populate status - time.sleep(0.3) - - # Set initial velocity parameters - for dest in [AXIS_X, AXIS_Y]: - self.controller.set_velocity_params( - dest, - max_velocity=self.jog_speed, - acceleration=self.acceleration - ) - - self.is_connected = True - # Force initial updates (they will be emitted because last values are None) - self.update_position() - self.update_home_status() - self.update_motion_status() - self.connected.emit() - - print("Motion controller connected successfully") - - except Exception as e: - print(f"Failed to connect to motion controller: {e}") - self.connection_failed.emit(str(e)) - - def do_disconnect(self): - """Disconnect from the motion controller""" - if self.controller: - try: - self.controller.disconnect() - print("Motion controller disconnected") - except Exception as e: - print(f"Error during disconnect: {e}") - - self.controller = None - self.is_connected = False - self.disconnected.emit() - - def do_jog(self, axis: str, direction: int): - """Execute a jog move""" - if not self.is_connected or not self.controller: - return - - try: - dest = AXIS_X if axis == 'x' else AXIS_Y - - # Calculate relative distance - distance = self.step_size * direction - - # Execute the move (blocking, with short timeout for continuous jogging) - self.controller.move_axis_relative(dest, distance, timeout=0.5) - - # Update position - self.update_position() - - self.move_completed.emit(axis) - - except TimeoutError: - # Timeout is expected during continuous jog - don't report as error - pass - except Exception as e: - print(f"Jog error: {e}") - self.error_occurred.emit(f"Jog failed: {str(e)}") - - def do_home(self, axis: str): - """Home an axis""" - if not self.is_connected or not self.controller: - return - - try: - dest = AXIS_X if axis == 'x' else AXIS_Y - - print(f"Homing {axis.upper()} axis...") - self.controller.home_axis(dest, timeout=60.0) - - # Update position and status after homing - self.update_position() - self.update_home_status() - - print(f"{axis.upper()} axis homed successfully") - - except TimeoutError: - print(f"Home timeout: {axis.upper()} axis") - self.error_occurred.emit(f"Homing {axis.upper()} timed out") - except Exception as e: - print(f"Home error: {e}") - self.error_occurred.emit(f"Homing {axis.upper()} failed: {str(e)}") - - def do_set_velocity(self, speed: float, accel: float): - """Set velocity parameters""" - if not self.is_connected or not self.controller: - self.jog_speed = speed - self.acceleration = accel - return - - try: - self.jog_speed = speed - self.acceleration = accel - - for dest in [AXIS_X, AXIS_Y]: - self.controller.set_velocity_params( - dest, - max_velocity=self.jog_speed, - acceleration=self.acceleration - ) - - except Exception as e: - print(f"Set velocity error: {e}") - - def do_set_axis_enable(self, axis: str, enabled: bool): - """Enable or disable an axis for manual movement""" - if not self.is_connected or not self.controller: - return - - try: - dest = AXIS_X if axis == 'x' else AXIS_Y - if enabled: - self.controller.enable_axis(dest) - else: - self.controller.disable_axis(dest) - state_str = "enabled" if enabled else "disabled" - print(f"{axis.upper()} axis {state_str}") - - except Exception as e: - print(f"Set axis enable error: {e}") - self.error_occurred.emit(f"Failed to {'enable' if enabled else 'disable'} {axis.upper()} axis: {str(e)}") - - def update_position(self): - """Update current position and emit signal if changed""" - if not self.is_connected or not self.controller: - return - - # Throttle position reads to avoid excessive signal emission - current_time = time.time() - if current_time - self.last_position_update_time < self.position_update_interval: - return - self.last_position_update_time = current_time - - try: - # Read cached positions (populated by polling worker) - x_pos = self.controller.positions[0] - y_pos = self.controller.positions[1] - - # Always emit on first update, or if position changed significantly (> 0.001mm) - if (self.last_x is None or self.last_y is None or - abs(x_pos - self.last_x) > 0.001 or abs(y_pos - self.last_y) > 0.001): - self.last_x = x_pos - self.last_y = y_pos - print(f"Position update: X={x_pos:.3f}mm, Y={y_pos:.3f}mm") - self.position_updated.emit(x_pos, y_pos) - - except Exception as e: - print(f"Error updating position: {e}") - import traceback - traceback.print_exc() - - def update_home_status(self): - """Update home status and emit signal if changed""" - if not self.is_connected or not self.controller: - return - - try: - x_homed = self.controller.am_homed[0] - y_homed = self.controller.am_homed[1] - - # Only emit if status changed - if x_homed != self.last_x_homed or y_homed != self.last_y_homed: - self.last_x_homed = x_homed - self.last_y_homed = y_homed - self.homed_status.emit(x_homed, y_homed) - - except Exception as e: - print(f"Error updating home status: {e}") - import traceback - traceback.print_exc() - - def update_motion_status(self): - """Update motion status and emit signal if changed. - - The new driver's polling worker keeps am_moving[], am_error[] - up to date automatically via status update messages. - """ - if not self.is_connected or not self.controller: - return - - try: - # Check for any error conditions - if self.controller.am_error[0]: - self.error_occurred.emit("X-axis error detected") - if self.controller.am_error[1]: - self.error_occurred.emit("Y-axis error detected") - - # Read cached motion status (updated by polling worker) - x_moving = self.controller.am_moving[0] - y_moving = self.controller.am_moving[1] - - # Only emit if status changed - if x_moving != self.last_x_moving or y_moving != self.last_y_moving: - self.last_x_moving = x_moving - self.last_y_moving = y_moving - self.motion_status.emit(x_moving, y_moving) - - except Exception as e: - print(f"Error updating motion status: {e}") - import traceback - traceback.print_exc() - - # Slot methods for queuing commands - @QtCore.pyqtSlot() - def queue_connect(self): - """Queue a connect command""" - self.command_queue.put(MotionCommand('connect')) - - @QtCore.pyqtSlot() - def queue_disconnect(self): - """Queue a disconnect command""" - self.command_queue.put(MotionCommand('disconnect')) - - @QtCore.pyqtSlot(str, int) - def queue_jog(self, axis: str, direction: int): - """Queue a jog command""" - self.command_queue.put(MotionCommand('jog', axis=axis, direction=direction)) - - @QtCore.pyqtSlot(str) - def queue_home(self, axis: str): - """Queue a home command""" - self.command_queue.put(MotionCommand('home', axis=axis)) - - @QtCore.pyqtSlot(float, float) - def queue_set_velocity(self, speed: float, accel: float): - """Queue a set velocity command""" - self.command_queue.put(MotionCommand('set_velocity', speed=speed, accel=accel)) - - @QtCore.pyqtSlot(float) - def queue_set_step_size(self, step_size: float): - """Queue a set step size command""" - self.command_queue.put(MotionCommand('set_step_size', step_size=step_size)) - - @QtCore.pyqtSlot(str, bool) - def queue_set_axis_enable(self, axis: str, enabled: bool): - """Queue a command to enable or disable an axis""" - self.command_queue.put(MotionCommand('set_axis_enable', axis=axis, enabled=enabled)) - - @QtCore.pyqtSlot() - def stop(self): - """Stop the worker thread""" - # Set running to False immediately so the main loop can exit - # even if it's blocked waiting for a response from the controller - self.running = False - # Also queue a stop command to ensure the command_queue.get() returns - self.command_queue.put(MotionCommand('stop')) diff --git a/sc3-new.ui b/sc3-new.ui deleted file mode 100755 index ed19d9e..0000000 --- a/sc3-new.ui +++ /dev/null @@ -1,2635 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 1312 - 1036 - - - - MainWindow - - - QMainWindow { - background-color: #000; - color: #FFF -} -QWidget { - background-color: #000; - color: #FFF; - font-family: "Space Grotesk", sans-serif -} -QLabel { - color: #FFF -} -QTabWidget { - border: 2px solid #696773; -} - - - - - - - - 1 - - - - - - - - - - Space Grotesk - 48 - - - - Scanengine - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignHCenter - - - - - - - - Space Grotesk - 14 - - - - What do you want to do? - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignHCenter - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - Start a New Scan - - - - - - - Resume an Interrupted Scan - - - - - - - Edit Options - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - - - - Space Grotesk - 32 - - - - Scanengine Configuration - - - - - - - - Space Grotesk - 14 - - - - 2 - - - - Kinematics - - - - - - - Space Grotesk - 14 - - - - Scan Kinematics: - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft - - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - Y -Coordinate: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - X -Coordinate: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - Trigger Behavior: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - Scan Acceleration: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - 0 x 0 micron - - - - - - - Scan Velocity: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - - - - - Space Grotesk - true - false - true - - - - This is affected by settings in the -Generation/IR and PulseDecimator panels as well. - - - - - - - - - - - Space Grotesk - 14 - - - - Optical Axis Correction - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft - - - - - - - Serial Number: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - mm/s2 - - - - - - - Test Connection - - - - - - - - - - Effective Pixel -Size: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - mm/s - - - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - - Space Grotesk - 14 - - - - Triggering Settings - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft - - - - - - - - - - Detection / VIS - - - - - - - - Interlock Status: - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignHCenter - - - - - - - Unknown - - - Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop - - - - - - - Diode Status: - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignHCenter - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - 123456789 - - - Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop - - - - - - - Test Connection && Query Laser - - - - - - - Keyswitch Status: - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignHCenter - - - - - - - Default -Power Level: - - - - - - - Serial Number: - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignHCenter - - - - - - - milliwatts - - - - - - - - 200 - 16777215 - - - - - - - - Unknown - - - Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop - - - - - - - Unknown - - - Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop - - - - - - - 0.00C - - - Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop - - - - - - - Temperature: - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignHCenter - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - - - - Generation / IR - - - - - - - - Interlock Status: - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignHCenter - - - - - - - - 400 - 16777215 - - - - - - - - Diode Pump -Current: - - - - - - - Serial Port: - - - - - - - Refresh Ports - - - - - - - milliamps - - - - - - - Diode Status: - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignHCenter - - - - - - - Enable Switch Status: - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignHCenter - - - - - - - Warmup Lockout: - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignHCenter - - - - - - - Connect - - - - - - - - - - Diode Temperature: - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignHCenter - - - - - - - Pulse Frequency: - - - - - - - Head Temperature: - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignHCenter - - - - - - - - 400 - 16777215 - - - - - - - - - Space Grotesk - 22 - - - - Status Information: - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft - - - - - - - Hertz - - - - - - - Head Hours: - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignHCenter - - - - - - - Unknown - - - Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop - - - - - - - Unknown - - - Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop - - - - - - - Unknown - - - Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop - - - - - - - Unknown - - - Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop - - - - - - - Unknown - - - Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop - - - - - - - Unknown - - - Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop - - - - - - - Unknown - - - Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop - - - - - - - Pixel Size: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - 0 x 0 microns - - - - - - - - Space Grotesk - true - true - - - - QFrame::Shape::NoFrame - - - This is also affected by settings in the Kinematics -and PulseDecimator tabs. - - - Qt::AlignmentFlag::AlignCenter - - - - - - - - - - PulseDecimator - - - - - - - - This is also affected by settings on the -Kinematics and Generation tabs. - - - - - - - Current Divider Value - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - Unknown - - - - - - - Unknown - - - - - - - - - - - 150 - 16777215 - - - - - - - - Refresh Ports - - - - - - - Unknown - - - - - - - Divider Value: - - - - - - - Firmware Version - - - - - - - T3RSL -Communicaton Port: - - - - - - - Enable RowPack feature? - - - - - - - Supports RowPack? - - - - - - - Effective Pixel Size: - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - Connect && Query - - - - - - - 0 x 0 microns - - - - - - - - - - T3R-SL - - - - - - - - T Axis Microstepping Mode: - - - - - - - Connect && Query - - - - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - - - - - - 150 - 16777215 - - - - - - - - T3RSL Communication Port: - - - - - - - - 200 - 16777215 - - - - - - - - GR Axis Drive Current - - - - - - - - 150 - 16777215 - - - - Refresh Ports - - - - - - - GR Axis Microstepping Mode: - - - - - - - T Axis Drive Current - - - - - - - - 200 - 16777215 - - - - - - - - - 150 - 16777215 - - - - - - - - milliamperes - - - - - - - - 350 - 0 - - - - - - - - milliamperes - - - - - - - - - - Oscilloscope - - - - - - - - Oscilloscope IP Address: - - - - - - - - - - Test Connection - - - - - - - - - - - - - - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - - - - - Save Settings - - - - - - - Cancel - - - - - - - - - - - - - - - - - - Space Grotesk - 32 - - - - Start a New Scan - - - - - - - - - VIS Laser: - - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - X-Position - - - - - - - - Space Grotesk - 28 - - - - 000.00 - - - - - - - Y-Position - - - - - - - - Space Grotesk - 28 - - - - 000.00 - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - Set as Start - - - - - - - Calculate Delta - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - - - - 16777213 - 16777215 - - - - Scan Friendly Name: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - 50 microns - - - - - - - - 16777213 - 16777215 - - - - Save to Folder: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - - Space Grotesk - 24 - - - - Sample Check: - - - - - - - - 480 - 480 - - - - - 480 - 480 - - - - QWidget { - border: 1px solid #FFF -} - - - - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - - - - - - 16777213 - 16777215 - - - - X Delta: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - - 16777213 - 16777215 - - - - Row Size: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - - 16777213 - 16777215 - - - - Y Delta: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - - 16777213 - 16777215 - - - - Start Y -Coord: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - - - - 150 - 16777215 - - - - Toggle Emission - - - - - - - - 16777215 - 16777215 - - - - Test Power Level: - - - Qt::AlignmentFlag::AlignCenter - - - - - - - - 50 - 16777215 - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - 100 - 16777215 - - - - - - - - 100 microns - - - - - - - - 100 - 16777215 - - - - - - - - Stage Jog Controls: - - - - - - - - 100 - 16777215 - - - - - - - - - 480 - 16777215 - - - - Camera Controls: - - - - - - - - 16777213 - 16777215 - - - - Start X -Coord: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - - 16777213 - 16777215 - - - - Number of -Angles: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - - - - - 150 - 16777215 - - - - Browse Folders - - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - Exposure: - - - - - - - Qt::Orientation::Horizontal - - - - - - - Gain: - - - - - - - Qt::Orientation::Horizontal - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - X - - - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - Y + - - - - - - - Jog Speed [mm/s]: - - - - - - - - 75 - 16777215 - - - - Qt::AlignmentFlag::AlignCenter - - - - - - - Y - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - - - X + - - - - - - - - - - - - 480 - 16777215 - - - - Camera Preview: - - - - - - - - 16777213 - 16777215 - - - - File Prefix: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - - 400 - 0 - - - - - - - - - 100 - 16777215 - - - - - - - - - 100 - 16777215 - - - - - - - - 250 microns - - - - - - - - - - - Continue - - - - - - - - - - - - - - Space Grotesk - 20 - - - - Metadata Summary: - - - - - - - Y-Start: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - - 150 - 0 - - - - Pixel Size: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - Select Existing Scan Directory: - - - - - - - Unknown - - - - - - - - - - Unknown - - - - - - - Unknown - - - - - - - 0 x 0 microns - - - - - - - Number of Angles: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - Friendly Name: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - - 150 - 16777215 - - - - Browse Directory - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - - Space Grotesk - 32 - - - - Resume an Existing Scan: - - - - - - - - 150 - 0 - - - - Y-Delta: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - Unknown - - - - - - - 0 - - - - - - - Unknown - - - - - - - Last Successfully -Scanned Angle: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - Unknown - - - - - - - - 150 - 0 - - - - X-Delta: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - X-Start: - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::Expanding - - - - 20 - 40 - - - - - - - - Read Directory and Load Metadata - - - - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - - - - - - - Resume Scanning - - - - - - - - - - - - - 24 - - - - - - - - - Kinematics Info: - - - - - - - - - XPos: - - - - - - - 000.00 - - - - - - - - - - - YPos: - - - - - - - 000.00 - - - - - - - - - - - State: - - - - - - - IDLE - - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - Genesis Info: - - - - - - - - - State: - - - - - - - EMISSION - - - - - - - - - - - Power: - - - - - - - 000mW - - - - - - - - - - - Temp: - - - - - - - 0C - - - - - - - - - - - Error: - - - - - - - NONE - - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - Helios Info: - - - - - - - - - State: - - - - - - - EMISSION - - - - - - - - - - - Frequency: - - - - - - - 20,000Hz - - - - - - - - - - - Pump Current: - - - - - - - 1500mA - - - - - - - - - - - Head Hours: - - - - - - - 0.0 - - - - - - - - - - - Head Temp: - - - - - - - 0C - - - - - - - - - - - Error: - - - - - - - NONE - - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - PulseDecimator: - - - - - - - - - Status: - - - - - - - ACTIVE - - - - - - - - - - - Divider: - - - - - - - /10 - - - - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - - Space Grotesk - 32 - - - - Scanning in Progress: - - - - - - - Scan Preview (DC Only): - - - - - - - 0h 0min - - - - - - - Scan Progress: - - - - - - - Overall Progress: - - - - - - - Estimated Remaining Time: - - - - - - - 24 - - - - - - - - 640 - 640 - - - - - 640 - 640 - - - - QWidget { - border: 1px solid #FFF; -} - - - - - - - - - - Space Grotesk - 14 - DemiBold - - - - ABORT SCAN - - - - - - - - - - - - - - diff --git a/scanning/__init__.py b/scanning/__init__.py deleted file mode 100755 index c03dc67..0000000 --- a/scanning/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Scan planning and modeling modules""" -from .sc3_scan_model import SC3ScanModel -from .stage_scan_plan_generator import * diff --git a/scanning/sc3_scan_model.py b/scanning/sc3_scan_model.py deleted file mode 100755 index 53dae6a..0000000 --- a/scanning/sc3_scan_model.py +++ /dev/null @@ -1,638 +0,0 @@ -""" -Scan Model Class. -Holds the scan configuration, and computes the -required start and end points for various specified angles. -Python implementation of SC3ScanModel.cs -""" - -import math -from decimal import Decimal, InvalidOperation -from typing import List, Optional -import csv - - -class SC3ScanModel: - """ - Scan Model for generating scan paths and rotated scans. - Manages scan configuration and computes scan coordinates at various angles. - """ - - def __init__(self): - # Private variables - self._x_origin: Decimal = Decimal('0.0') - self._y_origin: Decimal = Decimal('0.0') - self._x_delta: Decimal = Decimal('0.0') - self._y_delta: Decimal = Decimal('0.0') - self._row_spacing: Decimal = Decimal('0.0') - self._laser_frequency: Decimal = Decimal('2000.0') # Default: 2000 Hz - self._scan_velocity: Decimal = Decimal('100.0') # Default: 100 mm/s - self._scan_acceleration: Decimal = Decimal('0.0') - self._scan_angles: int = 0 - self._points_required: int = 0 - self._rows_required: int = 0 - self._points_per_line: int = 0 - - # Optical axis centerline in stage coordinates - # Stage: MLS203-1 - self._optical_x_origin: Decimal = Decimal('55.0') - self._optical_y_origin: Decimal = Decimal('37.5') - - # Data storage - self._scan_coordinates: List[List[Decimal]] = [] - self._scan_velocities: List[List[Decimal]] = [] - self._scan_accelerations: List[List[Decimal]] = [] - self._rotated_coordinates: List[List[List[Decimal]]] = [] - - # Constants - self._deg2rad: float = math.pi / 180.0 - - # Properties - @property - def x_origin(self) -> Decimal: - """X coordinate of scan origin (mm)""" - return self._x_origin - - @x_origin.setter - def x_origin(self, value: Decimal): - try: - self._x_origin = Decimal(str(value)) - except (ValueError, InvalidOperation) as e: - raise ValueError(f"Invalid x_origin value: {value}") from e - - @property - def y_origin(self) -> Decimal: - """Y coordinate of scan origin (mm)""" - return self._y_origin - - @y_origin.setter - def y_origin(self, value: Decimal): - try: - self._y_origin = Decimal(str(value)) - except (ValueError, InvalidOperation) as e: - raise ValueError(f"Invalid y_origin value: {value}") from e - - @property - def x_delta(self) -> Decimal: - """Total X distance to scan (mm)""" - return self._x_delta - - @x_delta.setter - def x_delta(self, value: Decimal): - try: - val = Decimal(str(value)) - if val < 0: - raise ValueError("x_delta must be non-negative") - self._x_delta = val - self.calculate_points_per_line() - self.calculate_points_required() - except (ValueError, InvalidOperation) as e: - raise ValueError(f"Invalid x_delta value: {value}") from e - - @property - def y_delta(self) -> Decimal: - """Total Y distance to scan (mm)""" - return self._y_delta - - @y_delta.setter - def y_delta(self, value: Decimal): - try: - val = Decimal(str(value)) - if val < 0: - raise ValueError("y_delta must be non-negative") - self._y_delta = val - self.calculate_rows_required() - self.calculate_points_required() - except (ValueError, InvalidOperation) as e: - raise ValueError(f"Invalid y_delta value: {value}") from e - - @property - def row_spacing(self) -> Decimal: - """Spacing between scan rows (mm)""" - return self._row_spacing - - @row_spacing.setter - def row_spacing(self, value: Decimal): - try: - val = Decimal(str(value)) - if val < 0: - raise ValueError("row_spacing must be non-negative") - self._row_spacing = val - self.calculate_rows_required() - self.calculate_points_required() - except (ValueError, InvalidOperation) as e: - raise ValueError(f"Invalid row_spacing value: {value}") from e - - @property - def laser_frequency(self) -> Decimal: - """Laser pulse frequency (Hz)""" - return self._laser_frequency - - @laser_frequency.setter - def laser_frequency(self, value: Decimal): - try: - val = Decimal(str(value)) - if val <= 0: - raise ValueError("laser_frequency must be positive") - self._laser_frequency = val - self.calculate_points_per_line() - self.calculate_points_required() - except (ValueError, InvalidOperation) as e: - raise ValueError(f"Invalid laser_frequency value: {value}") from e - - @property - def scan_velocity(self) -> Decimal: - """Scan velocity (mm/s)""" - return self._scan_velocity - - @scan_velocity.setter - def scan_velocity(self, value: Decimal): - try: - val = Decimal(str(value)) - if val <= 0: - raise ValueError("scan_velocity must be positive") - self._scan_velocity = val - self.calculate_points_per_line() - self.calculate_points_required() - except (ValueError, InvalidOperation) as e: - raise ValueError(f"Invalid scan_velocity value: {value}") from e - - @property - def scan_acceleration(self) -> Decimal: - """Scan acceleration (mm/s²)""" - return self._scan_acceleration - - @scan_acceleration.setter - def scan_acceleration(self, value: Decimal): - try: - val = Decimal(str(value)) - if val < 0: - raise ValueError("scan_acceleration must be non-negative") - self._scan_acceleration = val - except (ValueError, InvalidOperation) as e: - raise ValueError(f"Invalid scan_acceleration value: {value}") from e - - @property - def scan_angles(self) -> int: - """Number of scan angles to compute""" - return self._scan_angles - - @scan_angles.setter - def scan_angles(self, value: int): - if value < 0: - raise ValueError("scan_angles must be non-negative") - self._scan_angles = value - # Only compute rotated scans if we have base scan coordinates - if self._scan_coordinates: - self.compute_rotated_scans() - - @property - def points_required(self) -> int: - """Total number of points required for a single angle scan (computed)""" - return self._points_required - - @property - def rows_required(self) -> int: - """Number of rows required for the scan (computed)""" - return self._rows_required - - @property - def points_per_line(self) -> int: - """Number of points per scan line (computed)""" - return self._points_per_line - - @property - def scan_coordinates(self) -> List[List[Decimal]]: - """List of scan coordinates [x_start, y_start, x_end, y_end] in mm""" - return self._scan_coordinates - - @property - def scan_velocities(self) -> List[List[Decimal]]: - """List of velocity vectors [vx, vy] in mm/s for each angle""" - return self._scan_velocities - - @property - def scan_accelerations(self) -> List[List[Decimal]]: - """List of acceleration vectors [ax, ay] in mm/s² for each angle""" - return self._scan_accelerations - - @property - def rotated_coordinates(self) -> List[List[List[Decimal]]]: - """List of rotated scan coordinates for each angle in mm""" - return self._rotated_coordinates - - @property - def optical_x_origin(self) -> Decimal: - """X coordinate of optical axis origin in mm (read-only, MLS203-1 stage)""" - return self._optical_x_origin - - @property - def optical_y_origin(self) -> Decimal: - """Y coordinate of optical axis origin in mm (read-only, MLS203-1 stage)""" - return self._optical_y_origin - - # Calculation Methods - def calculate_points_per_line(self): - """ - Calculates the number of data points per scan line based on - x_delta, scan_velocity, and laser_frequency. - - Formula: points = (distance / velocity) * frequency - """ - if self._scan_velocity != 0: - self._points_per_line = int( - (self._x_delta / self._scan_velocity) * self._laser_frequency - ) - - def calculate_points_required(self): - """ - Calculates the total number of data points for a complete single-angle scan. - Also triggers computation of the zero-angle scan coordinates. - - Formula: total_points = points_per_line * rows_required - """ - if self._rows_required != 0: - self._points_required = self._points_per_line * self._rows_required - self.compute_zero_scan() - - def calculate_rows_required(self): - """ - Calculates the number of scan rows needed based on y_delta and row_spacing. - - Formula: rows = ceil(y_delta / row_spacing) - """ - if self._row_spacing == 0: - self._rows_required = 0 - else: - self._rows_required = int(math.ceil(self._y_delta / self._row_spacing)) - - def compute_zero_scan(self): - """ - Computes the zero-angle (reference) scan coordinates. - Each coordinate is [x_start, y_start, x_end, y_end]. - """ - # Ignore the zero-row case - if self._rows_required == 0: - return - - y_offset = Decimal('0.0') - self._scan_coordinates.clear() - - for row in range(self._rows_required + 1): - # Calculate x/y origin/delta for each needed row - y_offset = Decimal(row) * self._row_spacing - - coords = [ - self._x_origin, # x_start - self._y_origin + y_offset, # y_start - self._x_origin + self._x_delta, # x_end - self._y_origin + y_offset # y_end (same as y_start for horizontal scan) - ] - self._scan_coordinates.append(coords) - - def _rotate_point(self, x: Decimal, y: Decimal, cosine: Decimal, sine: Decimal) -> tuple: - """ - Rotate a point around the optical axis origin. - - Args: - x, y: Point coordinates to rotate - cosine, sine: Precomputed cos and sin of rotation angle - - Returns: - Tuple of (rotated_x, rotated_y) - """ - # Rotation transformation: - # Xr = (X - Xo)*cos(a) + (Y - Yo)*sin(a) + Xo - # Yr = -(X - Xo)*sin(a) + (Y - Yo)*cos(a) + Yo - x_rot = ((x - self._optical_x_origin) * cosine + - (y - self._optical_y_origin) * sine + - self._optical_x_origin) - y_rot = (-(x - self._optical_x_origin) * sine + - (y - self._optical_y_origin) * cosine + - self._optical_y_origin) - return (x_rot, y_rot) - - def _compute_rotated_aoi_bbox(self, angle_rad: float) -> tuple: - """ - Compute the bounding box of the rotated area of interest. - - Rotates the four corners of the AoI rectangle and finds the - min/max extents to create a bounding box. - - Args: - angle_rad: Rotation angle in radians - - Returns: - Tuple of (min_x, min_y, max_x, max_y) as Decimals - """ - cosine = Decimal(str(math.cos(angle_rad))) - sine = Decimal(str(math.sin(angle_rad))) - - # Define the four corners of the AoI rectangle - corners = [ - (self._x_origin, self._y_origin), - (self._x_origin + self._x_delta, self._y_origin), - (self._x_origin + self._x_delta, self._y_origin + self._y_delta), - (self._x_origin, self._y_origin + self._y_delta) - ] - - # Rotate all corners - rotated_corners = [] - for x, y in corners: - x_rot, y_rot = self._rotate_point(x, y, cosine, sine) - rotated_corners.append((x_rot, y_rot)) - - # Find bounding box extents - x_coords = [corner[0] for corner in rotated_corners] - y_coords = [corner[1] for corner in rotated_corners] - - return (min(x_coords), min(y_coords), max(x_coords), max(y_coords)) - - def compute_rotated_scans(self): - """ - Computes rotated scan coordinates by rotating the area of interest (AoI) - and generating horizontal (+x direction) scans through the bounding box - of the rotated AoI. - - The rotation covers 0 to 180 degrees with spacing determined by scan_angles. - For each angle: - 1. Rotate the AoI rectangle around the optical axis origin - 2. Compute the bounding box of the rotated rectangle - 3. Generate horizontal scan lines through the bounding box - """ - if self._rows_required == 0: - return - - # Compute spacing between scans - if self._scan_angles == 0: - angle_spacing = 180 - else: - angle_spacing = 180 / self._scan_angles - - self._rotated_coordinates.clear() - - # Iterate through each required angle from 0 to 180 degrees - i = 0 - while i < 180: - current_angle_radians = i * (math.pi / 180.0) - - # Get bounding box of rotated AoI - min_x, min_y, max_x, max_y = self._compute_rotated_aoi_bbox(current_angle_radians) - - # Calculate the y extent of the bounding box - y_extent = max_y - min_y - - # Determine number of rows needed for this bounding box - if self._row_spacing == 0: - num_rows = 0 - else: - num_rows = int(math.ceil(y_extent / self._row_spacing)) - - temp_list = [] - - # Generate horizontal scan lines through the bounding box - for row in range(num_rows + 1): - y_offset = Decimal(row) * self._row_spacing - y_pos = min_y + y_offset - - # Create horizontal scan line at this y position - scan_line = [ - min_x, # x_start - y_pos, # y_start - max_x, # x_end - y_pos # y_end (same as y_start for horizontal scan) - ] - temp_list.append(scan_line) - - self._rotated_coordinates.append(temp_list) - i += int(round(angle_spacing)) - - def compute_kinematics(self, offset: int = 0): - """ - Computes velocity and acceleration component vectors for each scan angle. - - For each angle, decomposes the scalar velocity and acceleration into - X and Y components based on the scan direction angle. - - Args: - offset: Angle offset in degrees (default: 0) - """ - if self._scan_angles == 0: - scan_increment = 180 - else: - scan_increment = 180 // self._scan_angles - - self._scan_velocities.clear() - self._scan_accelerations.clear() - - for i in range(self._scan_angles): - deg_angle = scan_increment * i + offset - angle_rad = deg_angle * self._deg2rad - - # Compute velocity components: V = V_mag * [cos(θ), sin(θ)] - velocities = [ - Decimal(str(math.cos(angle_rad))) * self._scan_velocity, - Decimal(str(math.sin(angle_rad))) * self._scan_velocity - ] - - # Compute acceleration components: A = A_mag * [cos(θ), sin(θ)] - accels = [ - Decimal(str(math.cos(angle_rad))) * self._scan_acceleration, - Decimal(str(math.sin(angle_rad))) * self._scan_acceleration - ] - - self._scan_velocities.append(velocities) - self._scan_accelerations.append(accels) - - # Export Methods - def export_zero_scan_csv(self, filename: Optional[str] = None) -> str: - """ - Export the zero-angle scan coordinates to a CSV file. - - Args: - filename: Output filename. If None, generates from row count. - - Returns: - The filename that was written - - Raises: - ValueError: If no scan coordinates have been computed - IOError: If file cannot be written - """ - if not self._scan_coordinates: - raise ValueError("No scan coordinates available. Configure scan parameters first.") - - if filename is None: - filename = f"scantest-{self._rows_required}rows.csv" - - try: - with open(filename, 'w', newline='') as f: - writer = csv.writer(f) - for coords in self._scan_coordinates: - writer.writerow([str(c) for c in coords]) - except IOError as e: - raise IOError(f"Failed to write file {filename}: {e}") from e - - return filename - - def export_rotated_scan_csv(self, angle_index: int, filename: Optional[str] = None) -> str: - """ - Export a specific rotated scan to CSV. - - Args: - angle_index: Index of the angle to export (0-based) - filename: Output filename. If None, generates from angle and row count. - - Returns: - The filename that was written - - Raises: - ValueError: If angle_index is invalid or no rotated coordinates exist - IOError: If file cannot be written - """ - if not self._rotated_coordinates: - raise ValueError("No rotated coordinates available. Set scan_angles first.") - - if angle_index < 0 or angle_index >= len(self._rotated_coordinates): - raise ValueError( - f"Invalid angle_index {angle_index}. Must be 0-{len(self._rotated_coordinates)-1}" - ) - - if filename is None: - angle_deg = angle_index * (180 // self._scan_angles if self._scan_angles > 0 else 180) - filename = f"scantest-{angle_deg:03d}deg-{self._rows_required}rows.csv" - - try: - with open(filename, 'w', newline='') as f: - writer = csv.writer(f) - for coords in self._rotated_coordinates[angle_index]: - writer.writerow([str(c) for c in coords]) - except IOError as e: - raise IOError(f"Failed to write file {filename}: {e}") from e - - return filename - - def export_all_rotated_scans_csv(self, output_dir: str = ".") -> List[str]: - """ - Export all rotated scans to separate CSV files. - - Args: - output_dir: Directory to write files to (default: current directory) - - Returns: - List of filenames that were written - - Raises: - ValueError: If no rotated coordinates exist - IOError: If files cannot be written - """ - if not self._rotated_coordinates: - raise ValueError("No rotated coordinates available. Set scan_angles first.") - - import os - filenames = [] - - for angle_index in range(len(self._rotated_coordinates)): - angle_deg = angle_index * (180 // self._scan_angles if self._scan_angles > 0 else 180) - filename = f"scantest-{angle_deg:03d}deg-{self._rows_required}rows.csv" - filepath = os.path.join(output_dir, filename) - - try: - with open(filepath, 'w', newline='') as f: - writer = csv.writer(f) - for coords in self._rotated_coordinates[angle_index]: - writer.writerow([str(c) for c in coords]) - filenames.append(filepath) - except IOError as e: - raise IOError(f"Failed to write file {filepath}: {e}") from e - - return filenames - - def export_kinematics_csv(self, filename: Optional[str] = None) -> str: - """ - Export velocity and acceleration data to CSV. - Format: vx, vy, ax, ay for each angle. - - Args: - filename: Output filename. If None, generates from row count. - - Returns: - The filename that was written - - Raises: - ValueError: If no kinematics data has been computed - IOError: If file cannot be written - """ - if not self._scan_velocities or not self._scan_accelerations: - raise ValueError("No kinematics data available. Call compute_kinematics() first.") - - if filename is None: - filename = f"kinematics-{self._rows_required}rows.csv" - - try: - with open(filename, 'w', newline='') as f: - writer = csv.writer(f) - # Optional: write header - writer.writerow(['vx', 'vy', 'ax', 'ay']) - for i in range(len(self._scan_velocities)): - row = [ - str(self._scan_velocities[i][0]), - str(self._scan_velocities[i][1]), - str(self._scan_accelerations[i][0]), - str(self._scan_accelerations[i][1]) - ] - writer.writerow(row) - except IOError as e: - raise IOError(f"Failed to write file {filename}: {e}") from e - - return filename - - # Utility Methods - def get_angle_list(self) -> List[int]: - """ - Get the list of scan angles in degrees. - - Returns: - List of angles in degrees for the configured scan_angles - """ - if self._scan_angles == 0: - return [] - - scan_increment = 180 // self._scan_angles - return [scan_increment * i for i in range(self._scan_angles)] - - def get_scan_info(self) -> dict: - """ - Get a dictionary with current scan configuration and computed values. - - Returns: - Dictionary containing scan parameters and computed values - """ - return { - 'x_origin': float(self._x_origin), - 'y_origin': float(self._y_origin), - 'x_delta': float(self._x_delta), - 'y_delta': float(self._y_delta), - 'row_spacing': float(self._row_spacing), - 'laser_frequency': float(self._laser_frequency), - 'scan_velocity': float(self._scan_velocity), - 'scan_acceleration': float(self._scan_acceleration), - 'scan_angles': self._scan_angles, - 'points_per_line': self._points_per_line, - 'rows_required': self._rows_required, - 'points_required': self._points_required, - 'optical_x_origin': float(self._optical_x_origin), - 'optical_y_origin': float(self._optical_y_origin), - 'num_scan_coordinates': len(self._scan_coordinates), - 'num_rotated_angles': len(self._rotated_coordinates), - 'angle_list': self.get_angle_list(), - } - - def __repr__(self) -> str: - """String representation of the scan model.""" - return ( - f"SC3ScanModel(" - f"origin=({self._x_origin},{self._y_origin}), " - f"delta=({self._x_delta},{self._y_delta}), " - f"rows={self._rows_required}, " - f"angles={self._scan_angles})" - ) diff --git a/scanning/stage_scan_plan_generator.py b/scanning/stage_scan_plan_generator.py deleted file mode 100755 index 7a02864..0000000 --- a/scanning/stage_scan_plan_generator.py +++ /dev/null @@ -1,117 +0,0 @@ -""" -This module contains a StageScanPlanGenerator class that generates scanning plans -for microscope stages. The scans are generated in a single direction based on provided -start and end coordinates, as well as spacing between scan lines. -""" - -import numpy as np -from typing import List, Tuple, Optional - - -class StageScanPlanGenerator: - """ - A class to generate scanning plans for microscope stages. - - Attributes: - start_coords (Tuple[float, float]): Starting X and Y coordinates. - end_coords (Tuple[float, float]): Ending X and Y coordinates. - spacing (float): Spacing between scan lines in the perpendicular direction. - """ - - def __init__(self, start_x: float, start_y: float, - end_x: float, end_y: float, spacing: float): - """ - Initialize the StageScanPlanGenerator with scan parameters. - - Args: - start_x (float): Starting X coordinate. - start_y (float): Starting Y coordinate. - end_x (float): Ending X coordinate. - end_y (float): Ending Y coordinate. - spacing (float): Spacing between scan lines in the perpendicular direction. - """ - self.start_coords = (start_x, start_y) - self.end_coords = (end_x, end_y) - self.spacing = spacing - - def _calculate_scan_direction(self) -> Tuple[float, float]: - """ - Calculate the direction vector of the scan. - - Returns: - Tuple[float, float]: Normalized direction vector (dx, dy). - """ - dx = self.end_coords[0] - self.start_coords[0] - dy = self.end_coords[1] - self.start_coords[1] - length = np.sqrt(dx**2 + dy**2) - - if length == 0: - raise ValueError("Start and end coordinates cannot be the same") - - return dx / length, dy / length - - def _calculate_perpendicular_direction(self) -> Tuple[float, float]: - """ - Calculate a perpendicular direction vector to the scan direction. - - Returns: - Tuple[float, float]: Perpendicular vector (px, py). - """ - dx, dy = self._calculate_scan_direction() - # Rotate (dx, dy) by 90 degrees to get perpendicular vector - px = -dy - py = dx - return px, py - - def generate_scan_plan(self) -> List[Tuple[Tuple[float, float], Tuple[float, float]]]: - """ - Generate a scan plan with waypoints for the microscope stage. - - Returns: - List[Tuple[Tuple[float, float], Tuple[float, float]]]: - A list of (start_point, end_point) tuples for each scan line. - """ - dx, dy = self._calculate_scan_direction() - px, py = self._calculate_perpendicular_direction() - - # Calculate the total length in the perpendicular direction - start_x, start_y = self.start_coords - end_x, end_y = self.end_coords - min_coord_perp = min(start_x * px + start_y * py, end_x * px + end_y * py) - max_coord_perp = max(start_x * px + start_y * py, end_x * px + end_y * py) - - # Generate scan lines - waypoints = [] - current_pos_perp = min_coord_perp - - while current_pos_perp <= max_coord_perp: - # Calculate start and end points for this scan line - perp_offset = current_pos_perp - (start_x * px + start_y * py) - line_start_x = start_x + perp_offset * dx - line_start_y = start_y + perp_offset * dy - - line_end_x = line_start_x + dx * abs(self.end_coords[0] - self.start_coords[0]) - line_end_y = line_start_y + dy * abs(self.end_coords[1] - self.start_coords[1]) - - waypoints.append(((line_start_x, line_start_y), (line_end_x, line_end_y))) - current_pos_perp += self.spacing - - return waypoints - - -# Example usage: -if __name__ == "__main__": - # Create a scan plan generator - generator = StageScanPlanGenerator( - start_x=0.0, start_y=0.0, - end_x=10.0, end_y=10.0, - spacing=2.5 - ) - - # Generate the scan plan - scan_plan = generator.generate_scan_plan() - - # Print the scan plan - print("Scan Plan:") - for i, (start, end) in enumerate(scan_plan): - print(f"Line {i+1}: Start at {start}, End at {end}") \ No newline at end of file diff --git a/ui_mainwindow.py b/ui_mainwindow.py deleted file mode 100755 index 5a1b11a..0000000 --- a/ui_mainwindow.py +++ /dev/null @@ -1,1321 +0,0 @@ -# Form implementation generated from reading ui file '/opt/scanengine-3/sc3-new.ui' -# -# Created by: PyQt6 UI code generator 6.10.2 -# -# WARNING: Any manual changes made to this file will be lost when pyuic6 is -# run again. Do not edit this file unless you know what you are doing. - - -from PyQt6 import QtCore, QtGui, QtWidgets - - -class Ui_MainWindow(object): - def setupUi(self, MainWindow): - MainWindow.setObjectName("MainWindow") - MainWindow.resize(1312, 1036) - MainWindow.setStyleSheet("QMainWindow {\n" -" background-color: #000;\n" -" color: #FFF\n" -"}\n" -"QWidget {\n" -" background-color: #000;\n" -" color: #FFF;\n" -" font-family: \"Space Grotesk\", sans-serif\n" -"}\n" -"QLabel {\n" -" color: #FFF\n" -"}\n" -"QTabWidget {\n" -" border: 2px solid #696773;\n" -"}\n" -"") - self.centralwidget = QtWidgets.QWidget(parent=MainWindow) - self.centralwidget.setObjectName("centralwidget") - self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget) - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.stackedWidget = QtWidgets.QStackedWidget(parent=self.centralwidget) - self.stackedWidget.setObjectName("stackedWidget") - self.start_page = QtWidgets.QWidget() - self.start_page.setObjectName("start_page") - self.verticalLayout = QtWidgets.QVBoxLayout(self.start_page) - self.verticalLayout.setObjectName("verticalLayout") - self.vl_mainmenu = QtWidgets.QVBoxLayout() - self.vl_mainmenu.setObjectName("vl_mainmenu") - self.label = QtWidgets.QLabel(parent=self.start_page) - font = QtGui.QFont() - font.setFamily("Space Grotesk") - font.setPointSize(48) - self.label.setFont(font) - self.label.setAlignment(QtCore.Qt.AlignmentFlag.AlignBottom|QtCore.Qt.AlignmentFlag.AlignHCenter) - self.label.setObjectName("label") - self.vl_mainmenu.addWidget(self.label) - self.label_2 = QtWidgets.QLabel(parent=self.start_page) - font = QtGui.QFont() - font.setFamily("Space Grotesk") - font.setPointSize(14) - self.label_2.setFont(font) - self.label_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignBottom|QtCore.Qt.AlignmentFlag.AlignHCenter) - self.label_2.setObjectName("label_2") - self.vl_mainmenu.addWidget(self.label_2) - spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.vl_mainmenu.addItem(spacerItem) - self.start_new_scan_btn = QtWidgets.QPushButton(parent=self.start_page) - self.start_new_scan_btn.setObjectName("start_new_scan_btn") - self.vl_mainmenu.addWidget(self.start_new_scan_btn) - self.resume_scan_btn = QtWidgets.QPushButton(parent=self.start_page) - self.resume_scan_btn.setObjectName("resume_scan_btn") - self.vl_mainmenu.addWidget(self.resume_scan_btn) - self.edit_options_btn = QtWidgets.QPushButton(parent=self.start_page) - self.edit_options_btn.setObjectName("edit_options_btn") - self.vl_mainmenu.addWidget(self.edit_options_btn) - spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.vl_mainmenu.addItem(spacerItem1) - self.verticalLayout.addLayout(self.vl_mainmenu) - self.stackedWidget.addWidget(self.start_page) - self.options_page = QtWidgets.QWidget() - self.options_page.setObjectName("options_page") - self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.options_page) - self.verticalLayout_3.setObjectName("verticalLayout_3") - self.gridLayout = QtWidgets.QGridLayout() - self.gridLayout.setObjectName("gridLayout") - self.verticalLayout_4 = QtWidgets.QVBoxLayout() - self.verticalLayout_4.setObjectName("verticalLayout_4") - self.label_3 = QtWidgets.QLabel(parent=self.options_page) - font = QtGui.QFont() - font.setFamily("Space Grotesk") - font.setPointSize(32) - self.label_3.setFont(font) - self.label_3.setObjectName("label_3") - self.verticalLayout_4.addWidget(self.label_3) - self.configuration_tabwidget = QtWidgets.QTabWidget(parent=self.options_page) - font = QtGui.QFont() - font.setFamily("Space Grotesk") - font.setPointSize(14) - self.configuration_tabwidget.setFont(font) - self.configuration_tabwidget.setObjectName("configuration_tabwidget") - self.kinematics_tab = QtWidgets.QWidget() - self.kinematics_tab.setObjectName("kinematics_tab") - self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.kinematics_tab) - self.verticalLayout_5.setObjectName("verticalLayout_5") - self.label_65 = QtWidgets.QLabel(parent=self.kinematics_tab) - font = QtGui.QFont() - font.setFamily("Space Grotesk") - font.setPointSize(14) - self.label_65.setFont(font) - self.label_65.setAlignment(QtCore.Qt.AlignmentFlag.AlignBottom|QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft) - self.label_65.setObjectName("label_65") - self.verticalLayout_5.addWidget(self.label_65) - self.gridLayout_2 = QtWidgets.QGridLayout() - self.gridLayout_2.setObjectName("gridLayout_2") - spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.gridLayout_2.addItem(spacerItem2, 2, 1, 1, 1) - self.label_15 = QtWidgets.QLabel(parent=self.kinematics_tab) - self.label_15.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_15.setObjectName("label_15") - self.gridLayout_2.addWidget(self.label_15, 9, 3, 1, 1) - self.label_14 = QtWidgets.QLabel(parent=self.kinematics_tab) - self.label_14.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_14.setObjectName("label_14") - self.gridLayout_2.addWidget(self.label_14, 9, 0, 1, 1) - self.label_11 = QtWidgets.QLabel(parent=self.kinematics_tab) - self.label_11.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_11.setObjectName("label_11") - self.gridLayout_2.addWidget(self.label_11, 4, 0, 1, 1) - self.label_6 = QtWidgets.QLabel(parent=self.kinematics_tab) - self.label_6.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_6.setObjectName("label_6") - self.gridLayout_2.addWidget(self.label_6, 0, 3, 1, 1) - self.label_9 = QtWidgets.QLabel(parent=self.kinematics_tab) - self.label_9.setObjectName("label_9") - self.gridLayout_2.addWidget(self.label_9, 1, 1, 1, 1) - self.label_4 = QtWidgets.QLabel(parent=self.kinematics_tab) - self.label_4.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_4.setObjectName("label_4") - self.gridLayout_2.addWidget(self.label_4, 0, 0, 1, 1) - self.scan_accel_edit = QtWidgets.QLineEdit(parent=self.kinematics_tab) - self.scan_accel_edit.setObjectName("scan_accel_edit") - self.gridLayout_2.addWidget(self.scan_accel_edit, 0, 4, 1, 1) - self.optical_axis_x_edit = QtWidgets.QLineEdit(parent=self.kinematics_tab) - self.optical_axis_x_edit.setObjectName("optical_axis_x_edit") - self.gridLayout_2.addWidget(self.optical_axis_x_edit, 9, 1, 1, 1) - spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.gridLayout_2.addItem(spacerItem3, 7, 0, 1, 1) - self.stage_trigger_combo = QtWidgets.QComboBox(parent=self.kinematics_tab) - self.stage_trigger_combo.setObjectName("stage_trigger_combo") - self.gridLayout_2.addWidget(self.stage_trigger_combo, 4, 1, 1, 1) - self.label_10 = QtWidgets.QLabel(parent=self.kinematics_tab) - font = QtGui.QFont() - font.setFamily("Space Grotesk") - font.setBold(False) - font.setItalic(True) - font.setUnderline(True) - self.label_10.setFont(font) - self.label_10.setObjectName("label_10") - self.gridLayout_2.addWidget(self.label_10, 1, 2, 1, 3) - self.stage_serial_edit = QtWidgets.QLineEdit(parent=self.kinematics_tab) - self.stage_serial_edit.setObjectName("stage_serial_edit") - self.gridLayout_2.addWidget(self.stage_serial_edit, 5, 1, 1, 1) - self.label_13 = QtWidgets.QLabel(parent=self.kinematics_tab) - font = QtGui.QFont() - font.setFamily("Space Grotesk") - font.setPointSize(14) - self.label_13.setFont(font) - self.label_13.setAlignment(QtCore.Qt.AlignmentFlag.AlignBottom|QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft) - self.label_13.setObjectName("label_13") - self.gridLayout_2.addWidget(self.label_13, 8, 0, 1, 2) - self.label_12 = QtWidgets.QLabel(parent=self.kinematics_tab) - self.label_12.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_12.setObjectName("label_12") - self.gridLayout_2.addWidget(self.label_12, 5, 0, 1, 1) - self.label_7 = QtWidgets.QLabel(parent=self.kinematics_tab) - self.label_7.setObjectName("label_7") - self.gridLayout_2.addWidget(self.label_7, 0, 5, 1, 1) - self.stage_test_connection_btn = QtWidgets.QPushButton(parent=self.kinematics_tab) - self.stage_test_connection_btn.setObjectName("stage_test_connection_btn") - self.gridLayout_2.addWidget(self.stage_test_connection_btn, 5, 3, 1, 1) - self.scan_velocity_edit = QtWidgets.QLineEdit(parent=self.kinematics_tab) - self.scan_velocity_edit.setObjectName("scan_velocity_edit") - self.gridLayout_2.addWidget(self.scan_velocity_edit, 0, 1, 1, 1) - self.label_8 = QtWidgets.QLabel(parent=self.kinematics_tab) - self.label_8.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_8.setObjectName("label_8") - self.gridLayout_2.addWidget(self.label_8, 1, 0, 1, 1) - self.label_5 = QtWidgets.QLabel(parent=self.kinematics_tab) - self.label_5.setObjectName("label_5") - self.gridLayout_2.addWidget(self.label_5, 0, 2, 1, 1) - self.optical_axis_y_edit = QtWidgets.QLineEdit(parent=self.kinematics_tab) - self.optical_axis_y_edit.setObjectName("optical_axis_y_edit") - self.gridLayout_2.addWidget(self.optical_axis_y_edit, 9, 4, 1, 1) - spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.gridLayout_2.addItem(spacerItem4, 10, 1, 1, 1) - self.label_64 = QtWidgets.QLabel(parent=self.kinematics_tab) - font = QtGui.QFont() - font.setFamily("Space Grotesk") - font.setPointSize(14) - self.label_64.setFont(font) - self.label_64.setAlignment(QtCore.Qt.AlignmentFlag.AlignBottom|QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft) - self.label_64.setObjectName("label_64") - self.gridLayout_2.addWidget(self.label_64, 3, 0, 1, 2) - self.verticalLayout_5.addLayout(self.gridLayout_2) - self.configuration_tabwidget.addTab(self.kinematics_tab, "") - self.detection_tab = QtWidgets.QWidget() - self.detection_tab.setObjectName("detection_tab") - self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.detection_tab) - self.verticalLayout_6.setObjectName("verticalLayout_6") - self.gridLayout_3 = QtWidgets.QGridLayout() - self.gridLayout_3.setObjectName("gridLayout_3") - self.label_17 = QtWidgets.QLabel(parent=self.detection_tab) - self.label_17.setAlignment(QtCore.Qt.AlignmentFlag.AlignBottom|QtCore.Qt.AlignmentFlag.AlignHCenter) - self.label_17.setObjectName("label_17") - self.gridLayout_3.addWidget(self.label_17, 1, 1, 1, 1) - self.detection_diode_label = QtWidgets.QLabel(parent=self.detection_tab) - self.detection_diode_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop) - self.detection_diode_label.setObjectName("detection_diode_label") - self.gridLayout_3.addWidget(self.detection_diode_label, 2, 3, 1, 1) - self.label_19 = QtWidgets.QLabel(parent=self.detection_tab) - self.label_19.setAlignment(QtCore.Qt.AlignmentFlag.AlignBottom|QtCore.Qt.AlignmentFlag.AlignHCenter) - self.label_19.setObjectName("label_19") - self.gridLayout_3.addWidget(self.label_19, 1, 3, 1, 1) - spacerItem5 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.gridLayout_3.addItem(spacerItem5, 3, 0, 1, 1) - self.detection_serial_num_label = QtWidgets.QLabel(parent=self.detection_tab) - self.detection_serial_num_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop) - self.detection_serial_num_label.setObjectName("detection_serial_num_label") - self.gridLayout_3.addWidget(self.detection_serial_num_label, 2, 0, 1, 1) - self.detection_test_btn = QtWidgets.QPushButton(parent=self.detection_tab) - self.detection_test_btn.setObjectName("detection_test_btn") - self.gridLayout_3.addWidget(self.detection_test_btn, 0, 0, 1, 2) - self.label_18 = QtWidgets.QLabel(parent=self.detection_tab) - self.label_18.setAlignment(QtCore.Qt.AlignmentFlag.AlignBottom|QtCore.Qt.AlignmentFlag.AlignHCenter) - self.label_18.setObjectName("label_18") - self.gridLayout_3.addWidget(self.label_18, 1, 2, 1, 1) - self.label_21 = QtWidgets.QLabel(parent=self.detection_tab) - self.label_21.setObjectName("label_21") - self.gridLayout_3.addWidget(self.label_21, 4, 0, 1, 1) - self.label_16 = QtWidgets.QLabel(parent=self.detection_tab) - self.label_16.setAlignment(QtCore.Qt.AlignmentFlag.AlignBottom|QtCore.Qt.AlignmentFlag.AlignHCenter) - self.label_16.setObjectName("label_16") - self.gridLayout_3.addWidget(self.label_16, 1, 0, 1, 1) - self.label_22 = QtWidgets.QLabel(parent=self.detection_tab) - self.label_22.setObjectName("label_22") - self.gridLayout_3.addWidget(self.label_22, 4, 2, 1, 1) - self.detection_power_edit = QtWidgets.QLineEdit(parent=self.detection_tab) - self.detection_power_edit.setMaximumSize(QtCore.QSize(200, 16777215)) - self.detection_power_edit.setObjectName("detection_power_edit") - self.gridLayout_3.addWidget(self.detection_power_edit, 4, 1, 1, 1) - self.detection_interlock_label = QtWidgets.QLabel(parent=self.detection_tab) - self.detection_interlock_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop) - self.detection_interlock_label.setObjectName("detection_interlock_label") - self.gridLayout_3.addWidget(self.detection_interlock_label, 2, 1, 1, 1) - self.detection_keyswitch_label = QtWidgets.QLabel(parent=self.detection_tab) - self.detection_keyswitch_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop) - self.detection_keyswitch_label.setObjectName("detection_keyswitch_label") - self.gridLayout_3.addWidget(self.detection_keyswitch_label, 2, 2, 1, 1) - self.detection_temperature_label = QtWidgets.QLabel(parent=self.detection_tab) - self.detection_temperature_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop) - self.detection_temperature_label.setObjectName("detection_temperature_label") - self.gridLayout_3.addWidget(self.detection_temperature_label, 2, 4, 1, 1) - self.label_20 = QtWidgets.QLabel(parent=self.detection_tab) - self.label_20.setAlignment(QtCore.Qt.AlignmentFlag.AlignBottom|QtCore.Qt.AlignmentFlag.AlignHCenter) - self.label_20.setObjectName("label_20") - self.gridLayout_3.addWidget(self.label_20, 1, 4, 1, 1) - spacerItem6 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.gridLayout_3.addItem(spacerItem6, 5, 0, 1, 1) - self.verticalLayout_6.addLayout(self.gridLayout_3) - self.configuration_tabwidget.addTab(self.detection_tab, "") - self.generation_tab = QtWidgets.QWidget() - self.generation_tab.setObjectName("generation_tab") - self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.generation_tab) - self.verticalLayout_7.setObjectName("verticalLayout_7") - self.gridLayout_4 = QtWidgets.QGridLayout() - self.gridLayout_4.setObjectName("gridLayout_4") - self.label_30 = QtWidgets.QLabel(parent=self.generation_tab) - self.label_30.setAlignment(QtCore.Qt.AlignmentFlag.AlignBottom|QtCore.Qt.AlignmentFlag.AlignHCenter) - self.label_30.setObjectName("label_30") - self.gridLayout_4.addWidget(self.label_30, 4, 1, 1, 1) - self.diode_pump_current_edit = QtWidgets.QLineEdit(parent=self.generation_tab) - self.diode_pump_current_edit.setMaximumSize(QtCore.QSize(400, 16777215)) - self.diode_pump_current_edit.setObjectName("diode_pump_current_edit") - self.gridLayout_4.addWidget(self.diode_pump_current_edit, 2, 1, 1, 2) - self.label_26 = QtWidgets.QLabel(parent=self.generation_tab) - self.label_26.setObjectName("label_26") - self.gridLayout_4.addWidget(self.label_26, 2, 0, 1, 1) - self.label_23 = QtWidgets.QLabel(parent=self.generation_tab) - self.label_23.setObjectName("label_23") - self.gridLayout_4.addWidget(self.label_23, 0, 0, 1, 1) - self.refresh_serial_ports_btn = QtWidgets.QPushButton(parent=self.generation_tab) - self.refresh_serial_ports_btn.setObjectName("refresh_serial_ports_btn") - self.gridLayout_4.addWidget(self.refresh_serial_ports_btn, 0, 3, 1, 1) - self.label_27 = QtWidgets.QLabel(parent=self.generation_tab) - self.label_27.setObjectName("label_27") - self.gridLayout_4.addWidget(self.label_27, 2, 3, 1, 1) - self.label_33 = QtWidgets.QLabel(parent=self.generation_tab) - self.label_33.setAlignment(QtCore.Qt.AlignmentFlag.AlignBottom|QtCore.Qt.AlignmentFlag.AlignHCenter) - self.label_33.setObjectName("label_33") - self.gridLayout_4.addWidget(self.label_33, 4, 4, 1, 1) - self.label_31 = QtWidgets.QLabel(parent=self.generation_tab) - self.label_31.setAlignment(QtCore.Qt.AlignmentFlag.AlignBottom|QtCore.Qt.AlignmentFlag.AlignHCenter) - self.label_31.setObjectName("label_31") - self.gridLayout_4.addWidget(self.label_31, 4, 2, 1, 1) - self.label_32 = QtWidgets.QLabel(parent=self.generation_tab) - self.label_32.setAlignment(QtCore.Qt.AlignmentFlag.AlignBottom|QtCore.Qt.AlignmentFlag.AlignHCenter) - self.label_32.setObjectName("label_32") - self.gridLayout_4.addWidget(self.label_32, 4, 3, 1, 1) - self.generation_connect_button = QtWidgets.QPushButton(parent=self.generation_tab) - self.generation_connect_button.setObjectName("generation_connect_button") - self.gridLayout_4.addWidget(self.generation_connect_button, 0, 4, 1, 1) - self.comboBox = QtWidgets.QComboBox(parent=self.generation_tab) - self.comboBox.setObjectName("comboBox") - self.gridLayout_4.addWidget(self.comboBox, 0, 1, 1, 2) - self.label_35 = QtWidgets.QLabel(parent=self.generation_tab) - self.label_35.setAlignment(QtCore.Qt.AlignmentFlag.AlignBottom|QtCore.Qt.AlignmentFlag.AlignHCenter) - self.label_35.setObjectName("label_35") - self.gridLayout_4.addWidget(self.label_35, 4, 6, 1, 1) - self.label_24 = QtWidgets.QLabel(parent=self.generation_tab) - self.label_24.setObjectName("label_24") - self.gridLayout_4.addWidget(self.label_24, 1, 0, 1, 1) - self.label_34 = QtWidgets.QLabel(parent=self.generation_tab) - self.label_34.setAlignment(QtCore.Qt.AlignmentFlag.AlignBottom|QtCore.Qt.AlignmentFlag.AlignHCenter) - self.label_34.setObjectName("label_34") - self.gridLayout_4.addWidget(self.label_34, 4, 5, 1, 1) - self.generation_pulse_freq_edit = QtWidgets.QLineEdit(parent=self.generation_tab) - self.generation_pulse_freq_edit.setMaximumSize(QtCore.QSize(400, 16777215)) - self.generation_pulse_freq_edit.setObjectName("generation_pulse_freq_edit") - self.gridLayout_4.addWidget(self.generation_pulse_freq_edit, 1, 1, 1, 2) - self.label_28 = QtWidgets.QLabel(parent=self.generation_tab) - font = QtGui.QFont() - font.setFamily("Space Grotesk") - font.setPointSize(22) - self.label_28.setFont(font) - self.label_28.setAlignment(QtCore.Qt.AlignmentFlag.AlignBottom|QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft) - self.label_28.setObjectName("label_28") - self.gridLayout_4.addWidget(self.label_28, 3, 0, 1, 7) - self.label_25 = QtWidgets.QLabel(parent=self.generation_tab) - self.label_25.setObjectName("label_25") - self.gridLayout_4.addWidget(self.label_25, 1, 3, 1, 1) - self.label_29 = QtWidgets.QLabel(parent=self.generation_tab) - self.label_29.setAlignment(QtCore.Qt.AlignmentFlag.AlignBottom|QtCore.Qt.AlignmentFlag.AlignHCenter) - self.label_29.setObjectName("label_29") - self.gridLayout_4.addWidget(self.label_29, 4, 0, 1, 1) - self.generation_head_hours_lbl = QtWidgets.QLabel(parent=self.generation_tab) - self.generation_head_hours_lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop) - self.generation_head_hours_lbl.setObjectName("generation_head_hours_lbl") - self.gridLayout_4.addWidget(self.generation_head_hours_lbl, 5, 0, 1, 1) - self.generation_interlock_status_lbl = QtWidgets.QLabel(parent=self.generation_tab) - self.generation_interlock_status_lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop) - self.generation_interlock_status_lbl.setObjectName("generation_interlock_status_lbl") - self.gridLayout_4.addWidget(self.generation_interlock_status_lbl, 5, 1, 1, 1) - self.generation_enable_status_lbl = QtWidgets.QLabel(parent=self.generation_tab) - self.generation_enable_status_lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop) - self.generation_enable_status_lbl.setObjectName("generation_enable_status_lbl") - self.gridLayout_4.addWidget(self.generation_enable_status_lbl, 5, 2, 1, 1) - self.generation_warmup_lbl = QtWidgets.QLabel(parent=self.generation_tab) - self.generation_warmup_lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop) - self.generation_warmup_lbl.setObjectName("generation_warmup_lbl") - self.gridLayout_4.addWidget(self.generation_warmup_lbl, 5, 3, 1, 1) - self.generation_emission_lbl = QtWidgets.QLabel(parent=self.generation_tab) - self.generation_emission_lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop) - self.generation_emission_lbl.setObjectName("generation_emission_lbl") - self.gridLayout_4.addWidget(self.generation_emission_lbl, 5, 4, 1, 1) - self.generation_headtemp_lbl = QtWidgets.QLabel(parent=self.generation_tab) - self.generation_headtemp_lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop) - self.generation_headtemp_lbl.setObjectName("generation_headtemp_lbl") - self.gridLayout_4.addWidget(self.generation_headtemp_lbl, 5, 5, 1, 1) - self.generation_diodetemp_lbl = QtWidgets.QLabel(parent=self.generation_tab) - self.generation_diodetemp_lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop) - self.generation_diodetemp_lbl.setObjectName("generation_diodetemp_lbl") - self.gridLayout_4.addWidget(self.generation_diodetemp_lbl, 5, 6, 1, 1) - self.label_36 = QtWidgets.QLabel(parent=self.generation_tab) - self.label_36.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_36.setObjectName("label_36") - self.gridLayout_4.addWidget(self.label_36, 1, 4, 1, 1) - self.label_37 = QtWidgets.QLabel(parent=self.generation_tab) - self.label_37.setObjectName("label_37") - self.gridLayout_4.addWidget(self.label_37, 1, 5, 1, 1) - self.label_38 = QtWidgets.QLabel(parent=self.generation_tab) - font = QtGui.QFont() - font.setFamily("Space Grotesk") - font.setItalic(True) - font.setUnderline(True) - self.label_38.setFont(font) - self.label_38.setFrameShape(QtWidgets.QFrame.Shape.NoFrame) - self.label_38.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) - self.label_38.setObjectName("label_38") - self.gridLayout_4.addWidget(self.label_38, 2, 4, 1, 2) - self.verticalLayout_7.addLayout(self.gridLayout_4) - self.configuration_tabwidget.addTab(self.generation_tab, "") - self.pulsedecimator_tab = QtWidgets.QWidget() - self.pulsedecimator_tab.setObjectName("pulsedecimator_tab") - self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.pulsedecimator_tab) - self.verticalLayout_8.setObjectName("verticalLayout_8") - self.gridLayout_5 = QtWidgets.QGridLayout() - self.gridLayout_5.setObjectName("gridLayout_5") - self.label_49 = QtWidgets.QLabel(parent=self.pulsedecimator_tab) - self.label_49.setObjectName("label_49") - self.gridLayout_5.addWidget(self.label_49, 6, 3, 1, 1) - self.label_42 = QtWidgets.QLabel(parent=self.pulsedecimator_tab) - self.label_42.setObjectName("label_42") - self.gridLayout_5.addWidget(self.label_42, 4, 1, 1, 1) - spacerItem7 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.gridLayout_5.addItem(spacerItem7, 3, 0, 1, 1) - self.fpga_divider_value = QtWidgets.QLabel(parent=self.pulsedecimator_tab) - self.fpga_divider_value.setObjectName("fpga_divider_value") - self.gridLayout_5.addWidget(self.fpga_divider_value, 5, 1, 1, 1) - self.fpga_fw_version = QtWidgets.QLabel(parent=self.pulsedecimator_tab) - self.fpga_fw_version.setObjectName("fpga_fw_version") - self.gridLayout_5.addWidget(self.fpga_fw_version, 5, 0, 1, 1) - self.fpga_serial_port = QtWidgets.QComboBox(parent=self.pulsedecimator_tab) - self.fpga_serial_port.setObjectName("fpga_serial_port") - self.gridLayout_5.addWidget(self.fpga_serial_port, 0, 1, 1, 2) - self.fpga_divider_value_edit = QtWidgets.QLineEdit(parent=self.pulsedecimator_tab) - self.fpga_divider_value_edit.setMaximumSize(QtCore.QSize(150, 16777215)) - self.fpga_divider_value_edit.setObjectName("fpga_divider_value_edit") - self.gridLayout_5.addWidget(self.fpga_divider_value_edit, 1, 1, 1, 1) - self.fpga_refresh_ports_btn = QtWidgets.QPushButton(parent=self.pulsedecimator_tab) - self.fpga_refresh_ports_btn.setObjectName("fpga_refresh_ports_btn") - self.gridLayout_5.addWidget(self.fpga_refresh_ports_btn, 0, 3, 1, 1) - self.fpga_supports_rowpack = QtWidgets.QLabel(parent=self.pulsedecimator_tab) - self.fpga_supports_rowpack.setObjectName("fpga_supports_rowpack") - self.gridLayout_5.addWidget(self.fpga_supports_rowpack, 5, 2, 1, 1) - self.label_40 = QtWidgets.QLabel(parent=self.pulsedecimator_tab) - self.label_40.setObjectName("label_40") - self.gridLayout_5.addWidget(self.label_40, 1, 0, 1, 1) - self.label_41 = QtWidgets.QLabel(parent=self.pulsedecimator_tab) - self.label_41.setObjectName("label_41") - self.gridLayout_5.addWidget(self.label_41, 4, 0, 1, 1) - self.label_39 = QtWidgets.QLabel(parent=self.pulsedecimator_tab) - self.label_39.setObjectName("label_39") - self.gridLayout_5.addWidget(self.label_39, 0, 0, 1, 1) - self.checkBox = QtWidgets.QCheckBox(parent=self.pulsedecimator_tab) - self.checkBox.setObjectName("checkBox") - self.gridLayout_5.addWidget(self.checkBox, 2, 0, 1, 2) - self.label_43 = QtWidgets.QLabel(parent=self.pulsedecimator_tab) - self.label_43.setObjectName("label_43") - self.gridLayout_5.addWidget(self.label_43, 4, 2, 1, 1) - self.label_47 = QtWidgets.QLabel(parent=self.pulsedecimator_tab) - self.label_47.setObjectName("label_47") - self.gridLayout_5.addWidget(self.label_47, 4, 3, 1, 1) - spacerItem8 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.gridLayout_5.addItem(spacerItem8, 7, 0, 1, 1) - self.fpga_connect_button = QtWidgets.QPushButton(parent=self.pulsedecimator_tab) - self.fpga_connect_button.setObjectName("fpga_connect_button") - self.gridLayout_5.addWidget(self.fpga_connect_button, 0, 4, 1, 1) - self.fpga_pixel_size = QtWidgets.QLabel(parent=self.pulsedecimator_tab) - self.fpga_pixel_size.setObjectName("fpga_pixel_size") - self.gridLayout_5.addWidget(self.fpga_pixel_size, 5, 3, 1, 1) - self.verticalLayout_8.addLayout(self.gridLayout_5) - self.configuration_tabwidget.addTab(self.pulsedecimator_tab, "") - self.t3rsl_tab = QtWidgets.QWidget() - self.t3rsl_tab.setObjectName("t3rsl_tab") - self.verticalLayout_9 = QtWidgets.QVBoxLayout(self.t3rsl_tab) - self.verticalLayout_9.setObjectName("verticalLayout_9") - self.gridLayout_6 = QtWidgets.QGridLayout() - self.gridLayout_6.setObjectName("gridLayout_6") - self.label_60 = QtWidgets.QLabel(parent=self.t3rsl_tab) - self.label_60.setObjectName("label_60") - self.gridLayout_6.addWidget(self.label_60, 1, 3, 1, 1) - self.t3r_connect_btn = QtWidgets.QPushButton(parent=self.t3rsl_tab) - self.t3r_connect_btn.setObjectName("t3r_connect_btn") - self.gridLayout_6.addWidget(self.t3r_connect_btn, 0, 4, 1, 1) - spacerItem9 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) - self.gridLayout_6.addItem(spacerItem9, 0, 5, 1, 1) - self.lineEdit_2 = QtWidgets.QLineEdit(parent=self.t3rsl_tab) - self.lineEdit_2.setMaximumSize(QtCore.QSize(150, 16777215)) - self.lineEdit_2.setObjectName("lineEdit_2") - self.gridLayout_6.addWidget(self.lineEdit_2, 2, 1, 1, 1) - self.label_44 = QtWidgets.QLabel(parent=self.t3rsl_tab) - self.label_44.setObjectName("label_44") - self.gridLayout_6.addWidget(self.label_44, 0, 0, 1, 1) - self.comboBox_3 = QtWidgets.QComboBox(parent=self.t3rsl_tab) - self.comboBox_3.setMaximumSize(QtCore.QSize(200, 16777215)) - self.comboBox_3.setObjectName("comboBox_3") - self.gridLayout_6.addWidget(self.comboBox_3, 2, 4, 1, 1) - self.label_59 = QtWidgets.QLabel(parent=self.t3rsl_tab) - self.label_59.setObjectName("label_59") - self.gridLayout_6.addWidget(self.label_59, 2, 0, 1, 1) - self.t3r_refresh_ports_btn = QtWidgets.QPushButton(parent=self.t3rsl_tab) - self.t3r_refresh_ports_btn.setMaximumSize(QtCore.QSize(150, 16777215)) - self.t3r_refresh_ports_btn.setObjectName("t3r_refresh_ports_btn") - self.gridLayout_6.addWidget(self.t3r_refresh_ports_btn, 0, 3, 1, 1) - self.label_61 = QtWidgets.QLabel(parent=self.t3rsl_tab) - self.label_61.setObjectName("label_61") - self.gridLayout_6.addWidget(self.label_61, 2, 3, 1, 1) - self.label_58 = QtWidgets.QLabel(parent=self.t3rsl_tab) - self.label_58.setObjectName("label_58") - self.gridLayout_6.addWidget(self.label_58, 1, 0, 1, 1) - self.comboBox_2 = QtWidgets.QComboBox(parent=self.t3rsl_tab) - self.comboBox_2.setMaximumSize(QtCore.QSize(200, 16777215)) - self.comboBox_2.setObjectName("comboBox_2") - self.gridLayout_6.addWidget(self.comboBox_2, 1, 4, 1, 1) - self.lineEdit = QtWidgets.QLineEdit(parent=self.t3rsl_tab) - self.lineEdit.setMaximumSize(QtCore.QSize(150, 16777215)) - self.lineEdit.setObjectName("lineEdit") - self.gridLayout_6.addWidget(self.lineEdit, 1, 1, 1, 1) - self.label_62 = QtWidgets.QLabel(parent=self.t3rsl_tab) - self.label_62.setObjectName("label_62") - self.gridLayout_6.addWidget(self.label_62, 1, 2, 1, 1) - self.t3r_serial_port_edit = QtWidgets.QComboBox(parent=self.t3rsl_tab) - self.t3r_serial_port_edit.setMinimumSize(QtCore.QSize(350, 0)) - self.t3r_serial_port_edit.setObjectName("t3r_serial_port_edit") - self.gridLayout_6.addWidget(self.t3r_serial_port_edit, 0, 1, 1, 2) - self.label_63 = QtWidgets.QLabel(parent=self.t3rsl_tab) - self.label_63.setObjectName("label_63") - self.gridLayout_6.addWidget(self.label_63, 2, 2, 1, 1) - self.verticalLayout_9.addLayout(self.gridLayout_6) - self.configuration_tabwidget.addTab(self.t3rsl_tab, "") - self.oscilloscope_tab = QtWidgets.QWidget() - self.oscilloscope_tab.setObjectName("oscilloscope_tab") - self.verticalLayout_10 = QtWidgets.QVBoxLayout(self.oscilloscope_tab) - self.verticalLayout_10.setObjectName("verticalLayout_10") - self.gridLayout_7 = QtWidgets.QGridLayout() - self.gridLayout_7.setObjectName("gridLayout_7") - self.label_45 = QtWidgets.QLabel(parent=self.oscilloscope_tab) - self.label_45.setObjectName("label_45") - self.gridLayout_7.addWidget(self.label_45, 0, 0, 1, 1) - self.scope_ip_address_edit = QtWidgets.QLineEdit(parent=self.oscilloscope_tab) - self.scope_ip_address_edit.setObjectName("scope_ip_address_edit") - self.gridLayout_7.addWidget(self.scope_ip_address_edit, 0, 1, 1, 1) - self.scope_connect_btn = QtWidgets.QPushButton(parent=self.oscilloscope_tab) - self.scope_connect_btn.setObjectName("scope_connect_btn") - self.gridLayout_7.addWidget(self.scope_connect_btn, 0, 2, 1, 1) - self.verticalLayout_10.addLayout(self.gridLayout_7) - self.configuration_tabwidget.addTab(self.oscilloscope_tab, "") - self.verticalLayout_4.addWidget(self.configuration_tabwidget) - self.gridLayout.addLayout(self.verticalLayout_4, 0, 0, 1, 1) - self.horizontalLayout_4 = QtWidgets.QHBoxLayout() - self.horizontalLayout_4.setObjectName("horizontalLayout_4") - spacerItem10 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) - self.horizontalLayout_4.addItem(spacerItem10) - self.options_save_settings_btn = QtWidgets.QPushButton(parent=self.options_page) - self.options_save_settings_btn.setObjectName("options_save_settings_btn") - self.horizontalLayout_4.addWidget(self.options_save_settings_btn) - self.options_cancel_btn = QtWidgets.QPushButton(parent=self.options_page) - self.options_cancel_btn.setObjectName("options_cancel_btn") - self.horizontalLayout_4.addWidget(self.options_cancel_btn) - self.gridLayout.addLayout(self.horizontalLayout_4, 1, 0, 1, 1) - self.verticalLayout_3.addLayout(self.gridLayout) - self.stackedWidget.addWidget(self.options_page) - self.newscan_page_1 = QtWidgets.QWidget() - self.newscan_page_1.setObjectName("newscan_page_1") - self.verticalLayout_12 = QtWidgets.QVBoxLayout(self.newscan_page_1) - self.verticalLayout_12.setObjectName("verticalLayout_12") - self.verticalLayout_11 = QtWidgets.QVBoxLayout() - self.verticalLayout_11.setObjectName("verticalLayout_11") - self.label_46 = QtWidgets.QLabel(parent=self.newscan_page_1) - font = QtGui.QFont() - font.setFamily("Space Grotesk") - font.setPointSize(32) - self.label_46.setFont(font) - self.label_46.setObjectName("label_46") - self.verticalLayout_11.addWidget(self.label_46) - self.gridLayout_8 = QtWidgets.QGridLayout() - self.gridLayout_8.setObjectName("gridLayout_8") - self.label_69 = QtWidgets.QLabel(parent=self.newscan_page_1) - self.label_69.setObjectName("label_69") - self.gridLayout_8.addWidget(self.label_69, 13, 2, 1, 1) - self.verticalLayout_17 = QtWidgets.QVBoxLayout() - self.verticalLayout_17.setObjectName("verticalLayout_17") - spacerItem11 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.verticalLayout_17.addItem(spacerItem11) - self.label_72 = QtWidgets.QLabel(parent=self.newscan_page_1) - self.label_72.setObjectName("label_72") - self.verticalLayout_17.addWidget(self.label_72) - self.newscan_current_x_position_label = QtWidgets.QLabel(parent=self.newscan_page_1) - font = QtGui.QFont() - font.setFamily("Space Grotesk") - font.setPointSize(28) - self.newscan_current_x_position_label.setFont(font) - self.newscan_current_x_position_label.setObjectName("newscan_current_x_position_label") - self.verticalLayout_17.addWidget(self.newscan_current_x_position_label) - self.label_74 = QtWidgets.QLabel(parent=self.newscan_page_1) - self.label_74.setObjectName("label_74") - self.verticalLayout_17.addWidget(self.label_74) - self.newscan_current_y_position_label = QtWidgets.QLabel(parent=self.newscan_page_1) - font = QtGui.QFont() - font.setFamily("Space Grotesk") - font.setPointSize(28) - self.newscan_current_y_position_label.setFont(font) - self.newscan_current_y_position_label.setObjectName("newscan_current_y_position_label") - self.verticalLayout_17.addWidget(self.newscan_current_y_position_label) - spacerItem12 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.verticalLayout_17.addItem(spacerItem12) - self.newscan_set_current_as_start_btn = QtWidgets.QPushButton(parent=self.newscan_page_1) - self.newscan_set_current_as_start_btn.setObjectName("newscan_set_current_as_start_btn") - self.verticalLayout_17.addWidget(self.newscan_set_current_as_start_btn) - self.newscan_get_delta_from_current_btn = QtWidgets.QPushButton(parent=self.newscan_page_1) - self.newscan_get_delta_from_current_btn.setObjectName("newscan_get_delta_from_current_btn") - self.verticalLayout_17.addWidget(self.newscan_get_delta_from_current_btn) - spacerItem13 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.verticalLayout_17.addItem(spacerItem13) - self.gridLayout_8.addLayout(self.verticalLayout_17, 14, 1, 1, 1) - self.label_48 = QtWidgets.QLabel(parent=self.newscan_page_1) - self.label_48.setMaximumSize(QtCore.QSize(16777213, 16777215)) - self.label_48.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_48.setObjectName("label_48") - self.gridLayout_8.addWidget(self.label_48, 0, 0, 1, 1) - spacerItem14 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) - self.gridLayout_8.addItem(spacerItem14, 14, 3, 1, 1) - spacerItem15 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.gridLayout_8.addItem(spacerItem15, 10, 5, 1, 1) - self.newscan_50_micron_radio = QtWidgets.QRadioButton(parent=self.newscan_page_1) - self.newscan_50_micron_radio.setObjectName("newscan_50_micron_radio") - self.gridLayout_8.addWidget(self.newscan_50_micron_radio, 7, 1, 1, 1) - self.label_56 = QtWidgets.QLabel(parent=self.newscan_page_1) - self.label_56.setMaximumSize(QtCore.QSize(16777213, 16777215)) - self.label_56.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_56.setObjectName("label_56") - self.gridLayout_8.addWidget(self.label_56, 9, 0, 1, 1) - self.label_66 = QtWidgets.QLabel(parent=self.newscan_page_1) - font = QtGui.QFont() - font.setFamily("Space Grotesk") - font.setPointSize(24) - self.label_66.setFont(font) - self.label_66.setObjectName("label_66") - self.gridLayout_8.addWidget(self.label_66, 11, 0, 1, 6) - self.newscan_camera_preview = QtWidgets.QWidget(parent=self.newscan_page_1) - self.newscan_camera_preview.setMinimumSize(QtCore.QSize(480, 480)) - self.newscan_camera_preview.setMaximumSize(QtCore.QSize(480, 480)) - self.newscan_camera_preview.setStyleSheet("QWidget {\n" -" border: 1px solid #FFF\n" -"}") - self.newscan_camera_preview.setObjectName("newscan_camera_preview") - self.gridLayout_8.addWidget(self.newscan_camera_preview, 14, 5, 1, 1) - spacerItem16 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) - self.gridLayout_8.addItem(spacerItem16, 1, 5, 1, 1) - self.label_51 = QtWidgets.QLabel(parent=self.newscan_page_1) - self.label_51.setMaximumSize(QtCore.QSize(16777213, 16777215)) - self.label_51.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_51.setObjectName("label_51") - self.gridLayout_8.addWidget(self.label_51, 3, 0, 1, 1) - self.label_57 = QtWidgets.QLabel(parent=self.newscan_page_1) - self.label_57.setMaximumSize(QtCore.QSize(16777213, 16777215)) - self.label_57.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_57.setObjectName("label_57") - self.gridLayout_8.addWidget(self.label_57, 7, 0, 1, 1) - self.label_54 = QtWidgets.QLabel(parent=self.newscan_page_1) - self.label_54.setMaximumSize(QtCore.QSize(16777213, 16777215)) - self.label_54.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_54.setObjectName("label_54") - self.gridLayout_8.addWidget(self.label_54, 3, 2, 1, 1) - self.label_53 = QtWidgets.QLabel(parent=self.newscan_page_1) - self.label_53.setMaximumSize(QtCore.QSize(16777213, 16777215)) - self.label_53.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_53.setObjectName("label_53") - self.gridLayout_8.addWidget(self.label_53, 1, 2, 1, 1) - self.verticalLayout_16 = QtWidgets.QVBoxLayout() - self.verticalLayout_16.setObjectName("verticalLayout_16") - self.newscan_toggle_vis_laser_btn = QtWidgets.QPushButton(parent=self.newscan_page_1) - self.newscan_toggle_vis_laser_btn.setMaximumSize(QtCore.QSize(150, 16777215)) - self.newscan_toggle_vis_laser_btn.setObjectName("newscan_toggle_vis_laser_btn") - self.verticalLayout_16.addWidget(self.newscan_toggle_vis_laser_btn, 0, QtCore.Qt.AlignmentFlag.AlignHCenter) - self.label_70 = QtWidgets.QLabel(parent=self.newscan_page_1) - self.label_70.setMaximumSize(QtCore.QSize(16777215, 16777215)) - self.label_70.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) - self.label_70.setObjectName("label_70") - self.verticalLayout_16.addWidget(self.label_70) - self.newscan_vis_laser_power_edit = QtWidgets.QLineEdit(parent=self.newscan_page_1) - self.newscan_vis_laser_power_edit.setMaximumSize(QtCore.QSize(50, 16777215)) - self.newscan_vis_laser_power_edit.setObjectName("newscan_vis_laser_power_edit") - self.verticalLayout_16.addWidget(self.newscan_vis_laser_power_edit, 0, QtCore.Qt.AlignmentFlag.AlignHCenter) - spacerItem17 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.verticalLayout_16.addItem(spacerItem17) - self.gridLayout_8.addLayout(self.verticalLayout_16, 14, 2, 1, 1) - self.newscan_friendly_name_edit = QtWidgets.QLineEdit(parent=self.newscan_page_1) - self.newscan_friendly_name_edit.setObjectName("newscan_friendly_name_edit") - self.gridLayout_8.addWidget(self.newscan_friendly_name_edit, 0, 1, 1, 4) - self.newscan_num_angles_combo = QtWidgets.QComboBox(parent=self.newscan_page_1) - self.newscan_num_angles_combo.setMaximumSize(QtCore.QSize(100, 16777215)) - self.newscan_num_angles_combo.setObjectName("newscan_num_angles_combo") - self.gridLayout_8.addWidget(self.newscan_num_angles_combo, 4, 1, 1, 4) - self.newscan_100_micron_radio = QtWidgets.QRadioButton(parent=self.newscan_page_1) - self.newscan_100_micron_radio.setObjectName("newscan_100_micron_radio") - self.gridLayout_8.addWidget(self.newscan_100_micron_radio, 7, 2, 1, 1) - self.newscan_start_x_coord_edit = QtWidgets.QLineEdit(parent=self.newscan_page_1) - self.newscan_start_x_coord_edit.setMaximumSize(QtCore.QSize(100, 16777215)) - self.newscan_start_x_coord_edit.setObjectName("newscan_start_x_coord_edit") - self.gridLayout_8.addWidget(self.newscan_start_x_coord_edit, 1, 1, 1, 1) - self.label_67 = QtWidgets.QLabel(parent=self.newscan_page_1) - self.label_67.setObjectName("label_67") - self.gridLayout_8.addWidget(self.label_67, 13, 0, 1, 1) - self.newscan_delta_x_coord_edit = QtWidgets.QLineEdit(parent=self.newscan_page_1) - self.newscan_delta_x_coord_edit.setMaximumSize(QtCore.QSize(100, 16777215)) - self.newscan_delta_x_coord_edit.setObjectName("newscan_delta_x_coord_edit") - self.gridLayout_8.addWidget(self.newscan_delta_x_coord_edit, 3, 1, 1, 1) - self.label_76 = QtWidgets.QLabel(parent=self.newscan_page_1) - self.label_76.setMaximumSize(QtCore.QSize(480, 16777215)) - self.label_76.setObjectName("label_76") - self.gridLayout_8.addWidget(self.label_76, 13, 4, 1, 1) - self.label_50 = QtWidgets.QLabel(parent=self.newscan_page_1) - self.label_50.setMaximumSize(QtCore.QSize(16777213, 16777215)) - self.label_50.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_50.setObjectName("label_50") - self.gridLayout_8.addWidget(self.label_50, 1, 0, 1, 1) - self.label_52 = QtWidgets.QLabel(parent=self.newscan_page_1) - self.label_52.setMaximumSize(QtCore.QSize(16777213, 16777215)) - self.label_52.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_52.setObjectName("label_52") - self.gridLayout_8.addWidget(self.label_52, 4, 0, 1, 1) - self.newcsan_file_prefix_edit = QtWidgets.QLineEdit(parent=self.newscan_page_1) - self.newcsan_file_prefix_edit.setObjectName("newcsan_file_prefix_edit") - self.gridLayout_8.addWidget(self.newcsan_file_prefix_edit, 8, 1, 1, 4) - self.newscan_browse_folders_btn = QtWidgets.QPushButton(parent=self.newscan_page_1) - self.newscan_browse_folders_btn.setMaximumSize(QtCore.QSize(150, 16777215)) - self.newscan_browse_folders_btn.setObjectName("newscan_browse_folders_btn") - self.gridLayout_8.addWidget(self.newscan_browse_folders_btn, 9, 5, 1, 1) - self.verticalLayout_18 = QtWidgets.QVBoxLayout() - self.verticalLayout_18.setObjectName("verticalLayout_18") - spacerItem18 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.verticalLayout_18.addItem(spacerItem18) - self.label_77 = QtWidgets.QLabel(parent=self.newscan_page_1) - self.label_77.setObjectName("label_77") - self.verticalLayout_18.addWidget(self.label_77) - self.newscan_ccd_exposure_hslide = QtWidgets.QSlider(parent=self.newscan_page_1) - self.newscan_ccd_exposure_hslide.setOrientation(QtCore.Qt.Orientation.Horizontal) - self.newscan_ccd_exposure_hslide.setObjectName("newscan_ccd_exposure_hslide") - self.verticalLayout_18.addWidget(self.newscan_ccd_exposure_hslide) - self.label_78 = QtWidgets.QLabel(parent=self.newscan_page_1) - self.label_78.setObjectName("label_78") - self.verticalLayout_18.addWidget(self.label_78) - self.newscan_ccd_ampgain_hslide = QtWidgets.QSlider(parent=self.newscan_page_1) - self.newscan_ccd_ampgain_hslide.setOrientation(QtCore.Qt.Orientation.Horizontal) - self.newscan_ccd_ampgain_hslide.setObjectName("newscan_ccd_ampgain_hslide") - self.verticalLayout_18.addWidget(self.newscan_ccd_ampgain_hslide) - spacerItem19 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.verticalLayout_18.addItem(spacerItem19) - self.gridLayout_8.addLayout(self.verticalLayout_18, 14, 4, 1, 1) - self.verticalLayout_14 = QtWidgets.QVBoxLayout() - self.verticalLayout_14.setObjectName("verticalLayout_14") - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.newscan_jog_x_pos_btn = QtWidgets.QPushButton(parent=self.newscan_page_1) - self.newscan_jog_x_pos_btn.setObjectName("newscan_jog_x_pos_btn") - self.horizontalLayout.addWidget(self.newscan_jog_x_pos_btn) - self.verticalLayout_15 = QtWidgets.QVBoxLayout() - self.verticalLayout_15.setObjectName("verticalLayout_15") - spacerItem20 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.verticalLayout_15.addItem(spacerItem20) - self.newscan_jog_y_pos_btn = QtWidgets.QPushButton(parent=self.newscan_page_1) - self.newscan_jog_y_pos_btn.setObjectName("newscan_jog_y_pos_btn") - self.verticalLayout_15.addWidget(self.newscan_jog_y_pos_btn) - self.label_68 = QtWidgets.QLabel(parent=self.newscan_page_1) - self.label_68.setObjectName("label_68") - self.verticalLayout_15.addWidget(self.label_68) - self.newscan_jog_speed_edit = QtWidgets.QLineEdit(parent=self.newscan_page_1) - self.newscan_jog_speed_edit.setMaximumSize(QtCore.QSize(75, 16777215)) - self.newscan_jog_speed_edit.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) - self.newscan_jog_speed_edit.setObjectName("newscan_jog_speed_edit") - self.verticalLayout_15.addWidget(self.newscan_jog_speed_edit, 0, QtCore.Qt.AlignmentFlag.AlignHCenter) - self.newscan_jog_y_neg_btn = QtWidgets.QPushButton(parent=self.newscan_page_1) - self.newscan_jog_y_neg_btn.setObjectName("newscan_jog_y_neg_btn") - self.verticalLayout_15.addWidget(self.newscan_jog_y_neg_btn) - spacerItem21 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.verticalLayout_15.addItem(spacerItem21) - self.horizontalLayout.addLayout(self.verticalLayout_15) - self.newscan_jog_x_neg_btn = QtWidgets.QPushButton(parent=self.newscan_page_1) - self.newscan_jog_x_neg_btn.setObjectName("newscan_jog_x_neg_btn") - self.horizontalLayout.addWidget(self.newscan_jog_x_neg_btn) - self.verticalLayout_14.addLayout(self.horizontalLayout) - self.gridLayout_8.addLayout(self.verticalLayout_14, 14, 0, 1, 1) - self.label_71 = QtWidgets.QLabel(parent=self.newscan_page_1) - self.label_71.setMaximumSize(QtCore.QSize(480, 16777215)) - self.label_71.setObjectName("label_71") - self.gridLayout_8.addWidget(self.label_71, 13, 5, 1, 1) - self.label_55 = QtWidgets.QLabel(parent=self.newscan_page_1) - self.label_55.setMaximumSize(QtCore.QSize(16777213, 16777215)) - self.label_55.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_55.setObjectName("label_55") - self.gridLayout_8.addWidget(self.label_55, 8, 0, 1, 1) - self.newscan_save_directory_edit = QtWidgets.QLineEdit(parent=self.newscan_page_1) - self.newscan_save_directory_edit.setMinimumSize(QtCore.QSize(400, 0)) - self.newscan_save_directory_edit.setObjectName("newscan_save_directory_edit") - self.gridLayout_8.addWidget(self.newscan_save_directory_edit, 9, 1, 1, 4) - self.newscan_start_y_coord_edit = QtWidgets.QLineEdit(parent=self.newscan_page_1) - self.newscan_start_y_coord_edit.setMaximumSize(QtCore.QSize(100, 16777215)) - self.newscan_start_y_coord_edit.setObjectName("newscan_start_y_coord_edit") - self.gridLayout_8.addWidget(self.newscan_start_y_coord_edit, 1, 3, 1, 1) - self.newscan_delta_y_coord_edit = QtWidgets.QLineEdit(parent=self.newscan_page_1) - self.newscan_delta_y_coord_edit.setMaximumSize(QtCore.QSize(100, 16777215)) - self.newscan_delta_y_coord_edit.setObjectName("newscan_delta_y_coord_edit") - self.gridLayout_8.addWidget(self.newscan_delta_y_coord_edit, 3, 3, 1, 1) - self.newscan_250_micron_radio = QtWidgets.QRadioButton(parent=self.newscan_page_1) - self.newscan_250_micron_radio.setObjectName("newscan_250_micron_radio") - self.gridLayout_8.addWidget(self.newscan_250_micron_radio, 7, 3, 1, 1) - self.verticalLayout_11.addLayout(self.gridLayout_8) - self.verticalLayout_12.addLayout(self.verticalLayout_11) - self.newscan_continue_to_next_btn = QtWidgets.QPushButton(parent=self.newscan_page_1) - self.newscan_continue_to_next_btn.setObjectName("newscan_continue_to_next_btn") - self.verticalLayout_12.addWidget(self.newscan_continue_to_next_btn) - self.stackedWidget.addWidget(self.newscan_page_1) - self.continuescan_page_1 = QtWidgets.QWidget() - self.continuescan_page_1.setObjectName("continuescan_page_1") - self.verticalLayout_13 = QtWidgets.QVBoxLayout(self.continuescan_page_1) - self.verticalLayout_13.setObjectName("verticalLayout_13") - self.gridLayout_9 = QtWidgets.QGridLayout() - self.gridLayout_9.setObjectName("gridLayout_9") - self.label_79 = QtWidgets.QLabel(parent=self.continuescan_page_1) - font = QtGui.QFont() - font.setFamily("Space Grotesk") - font.setPointSize(20) - self.label_79.setFont(font) - self.label_79.setObjectName("label_79") - self.gridLayout_9.addWidget(self.label_79, 6, 0, 1, 4) - self.label_83 = QtWidgets.QLabel(parent=self.continuescan_page_1) - self.label_83.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_83.setObjectName("label_83") - self.gridLayout_9.addWidget(self.label_83, 9, 0, 1, 1) - self.label_85 = QtWidgets.QLabel(parent=self.continuescan_page_1) - self.label_85.setMinimumSize(QtCore.QSize(150, 0)) - self.label_85.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_85.setObjectName("label_85") - self.gridLayout_9.addWidget(self.label_85, 10, 2, 1, 1) - self.label_75 = QtWidgets.QLabel(parent=self.continuescan_page_1) - self.label_75.setObjectName("label_75") - self.gridLayout_9.addWidget(self.label_75, 3, 0, 1, 1) - self.continuescan_x_delta_label = QtWidgets.QLabel(parent=self.continuescan_page_1) - self.continuescan_x_delta_label.setObjectName("continuescan_x_delta_label") - self.gridLayout_9.addWidget(self.continuescan_x_delta_label, 8, 3, 1, 1) - self.lineEdit_3 = QtWidgets.QLineEdit(parent=self.continuescan_page_1) - self.lineEdit_3.setObjectName("lineEdit_3") - self.gridLayout_9.addWidget(self.lineEdit_3, 3, 1, 1, 3) - self.continuescan_x_start_label = QtWidgets.QLabel(parent=self.continuescan_page_1) - self.continuescan_x_start_label.setObjectName("continuescan_x_start_label") - self.gridLayout_9.addWidget(self.continuescan_x_start_label, 8, 1, 1, 1) - self.continuescan_num_angles_label = QtWidgets.QLabel(parent=self.continuescan_page_1) - self.continuescan_num_angles_label.setObjectName("continuescan_num_angles_label") - self.gridLayout_9.addWidget(self.continuescan_num_angles_label, 10, 1, 1, 1) - self.continuescan_pixel_size_label = QtWidgets.QLabel(parent=self.continuescan_page_1) - self.continuescan_pixel_size_label.setObjectName("continuescan_pixel_size_label") - self.gridLayout_9.addWidget(self.continuescan_pixel_size_label, 10, 3, 1, 1) - self.label_84 = QtWidgets.QLabel(parent=self.continuescan_page_1) - self.label_84.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_84.setObjectName("label_84") - self.gridLayout_9.addWidget(self.label_84, 10, 0, 1, 1) - self.label_80 = QtWidgets.QLabel(parent=self.continuescan_page_1) - self.label_80.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_80.setObjectName("label_80") - self.gridLayout_9.addWidget(self.label_80, 7, 0, 1, 1) - self.pushButton = QtWidgets.QPushButton(parent=self.continuescan_page_1) - self.pushButton.setMaximumSize(QtCore.QSize(150, 16777215)) - self.pushButton.setObjectName("pushButton") - self.gridLayout_9.addWidget(self.pushButton, 3, 4, 1, 1) - spacerItem22 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.gridLayout_9.addItem(spacerItem22, 12, 1, 1, 1) - self.label_73 = QtWidgets.QLabel(parent=self.continuescan_page_1) - font = QtGui.QFont() - font.setFamily("Space Grotesk") - font.setPointSize(32) - self.label_73.setFont(font) - self.label_73.setObjectName("label_73") - self.gridLayout_9.addWidget(self.label_73, 1, 0, 1, 6) - self.label_90 = QtWidgets.QLabel(parent=self.continuescan_page_1) - self.label_90.setMinimumSize(QtCore.QSize(150, 0)) - self.label_90.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_90.setObjectName("label_90") - self.gridLayout_9.addWidget(self.label_90, 9, 2, 1, 1) - self.continuescan_y_delta_label = QtWidgets.QLabel(parent=self.continuescan_page_1) - self.continuescan_y_delta_label.setObjectName("continuescan_y_delta_label") - self.gridLayout_9.addWidget(self.continuescan_y_delta_label, 9, 3, 1, 1) - self.continuescan_last_complete_scan = QtWidgets.QLabel(parent=self.continuescan_page_1) - self.continuescan_last_complete_scan.setObjectName("continuescan_last_complete_scan") - self.gridLayout_9.addWidget(self.continuescan_last_complete_scan, 11, 1, 1, 1) - self.continuescan_y_start_label = QtWidgets.QLabel(parent=self.continuescan_page_1) - self.continuescan_y_start_label.setObjectName("continuescan_y_start_label") - self.gridLayout_9.addWidget(self.continuescan_y_start_label, 9, 1, 1, 1) - self.label_86 = QtWidgets.QLabel(parent=self.continuescan_page_1) - self.label_86.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_86.setObjectName("label_86") - self.gridLayout_9.addWidget(self.label_86, 11, 0, 1, 1) - self.continuescan_friendlyname = QtWidgets.QLabel(parent=self.continuescan_page_1) - self.continuescan_friendlyname.setObjectName("continuescan_friendlyname") - self.gridLayout_9.addWidget(self.continuescan_friendlyname, 7, 1, 1, 3) - self.label_87 = QtWidgets.QLabel(parent=self.continuescan_page_1) - self.label_87.setMinimumSize(QtCore.QSize(150, 0)) - self.label_87.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_87.setObjectName("label_87") - self.gridLayout_9.addWidget(self.label_87, 8, 2, 1, 1) - self.label_82 = QtWidgets.QLabel(parent=self.continuescan_page_1) - self.label_82.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) - self.label_82.setObjectName("label_82") - self.gridLayout_9.addWidget(self.label_82, 8, 0, 1, 1) - spacerItem23 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.gridLayout_9.addItem(spacerItem23, 5, 0, 1, 1) - self.pushButton_2 = QtWidgets.QPushButton(parent=self.continuescan_page_1) - self.pushButton_2.setObjectName("pushButton_2") - self.gridLayout_9.addWidget(self.pushButton_2, 3, 5, 1, 1) - spacerItem24 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) - self.gridLayout_9.addItem(spacerItem24, 8, 4, 1, 2) - spacerItem25 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) - self.gridLayout_9.addItem(spacerItem25, 3, 6, 1, 1) - self.verticalLayout_13.addLayout(self.gridLayout_9) - self.continuescan_resume_scans = QtWidgets.QPushButton(parent=self.continuescan_page_1) - self.continuescan_resume_scans.setObjectName("continuescan_resume_scans") - self.verticalLayout_13.addWidget(self.continuescan_resume_scans) - self.stackedWidget.addWidget(self.continuescan_page_1) - self.scan_progress_page = QtWidgets.QWidget() - self.scan_progress_page.setObjectName("scan_progress_page") - self.verticalLayout_19 = QtWidgets.QVBoxLayout(self.scan_progress_page) - self.verticalLayout_19.setObjectName("verticalLayout_19") - self.gridLayout_11 = QtWidgets.QGridLayout() - self.gridLayout_11.setObjectName("gridLayout_11") - self.scanning_scan_progbar = QtWidgets.QProgressBar(parent=self.scan_progress_page) - self.scanning_scan_progbar.setProperty("value", 24) - self.scanning_scan_progbar.setObjectName("scanning_scan_progbar") - self.gridLayout_11.addWidget(self.scanning_scan_progbar, 7, 0, 1, 1) - self.verticalLayout_20 = QtWidgets.QVBoxLayout() - self.verticalLayout_20.setObjectName("verticalLayout_20") - self.label_102 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_102.setObjectName("label_102") - self.verticalLayout_20.addWidget(self.label_102) - self.horizontalLayout_3 = QtWidgets.QHBoxLayout() - self.horizontalLayout_3.setObjectName("horizontalLayout_3") - self.label_105 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_105.setObjectName("label_105") - self.horizontalLayout_3.addWidget(self.label_105) - self.scanning_x_position_label = QtWidgets.QLabel(parent=self.scan_progress_page) - self.scanning_x_position_label.setObjectName("scanning_x_position_label") - self.horizontalLayout_3.addWidget(self.scanning_x_position_label) - self.verticalLayout_20.addLayout(self.horizontalLayout_3) - self.horizontalLayout_5 = QtWidgets.QHBoxLayout() - self.horizontalLayout_5.setObjectName("horizontalLayout_5") - self.label_109 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_109.setObjectName("label_109") - self.horizontalLayout_5.addWidget(self.label_109) - self.scanning_y_position_label = QtWidgets.QLabel(parent=self.scan_progress_page) - self.scanning_y_position_label.setObjectName("scanning_y_position_label") - self.horizontalLayout_5.addWidget(self.scanning_y_position_label) - self.verticalLayout_20.addLayout(self.horizontalLayout_5) - self.horizontalLayout_2 = QtWidgets.QHBoxLayout() - self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.label_103 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_103.setObjectName("label_103") - self.horizontalLayout_2.addWidget(self.label_103) - self.scanning_stage_state_label = QtWidgets.QLabel(parent=self.scan_progress_page) - self.scanning_stage_state_label.setObjectName("scanning_stage_state_label") - self.horizontalLayout_2.addWidget(self.scanning_stage_state_label) - self.verticalLayout_20.addLayout(self.horizontalLayout_2) - spacerItem26 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.verticalLayout_20.addItem(spacerItem26) - self.label_111 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_111.setObjectName("label_111") - self.verticalLayout_20.addWidget(self.label_111) - self.horizontalLayout_8 = QtWidgets.QHBoxLayout() - self.horizontalLayout_8.setObjectName("horizontalLayout_8") - self.label_116 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_116.setObjectName("label_116") - self.horizontalLayout_8.addWidget(self.label_116) - self.scanning_vis_status_label = QtWidgets.QLabel(parent=self.scan_progress_page) - self.scanning_vis_status_label.setObjectName("scanning_vis_status_label") - self.horizontalLayout_8.addWidget(self.scanning_vis_status_label) - self.verticalLayout_20.addLayout(self.horizontalLayout_8) - self.horizontalLayout_9 = QtWidgets.QHBoxLayout() - self.horizontalLayout_9.setObjectName("horizontalLayout_9") - self.label_118 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_118.setObjectName("label_118") - self.horizontalLayout_9.addWidget(self.label_118) - self.scanning_vis_power_label = QtWidgets.QLabel(parent=self.scan_progress_page) - self.scanning_vis_power_label.setObjectName("scanning_vis_power_label") - self.horizontalLayout_9.addWidget(self.scanning_vis_power_label) - self.verticalLayout_20.addLayout(self.horizontalLayout_9) - self.horizontalLayout_10 = QtWidgets.QHBoxLayout() - self.horizontalLayout_10.setObjectName("horizontalLayout_10") - self.label_120 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_120.setObjectName("label_120") - self.horizontalLayout_10.addWidget(self.label_120) - self.scanning_vis_temperature_label = QtWidgets.QLabel(parent=self.scan_progress_page) - self.scanning_vis_temperature_label.setObjectName("scanning_vis_temperature_label") - self.horizontalLayout_10.addWidget(self.scanning_vis_temperature_label) - self.verticalLayout_20.addLayout(self.horizontalLayout_10) - self.horizontalLayout_6 = QtWidgets.QHBoxLayout() - self.horizontalLayout_6.setObjectName("horizontalLayout_6") - self.label_112 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_112.setObjectName("label_112") - self.horizontalLayout_6.addWidget(self.label_112) - self.scanning_vis_error_label = QtWidgets.QLabel(parent=self.scan_progress_page) - self.scanning_vis_error_label.setObjectName("scanning_vis_error_label") - self.horizontalLayout_6.addWidget(self.scanning_vis_error_label) - self.verticalLayout_20.addLayout(self.horizontalLayout_6) - spacerItem27 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.verticalLayout_20.addItem(spacerItem27) - self.label_122 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_122.setObjectName("label_122") - self.verticalLayout_20.addWidget(self.label_122) - self.horizontalLayout_12 = QtWidgets.QHBoxLayout() - self.horizontalLayout_12.setObjectName("horizontalLayout_12") - self.label_125 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_125.setObjectName("label_125") - self.horizontalLayout_12.addWidget(self.label_125) - self.scanning_ir_status_label = QtWidgets.QLabel(parent=self.scan_progress_page) - self.scanning_ir_status_label.setObjectName("scanning_ir_status_label") - self.horizontalLayout_12.addWidget(self.scanning_ir_status_label) - self.verticalLayout_20.addLayout(self.horizontalLayout_12) - self.horizontalLayout_15 = QtWidgets.QHBoxLayout() - self.horizontalLayout_15.setObjectName("horizontalLayout_15") - self.label_131 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_131.setObjectName("label_131") - self.horizontalLayout_15.addWidget(self.label_131) - self.scanning_ir_freq_label = QtWidgets.QLabel(parent=self.scan_progress_page) - self.scanning_ir_freq_label.setObjectName("scanning_ir_freq_label") - self.horizontalLayout_15.addWidget(self.scanning_ir_freq_label) - self.verticalLayout_20.addLayout(self.horizontalLayout_15) - self.horizontalLayout_16 = QtWidgets.QHBoxLayout() - self.horizontalLayout_16.setObjectName("horizontalLayout_16") - self.label_133 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_133.setObjectName("label_133") - self.horizontalLayout_16.addWidget(self.label_133) - self.scanning_ir_current_label = QtWidgets.QLabel(parent=self.scan_progress_page) - self.scanning_ir_current_label.setObjectName("scanning_ir_current_label") - self.horizontalLayout_16.addWidget(self.scanning_ir_current_label) - self.verticalLayout_20.addLayout(self.horizontalLayout_16) - self.horizontalLayout_17 = QtWidgets.QHBoxLayout() - self.horizontalLayout_17.setObjectName("horizontalLayout_17") - self.label_135 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_135.setObjectName("label_135") - self.horizontalLayout_17.addWidget(self.label_135) - self.scanning_ir_headhours_label = QtWidgets.QLabel(parent=self.scan_progress_page) - self.scanning_ir_headhours_label.setObjectName("scanning_ir_headhours_label") - self.horizontalLayout_17.addWidget(self.scanning_ir_headhours_label) - self.verticalLayout_20.addLayout(self.horizontalLayout_17) - self.horizontalLayout_11 = QtWidgets.QHBoxLayout() - self.horizontalLayout_11.setObjectName("horizontalLayout_11") - self.label_123 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_123.setObjectName("label_123") - self.horizontalLayout_11.addWidget(self.label_123) - self.scanning_ir_temp_label = QtWidgets.QLabel(parent=self.scan_progress_page) - self.scanning_ir_temp_label.setObjectName("scanning_ir_temp_label") - self.horizontalLayout_11.addWidget(self.scanning_ir_temp_label) - self.verticalLayout_20.addLayout(self.horizontalLayout_11) - self.horizontalLayout_20 = QtWidgets.QHBoxLayout() - self.horizontalLayout_20.setObjectName("horizontalLayout_20") - self.label_142 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_142.setObjectName("label_142") - self.horizontalLayout_20.addWidget(self.label_142) - self.scanning_ir_error_label = QtWidgets.QLabel(parent=self.scan_progress_page) - self.scanning_ir_error_label.setObjectName("scanning_ir_error_label") - self.horizontalLayout_20.addWidget(self.scanning_ir_error_label) - self.verticalLayout_20.addLayout(self.horizontalLayout_20) - spacerItem28 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.verticalLayout_20.addItem(spacerItem28) - self.label_137 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_137.setObjectName("label_137") - self.verticalLayout_20.addWidget(self.label_137) - self.horizontalLayout_19 = QtWidgets.QHBoxLayout() - self.horizontalLayout_19.setObjectName("horizontalLayout_19") - self.label_140 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_140.setObjectName("label_140") - self.horizontalLayout_19.addWidget(self.label_140) - self.scanning_pulsedivider_status_label = QtWidgets.QLabel(parent=self.scan_progress_page) - self.scanning_pulsedivider_status_label.setObjectName("scanning_pulsedivider_status_label") - self.horizontalLayout_19.addWidget(self.scanning_pulsedivider_status_label) - self.verticalLayout_20.addLayout(self.horizontalLayout_19) - self.horizontalLayout_18 = QtWidgets.QHBoxLayout() - self.horizontalLayout_18.setObjectName("horizontalLayout_18") - self.label_138 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_138.setObjectName("label_138") - self.horizontalLayout_18.addWidget(self.label_138) - self.scanning_pulsedivider_divider_label = QtWidgets.QLabel(parent=self.scan_progress_page) - self.scanning_pulsedivider_divider_label.setObjectName("scanning_pulsedivider_divider_label") - self.horizontalLayout_18.addWidget(self.scanning_pulsedivider_divider_label) - self.verticalLayout_20.addLayout(self.horizontalLayout_18) - self.gridLayout_11.addLayout(self.verticalLayout_20, 1, 1, 10, 1) - spacerItem29 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) - self.gridLayout_11.addItem(spacerItem29, 11, 1, 1, 1) - self.label_99 = QtWidgets.QLabel(parent=self.scan_progress_page) - font = QtGui.QFont() - font.setFamily("Space Grotesk") - font.setPointSize(32) - self.label_99.setFont(font) - self.label_99.setObjectName("label_99") - self.gridLayout_11.addWidget(self.label_99, 0, 0, 1, 1) - self.label_146 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_146.setObjectName("label_146") - self.gridLayout_11.addWidget(self.label_146, 1, 0, 1, 1) - self.scanning_estimated_remaining_time_label = QtWidgets.QLabel(parent=self.scan_progress_page) - self.scanning_estimated_remaining_time_label.setObjectName("scanning_estimated_remaining_time_label") - self.gridLayout_11.addWidget(self.scanning_estimated_remaining_time_label, 9, 0, 1, 1) - self.label_101 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_101.setObjectName("label_101") - self.gridLayout_11.addWidget(self.label_101, 6, 0, 1, 1) - self.label_100 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_100.setObjectName("label_100") - self.gridLayout_11.addWidget(self.label_100, 3, 0, 1, 1) - self.label_144 = QtWidgets.QLabel(parent=self.scan_progress_page) - self.label_144.setObjectName("label_144") - self.gridLayout_11.addWidget(self.label_144, 8, 0, 1, 1) - self.scanning_overall_progbar = QtWidgets.QProgressBar(parent=self.scan_progress_page) - self.scanning_overall_progbar.setProperty("value", 24) - self.scanning_overall_progbar.setObjectName("scanning_overall_progbar") - self.gridLayout_11.addWidget(self.scanning_overall_progbar, 5, 0, 1, 1) - self.scanning_dc_preview_widget = QtWidgets.QWidget(parent=self.scan_progress_page) - self.scanning_dc_preview_widget.setMinimumSize(QtCore.QSize(640, 640)) - self.scanning_dc_preview_widget.setMaximumSize(QtCore.QSize(640, 640)) - self.scanning_dc_preview_widget.setStyleSheet("QWidget {\n" -" border: 1px solid #FFF;\n" -"}") - self.scanning_dc_preview_widget.setObjectName("scanning_dc_preview_widget") - self.gridLayout_11.addWidget(self.scanning_dc_preview_widget, 2, 0, 1, 1, QtCore.Qt.AlignmentFlag.AlignHCenter) - self.verticalLayout_19.addLayout(self.gridLayout_11) - self.abort_scan_button = QtWidgets.QPushButton(parent=self.scan_progress_page) - font = QtGui.QFont() - font.setFamily("Space Grotesk") - font.setPointSize(14) - self.abort_scan_button.setFont(font) - self.abort_scan_button.setObjectName("abort_scan_button") - self.verticalLayout_19.addWidget(self.abort_scan_button) - self.stackedWidget.addWidget(self.scan_progress_page) - self.verticalLayout_2.addWidget(self.stackedWidget) - MainWindow.setCentralWidget(self.centralwidget) - self.statusbar = QtWidgets.QStatusBar(parent=MainWindow) - self.statusbar.setObjectName("statusbar") - MainWindow.setStatusBar(self.statusbar) - - self.retranslateUi(MainWindow) - self.stackedWidget.setCurrentIndex(1) - self.configuration_tabwidget.setCurrentIndex(2) - QtCore.QMetaObject.connectSlotsByName(MainWindow) - - def retranslateUi(self, MainWindow): - _translate = QtCore.QCoreApplication.translate - MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) - self.label.setText(_translate("MainWindow", "Scanengine")) - self.label_2.setText(_translate("MainWindow", "What do you want to do?")) - self.start_new_scan_btn.setText(_translate("MainWindow", "Start a New Scan")) - self.resume_scan_btn.setText(_translate("MainWindow", "Resume an Interrupted Scan")) - self.edit_options_btn.setText(_translate("MainWindow", "Edit Options")) - self.label_3.setText(_translate("MainWindow", "Scanengine Configuration")) - self.label_65.setText(_translate("MainWindow", "Scan Kinematics:")) - self.label_15.setText(_translate("MainWindow", "Y\n" -"Coordinate:")) - self.label_14.setText(_translate("MainWindow", "X\n" -"Coordinate:")) - self.label_11.setText(_translate("MainWindow", "Trigger Behavior:")) - self.label_6.setText(_translate("MainWindow", "Scan Acceleration:")) - self.label_9.setText(_translate("MainWindow", "0 x 0 micron")) - self.label_4.setText(_translate("MainWindow", "Scan Velocity:")) - self.label_10.setText(_translate("MainWindow", "This is affected by settings in the\n" -"Generation/IR and PulseDecimator panels as well.")) - self.label_13.setText(_translate("MainWindow", "Optical Axis Correction")) - self.label_12.setText(_translate("MainWindow", "Serial Number:")) - self.label_7.setText(_translate("MainWindow", "mm/s2")) - self.stage_test_connection_btn.setText(_translate("MainWindow", "Test Connection")) - self.label_8.setText(_translate("MainWindow", "Effective Pixel\n" -"Size:")) - self.label_5.setText(_translate("MainWindow", "mm/s")) - self.label_64.setText(_translate("MainWindow", "Triggering Settings")) - self.configuration_tabwidget.setTabText(self.configuration_tabwidget.indexOf(self.kinematics_tab), _translate("MainWindow", "Kinematics")) - self.label_17.setText(_translate("MainWindow", "Interlock Status:")) - self.detection_diode_label.setText(_translate("MainWindow", "Unknown")) - self.label_19.setText(_translate("MainWindow", "Diode Status:")) - self.detection_serial_num_label.setText(_translate("MainWindow", "123456789")) - self.detection_test_btn.setText(_translate("MainWindow", "Test Connection && Query Laser")) - self.label_18.setText(_translate("MainWindow", "Keyswitch Status:")) - self.label_21.setText(_translate("MainWindow", "Default\n" -"Power Level:")) - self.label_16.setText(_translate("MainWindow", "Serial Number:")) - self.label_22.setText(_translate("MainWindow", "milliwatts")) - self.detection_interlock_label.setText(_translate("MainWindow", "Unknown")) - self.detection_keyswitch_label.setText(_translate("MainWindow", "Unknown")) - self.detection_temperature_label.setText(_translate("MainWindow", "0.00C")) - self.label_20.setText(_translate("MainWindow", "Temperature:")) - self.configuration_tabwidget.setTabText(self.configuration_tabwidget.indexOf(self.detection_tab), _translate("MainWindow", "Detection / VIS")) - self.label_30.setText(_translate("MainWindow", "Interlock Status:")) - self.label_26.setText(_translate("MainWindow", "Diode Pump\n" -"Current:")) - self.label_23.setText(_translate("MainWindow", "Serial Port:")) - self.refresh_serial_ports_btn.setText(_translate("MainWindow", "Refresh Ports")) - self.label_27.setText(_translate("MainWindow", "milliamps")) - self.label_33.setText(_translate("MainWindow", "Diode Status:")) - self.label_31.setText(_translate("MainWindow", "Enable Switch Status:")) - self.label_32.setText(_translate("MainWindow", "Warmup Lockout:")) - self.generation_connect_button.setText(_translate("MainWindow", "Connect")) - self.label_35.setText(_translate("MainWindow", "Diode Temperature:")) - self.label_24.setText(_translate("MainWindow", "Pulse Frequency:")) - self.label_34.setText(_translate("MainWindow", "Head Temperature:")) - self.label_28.setText(_translate("MainWindow", "Status Information:")) - self.label_25.setText(_translate("MainWindow", "Hertz")) - self.label_29.setText(_translate("MainWindow", "Head Hours:")) - self.generation_head_hours_lbl.setText(_translate("MainWindow", "Unknown")) - self.generation_interlock_status_lbl.setText(_translate("MainWindow", "Unknown")) - self.generation_enable_status_lbl.setText(_translate("MainWindow", "Unknown")) - self.generation_warmup_lbl.setText(_translate("MainWindow", "Unknown")) - self.generation_emission_lbl.setText(_translate("MainWindow", "Unknown")) - self.generation_headtemp_lbl.setText(_translate("MainWindow", "Unknown")) - self.generation_diodetemp_lbl.setText(_translate("MainWindow", "Unknown")) - self.label_36.setText(_translate("MainWindow", "Pixel Size:")) - self.label_37.setText(_translate("MainWindow", "0 x 0 microns")) - self.label_38.setText(_translate("MainWindow", "This is also affected by settings in the Kinematics\n" -"and PulseDecimator tabs.")) - self.configuration_tabwidget.setTabText(self.configuration_tabwidget.indexOf(self.generation_tab), _translate("MainWindow", "Generation / IR")) - self.label_49.setText(_translate("MainWindow", "This is also affected by settings on the\n" -"Kinematics and Generation tabs.")) - self.label_42.setText(_translate("MainWindow", "Current Divider Value")) - self.fpga_divider_value.setText(_translate("MainWindow", "Unknown")) - self.fpga_fw_version.setText(_translate("MainWindow", "Unknown")) - self.fpga_refresh_ports_btn.setText(_translate("MainWindow", "Refresh Ports")) - self.fpga_supports_rowpack.setText(_translate("MainWindow", "Unknown")) - self.label_40.setText(_translate("MainWindow", "Divider Value:")) - self.label_41.setText(_translate("MainWindow", "Firmware Version")) - self.label_39.setText(_translate("MainWindow", "T3RSL\n" -"Communicaton Port:")) - self.checkBox.setText(_translate("MainWindow", "Enable RowPack feature?")) - self.label_43.setText(_translate("MainWindow", "Supports RowPack?")) - self.label_47.setText(_translate("MainWindow", "Effective Pixel Size:")) - self.fpga_connect_button.setText(_translate("MainWindow", "Connect && Query")) - self.fpga_pixel_size.setText(_translate("MainWindow", "0 x 0 microns")) - self.configuration_tabwidget.setTabText(self.configuration_tabwidget.indexOf(self.pulsedecimator_tab), _translate("MainWindow", "PulseDecimator")) - self.label_60.setText(_translate("MainWindow", "T Axis Microstepping Mode:")) - self.t3r_connect_btn.setText(_translate("MainWindow", "Connect && Query")) - self.label_44.setText(_translate("MainWindow", "T3RSL Communication Port:")) - self.label_59.setText(_translate("MainWindow", "GR Axis Drive Current")) - self.t3r_refresh_ports_btn.setText(_translate("MainWindow", "Refresh Ports")) - self.label_61.setText(_translate("MainWindow", "GR Axis Microstepping Mode:")) - self.label_58.setText(_translate("MainWindow", "T Axis Drive Current")) - self.label_62.setText(_translate("MainWindow", "milliamperes")) - self.label_63.setText(_translate("MainWindow", "milliamperes")) - self.configuration_tabwidget.setTabText(self.configuration_tabwidget.indexOf(self.t3rsl_tab), _translate("MainWindow", "T3R-SL")) - self.label_45.setText(_translate("MainWindow", "Oscilloscope IP Address:")) - self.scope_connect_btn.setText(_translate("MainWindow", "Test Connection")) - self.configuration_tabwidget.setTabText(self.configuration_tabwidget.indexOf(self.oscilloscope_tab), _translate("MainWindow", "Oscilloscope")) - self.options_save_settings_btn.setText(_translate("MainWindow", "Save Settings")) - self.options_cancel_btn.setText(_translate("MainWindow", "Cancel")) - self.label_46.setText(_translate("MainWindow", "Start a New Scan")) - self.label_69.setText(_translate("MainWindow", "VIS Laser:")) - self.label_72.setText(_translate("MainWindow", "X-Position")) - self.newscan_current_x_position_label.setText(_translate("MainWindow", "000.00")) - self.label_74.setText(_translate("MainWindow", "Y-Position")) - self.newscan_current_y_position_label.setText(_translate("MainWindow", "000.00")) - self.newscan_set_current_as_start_btn.setText(_translate("MainWindow", "Set as Start")) - self.newscan_get_delta_from_current_btn.setText(_translate("MainWindow", "Calculate Delta")) - self.label_48.setText(_translate("MainWindow", "Scan Friendly Name:")) - self.newscan_50_micron_radio.setText(_translate("MainWindow", "50 microns")) - self.label_56.setText(_translate("MainWindow", "Save to Folder:")) - self.label_66.setText(_translate("MainWindow", "Sample Check:")) - self.label_51.setText(_translate("MainWindow", "X Delta:")) - self.label_57.setText(_translate("MainWindow", "Row Size:")) - self.label_54.setText(_translate("MainWindow", "Y Delta:")) - self.label_53.setText(_translate("MainWindow", "Start Y\n" -"Coord:")) - self.newscan_toggle_vis_laser_btn.setText(_translate("MainWindow", "Toggle Emission")) - self.label_70.setText(_translate("MainWindow", "Test Power Level:")) - self.newscan_100_micron_radio.setText(_translate("MainWindow", "100 microns")) - self.label_67.setText(_translate("MainWindow", "Stage Jog Controls:")) - self.label_76.setText(_translate("MainWindow", "Camera Controls:")) - self.label_50.setText(_translate("MainWindow", "Start X\n" -"Coord:")) - self.label_52.setText(_translate("MainWindow", "Number of\n" -"Angles:")) - self.newscan_browse_folders_btn.setText(_translate("MainWindow", "Browse Folders")) - self.label_77.setText(_translate("MainWindow", "Exposure:")) - self.label_78.setText(_translate("MainWindow", "Gain:")) - self.newscan_jog_x_pos_btn.setText(_translate("MainWindow", "X - ")) - self.newscan_jog_y_pos_btn.setText(_translate("MainWindow", "Y +")) - self.label_68.setText(_translate("MainWindow", "Jog Speed [mm/s]:")) - self.newscan_jog_y_neg_btn.setText(_translate("MainWindow", "Y -")) - self.newscan_jog_x_neg_btn.setText(_translate("MainWindow", "X +")) - self.label_71.setText(_translate("MainWindow", "Camera Preview:")) - self.label_55.setText(_translate("MainWindow", "File Prefix:")) - self.newscan_250_micron_radio.setText(_translate("MainWindow", "250 microns")) - self.newscan_continue_to_next_btn.setText(_translate("MainWindow", "Continue")) - self.label_79.setText(_translate("MainWindow", "Metadata Summary:")) - self.label_83.setText(_translate("MainWindow", "Y-Start:")) - self.label_85.setText(_translate("MainWindow", "Pixel Size:")) - self.label_75.setText(_translate("MainWindow", "Select Existing Scan Directory:")) - self.continuescan_x_delta_label.setText(_translate("MainWindow", "Unknown")) - self.continuescan_x_start_label.setText(_translate("MainWindow", "Unknown")) - self.continuescan_num_angles_label.setText(_translate("MainWindow", "Unknown")) - self.continuescan_pixel_size_label.setText(_translate("MainWindow", "0 x 0 microns")) - self.label_84.setText(_translate("MainWindow", "Number of Angles:")) - self.label_80.setText(_translate("MainWindow", "Friendly Name:")) - self.pushButton.setText(_translate("MainWindow", "Browse Directory")) - self.label_73.setText(_translate("MainWindow", "Resume an Existing Scan:")) - self.label_90.setText(_translate("MainWindow", "Y-Delta:")) - self.continuescan_y_delta_label.setText(_translate("MainWindow", "Unknown")) - self.continuescan_last_complete_scan.setText(_translate("MainWindow", "0")) - self.continuescan_y_start_label.setText(_translate("MainWindow", "Unknown")) - self.label_86.setText(_translate("MainWindow", "Last Successfully\n" -"Scanned Angle:")) - self.continuescan_friendlyname.setText(_translate("MainWindow", "Unknown")) - self.label_87.setText(_translate("MainWindow", "X-Delta:")) - self.label_82.setText(_translate("MainWindow", "X-Start:")) - self.pushButton_2.setText(_translate("MainWindow", "Read Directory and Load Metadata")) - self.continuescan_resume_scans.setText(_translate("MainWindow", "Resume Scanning")) - self.label_102.setText(_translate("MainWindow", "Kinematics Info:")) - self.label_105.setText(_translate("MainWindow", "XPos:")) - self.scanning_x_position_label.setText(_translate("MainWindow", "000.00")) - self.label_109.setText(_translate("MainWindow", "YPos:")) - self.scanning_y_position_label.setText(_translate("MainWindow", "000.00")) - self.label_103.setText(_translate("MainWindow", "State:")) - self.scanning_stage_state_label.setText(_translate("MainWindow", "IDLE")) - self.label_111.setText(_translate("MainWindow", "Genesis Info:")) - self.label_116.setText(_translate("MainWindow", "State:")) - self.scanning_vis_status_label.setText(_translate("MainWindow", "EMISSION")) - self.label_118.setText(_translate("MainWindow", "Power:")) - self.scanning_vis_power_label.setText(_translate("MainWindow", "000mW")) - self.label_120.setText(_translate("MainWindow", "Temp:")) - self.scanning_vis_temperature_label.setText(_translate("MainWindow", "0C")) - self.label_112.setText(_translate("MainWindow", "Error:")) - self.scanning_vis_error_label.setText(_translate("MainWindow", "NONE")) - self.label_122.setText(_translate("MainWindow", "Helios Info:")) - self.label_125.setText(_translate("MainWindow", "State:")) - self.scanning_ir_status_label.setText(_translate("MainWindow", "EMISSION")) - self.label_131.setText(_translate("MainWindow", "Frequency:")) - self.scanning_ir_freq_label.setText(_translate("MainWindow", "20,000Hz")) - self.label_133.setText(_translate("MainWindow", "Pump Current:")) - self.scanning_ir_current_label.setText(_translate("MainWindow", "1500mA")) - self.label_135.setText(_translate("MainWindow", "Head Hours:")) - self.scanning_ir_headhours_label.setText(_translate("MainWindow", "0.0")) - self.label_123.setText(_translate("MainWindow", "Head Temp:")) - self.scanning_ir_temp_label.setText(_translate("MainWindow", "0C")) - self.label_142.setText(_translate("MainWindow", "Error:")) - self.scanning_ir_error_label.setText(_translate("MainWindow", "NONE")) - self.label_137.setText(_translate("MainWindow", "PulseDecimator:")) - self.label_140.setText(_translate("MainWindow", "Status:")) - self.scanning_pulsedivider_status_label.setText(_translate("MainWindow", "ACTIVE")) - self.label_138.setText(_translate("MainWindow", "Divider:")) - self.scanning_pulsedivider_divider_label.setText(_translate("MainWindow", "/10")) - self.label_99.setText(_translate("MainWindow", "Scanning in Progress:")) - self.label_146.setText(_translate("MainWindow", "Scan Preview (DC Only):")) - self.scanning_estimated_remaining_time_label.setText(_translate("MainWindow", "0h 0min")) - self.label_101.setText(_translate("MainWindow", "Scan Progress:")) - self.label_100.setText(_translate("MainWindow", "Overall Progress:")) - self.label_144.setText(_translate("MainWindow", "Estimated Remaining Time:")) - self.abort_scan_button.setText(_translate("MainWindow", "ABORT SCAN"))