From 093d1b749a39646b904f3407dc24ee0168cff942 Mon Sep 17 00:00:00 2001 From: Ian OHara Date: Fri, 27 Dec 2024 18:10:42 -0800 Subject: [PATCH] more integration and fixing --- software/control/core/core.py | 22 -- software/control/gui.py | 214 ------------------ software/control/gui_hcs.py | 12 - software/control/gui_malaria.py | 383 -------------------------------- software/control/widgets.py | 31 ++- 5 files changed, 22 insertions(+), 640 deletions(-) delete mode 100644 software/control/gui.py delete mode 100644 software/control/gui_malaria.py diff --git a/software/control/core/core.py b/software/control/core/core.py index 7768ba14..feae4401 100644 --- a/software/control/core/core.py +++ b/software/control/core/core.py @@ -789,18 +789,6 @@ def update_pos(self, microcontroller): print('joystick button pressed') microcontroller.signal_joystick_button_pressed_event = False - # TODO(imo): This should be don in the CephlaStage init since we have the encoder info there already - def configure_encoder(self, axis, transitions_per_revolution,flip_direction): - self.microcontroller.configure_stage_pid(axis, transitions_per_revolution=int(transitions_per_revolution), flip_direction=flip_direction) - - # TODO(imo): This should be done in the CephlaStage init - def set_pid_control_enable(self, axis, enable_flag): - self.pid_enable_flag[axis] = enable_flag - if self.pid_enable_flag[axis] is True: - self.microcontroller.turn_on_stage_pid(axis) - else: - self.microcontroller.turn_off_stage_pid(axis) - # TODO(imo): This is only called from a closeEvent. This isn't guaranteed to run - do we need to do this? def turnoff_axis_pid_control(self): for i in range(len(self.pid_enable_flag)): @@ -814,16 +802,6 @@ def keep_scan_begin_position(self, x, y): self.scan_begin_position_x = x self.scan_begin_position_y = y - # TODO(imo): This is only called in an init, so it should be done in CephlaStage init via CephlaStage specific axis config - def set_axis_PID_arguments(self, axis, pid_p, pid_i, pid_d): - self.microcontroller.set_pid_arguments(axis, pid_p, pid_i, pid_d) - - # TODO(imo): Can we just have this be a part of move_z and move_z_to? Or does it need to be a separate axis concept? - def set_piezo_um(self, z_piezo_um): - dac = int(65535 * (z_piezo_um / OBJECTIVE_PIEZO_RANGE_UM)) - dac = 65535 - dac if OBJECTIVE_PIEZO_FLIP_DIR else dac - self.microcontroller.analog_write_onboard_DAC(7, dac) - class SlidePositionControlWorker(QObject): diff --git a/software/control/gui.py b/software/control/gui.py deleted file mode 100644 index fb1ecc98..00000000 --- a/software/control/gui.py +++ /dev/null @@ -1,214 +0,0 @@ -# set QT_API environment variable -import os -os.environ["QT_API"] = "pyqt5" -import qtpy - -# qt libraries -from qtpy.QtCore import * -from qtpy.QtWidgets import * -from qtpy.QtGui import * - -# app specific libraries -import control.widgets as widgets -import control.camera as camera -import control.core as core -import control.microcontroller as microcontroller -from control._def import * -import squid.logging - -import pyqtgraph.dockarea as dock -SINGLE_WINDOW = True # set to False if use separate windows for display and control - -class OctopiGUI(QMainWindow): - - # variables - fps_software_trigger = 100 - - def __init__(self, is_simulation = False, *args, **kwargs): - super().__init__(*args, **kwargs) - - self.log = squid.logging.get_logger(self.__class__.__name__) - - # load window - if ENABLE_TRACKING: - self.imageDisplayWindow = core.ImageDisplayWindow(draw_crosshairs=True,autoLevels=AUTOLEVEL_DEFAULT_SETTING) - self.imageDisplayWindow.show_ROI_selector() - else: - self.imageDisplayWindow = core.ImageDisplayWindow(draw_crosshairs=True,autoLevels=AUTOLEVEL_DEFAULT_SETTING) - self.imageArrayDisplayWindow = core.ImageArrayDisplayWindow() - # self.imageDisplayWindow.show() - # self.imageArrayDisplayWindow.show() - - # image display windows - self.imageDisplayTabs = QTabWidget() - self.imageDisplayTabs.addTab(self.imageDisplayWindow.widget, "Live View") - self.imageDisplayTabs.addTab(self.imageArrayDisplayWindow.widget, "Multichannel Acquisition") - - # load objects - if is_simulation: - self.camera = camera.Camera_Simulation(rotate_image_angle=ROTATE_IMAGE_ANGLE,flip_image=FLIP_IMAGE) - self.microcontroller = microcontroller.Microcontroller(existing_serial=microcontroller.SimSerial()) - else: - try: - self.camera = camera.Camera(rotate_image_angle=ROTATE_IMAGE_ANGLE,flip_image=FLIP_IMAGE) - self.camera.open() - except: - self.camera = camera.Camera_Simulation(rotate_image_angle=ROTATE_IMAGE_ANGLE,flip_image=FLIP_IMAGE) - self.camera.open() - self.log.error("camera not detected, using simulated camera") - try: - self.microcontroller = microcontroller.Microcontroller(version=CONTROLLER_VERSION) - except: - self.log.error("Microcontroller not detected, using simulated microcontroller") - self.microcontroller = microcontroller.Microcontroller_Simulation() - - # reset the MCU - self.microcontroller.reset() - - # configure the actuators - self.microcontroller.configure_actuators() - - self.objectiveStore = core.ObjectiveStore() - self.configurationManager = core.ConfigurationManager('./channel_configurations.xml') - self.objectiveStore = core.ObjectiveStore(parent=self) # todo: add widget to select/save objective save - self.streamHandler = core.StreamHandler(display_resolution_scaling=DEFAULT_DISPLAY_CROP/100) - self.liveController = core.LiveController(self.camera,self.microcontroller,self.configurationManager) - self.navigationController = core.NavigationController(self.microcontroller,self.objectiveStore) - self.autofocusController = core.AutoFocusController(self.camera,self.navigationController,self.liveController) - self.scanCoordinates = core.ScanCoordinates() - self.multipointController = core.MultiPointController(self.camera,self.navigationController,self.liveController,self.autofocusController,self.configurationManager,scanCoordinates=self.scanCoordinates,parent=self) - if ENABLE_TRACKING: - self.trackingController = core.TrackingController(self.camera,self.microcontroller,self.navigationController,self.configurationManager,self.liveController,self.autofocusController,self.imageDisplayWindow) - self.imageSaver = core.ImageSaver(image_format=Acquisition.IMAGE_FORMAT) - self.imageDisplay = core.ImageDisplay() - - # set up the camera - # self.camera.set_reverse_x(CAMERA_REVERSE_X) # these are not implemented for the cameras in use - # self.camera.set_reverse_y(CAMERA_REVERSE_Y) # these are not implemented for the cameras in use - self.camera.set_software_triggered_acquisition() #self.camera.set_continuous_acquisition() - self.camera.set_callback(self.streamHandler.on_new_frame) - self.camera.enable_callback() - if ENABLE_STROBE_OUTPUT: - self.camera.set_line3_to_exposure_active() - - # load widgets: - self.objectivesWidget=widgets.ObjectivesWidget(self.objectiveStore) - - self.cameraSettingWidget = widgets.CameraSettingsWidget(self.camera,include_gain_exposure_time=False) - self.liveControlWidget = widgets.LiveControlWidget(self.streamHandler,self.liveController,self.configurationManager,show_trigger_options=True,show_display_options=True,show_autolevel=SHOW_AUTOLEVEL_BTN,autolevel=AUTOLEVEL_DEFAULT_SETTING) - self.navigationWidget = widgets.NavigationWidget(self.navigationController) - self.dacControlWidget = widgets.DACControWidget(self.microcontroller) - self.autofocusWidget = widgets.AutoFocusWidget(self.autofocusController) - self.recordingControlWidget = widgets.RecordingWidget(self.streamHandler,self.imageSaver) - if ENABLE_TRACKING: - self.trackingControlWidget = widgets.TrackingControllerWidget(self.trackingController,self.configurationManager,show_configurations=TRACKING_SHOW_MICROSCOPE_CONFIGURATIONS) - self.multiPointWidget = widgets.MultiPointWidget(self.multipointController,self.configurationManager) - self.navigationViewer = core.NavigationViewer(self.objectiveStore, sample=str(6)+' well plate') - self.multiPointWidget2 = widgets.MultiPointWidget2(self.navigationController,self.navigationViewer,self.multipointController,self.configurationManager,scanCoordinates=None) - - self.recordTabWidget = QTabWidget() - if ENABLE_TRACKING: - self.recordTabWidget.addTab(self.trackingControlWidget, "Tracking") - self.recordTabWidget.addTab(self.multiPointWidget2, "Flexible Multipoint") - self.recordTabWidget.addTab(self.recordingControlWidget, "Simple Recording") - # self.recordTabWidget.addTab(self.multiPointWidget, "Multipoint Acquisition") - - # layout widgets - layout = QVBoxLayout() - layout.addWidget(self.cameraSettingWidget) - #self.objectivesWidget.setFixedHeight(100) - layout.addWidget(self.liveControlWidget) - layout.addWidget(self.navigationWidget) - if SHOW_DAC_CONTROL: - layout.addWidget(self.dacControlWidget) - layout.addWidget(self.autofocusWidget) - layout.addWidget(self.recordTabWidget) - layout.addWidget(self.objectivesWidget) - layout.addStretch() - - # transfer the layout to the central widget - self.centralWidget = QWidget() - self.centralWidget.setLayout(layout) - # self.centralWidget.setFixedSize(self.centralWidget.minimumSize()) - # self.centralWidget.setFixedWidth(self.centralWidget.minimumWidth()) - # self.centralWidget.setMaximumWidth(self.centralWidget.minimumWidth()) - self.centralWidget.setFixedWidth(self.centralWidget.minimumSizeHint().width()) - - if SINGLE_WINDOW: - dock_display = dock.Dock('Image Display', autoOrientation = False) - dock_display.showTitleBar() - dock_display.addWidget(self.imageDisplayTabs) - dock_display.setStretch(x=100,y=None) - dock_controlPanel = dock.Dock('Controls', autoOrientation = False) - # dock_controlPanel.showTitleBar() - dock_controlPanel.addWidget(self.centralWidget) - dock_controlPanel.setStretch(x=1,y=None) - dock_controlPanel.setFixedWidth(dock_controlPanel.minimumSizeHint().width()) - main_dockArea = dock.DockArea() - main_dockArea.addDock(dock_display) - main_dockArea.addDock(dock_controlPanel,'right') - self.setCentralWidget(main_dockArea) - desktopWidget = QDesktopWidget() - height_min = 0.9*desktopWidget.height() - width_min = 0.96*desktopWidget.width() - self.setMinimumSize(int(width_min),int(height_min)) - else: - self.setCentralWidget(self.centralWidget) - self.tabbedImageDisplayWindow = QMainWindow() - self.tabbedImageDisplayWindow.setCentralWidget(self.imageDisplayTabs) - self.tabbedImageDisplayWindow.setWindowFlags(self.windowFlags() | Qt.CustomizeWindowHint) - self.tabbedImageDisplayWindow.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint) - desktopWidget = QDesktopWidget() - width = 0.96*desktopWidget.height() - height = width - self.tabbedImageDisplayWindow.setFixedSize(width,height) - self.tabbedImageDisplayWindow.show() - - # make connections - self.streamHandler.signal_new_frame_received.connect(self.liveController.on_new_frame) - self.streamHandler.image_to_display.connect(self.imageDisplay.enqueue) - self.streamHandler.packet_image_to_write.connect(self.imageSaver.enqueue) - # self.streamHandler.packet_image_for_tracking.connect(self.trackingController.on_new_frame) - self.imageDisplay.image_to_display.connect(self.imageDisplayWindow.display_image) # may connect streamHandler directly to imageDisplayWindow - self.navigationController.xPos.connect(self.navigationWidget.label_Xpos.setNum) - self.navigationController.yPos.connect(self.navigationWidget.label_Ypos.setNum) - self.navigationController.zPos.connect(self.navigationWidget.label_Zpos.setNum) - if ENABLE_TRACKING: - self.navigationController.signal_joystick_button_pressed.connect(self.trackingControlWidget.slot_joystick_button_pressed) - else: - self.navigationController.signal_joystick_button_pressed.connect(self.autofocusController.autofocus) - self.autofocusController.image_to_display.connect(self.imageDisplayWindow.display_image) - self.multipointController.image_to_display.connect(self.imageDisplayWindow.display_image) - self.multipointController.signal_current_configuration.connect(self.liveControlWidget.set_microscope_mode) - self.multipointController.image_to_display_multi.connect(self.imageArrayDisplayWindow.display_image) - self.liveControlWidget.signal_newExposureTime.connect(self.cameraSettingWidget.set_exposure_time) - self.liveControlWidget.signal_newAnalogGain.connect(self.cameraSettingWidget.set_analog_gain) - self.liveControlWidget.update_camera_settings() - self.liveControlWidget.signal_autoLevelSetting.connect(self.imageDisplayWindow.set_autolevel) - - self.multiPointWidget2.signal_acquisition_started.connect(self.navigationWidget.toggle_navigation_controls) - - if USE_NAPARI_FOR_MULTIPOINT: - self.napariMultiChannelWidget = widgets.NapariMultiChannelWidget(self.objectiveStore) - self.imageDisplayTabs.addTab(self.napariMultiChannelWidget, "Multichannel Acquisition") - self.multiPointWidget.signal_acquisition_channels.connect(self.napariMultiChannelWidget.initChannels) - self.multiPointWidget.signal_acquisition_shape.connect(self.napariMultiChannelWidget.initLayersShape) - if ENABLE_FLEXIBLE_MULTIPOINT: - self.multiPointWidget2.signal_acquisition_channels.connect(self.napariMultiChannelWidget.initChannels) - self.multiPointWidget2.signal_acquisition_shape.connect(self.napariMultiChannelWidget.initLayersShape) - - self.multipointController.napari_layers_init.connect(self.napariMultiChannelWidget.initLayers) - self.multipointController.napari_layers_update.connect(self.napariMultiChannelWidget.updateLayers) - - - def closeEvent(self, event): - event.accept() - self.liveController.stop_live() - self.camera.close() - self.imageSaver.close() - self.imageDisplay.close() - if not SINGLE_WINDOW: - self.imageDisplayWindow.close() - self.imageArrayDisplayWindow.close() - self.tabbedImageDisplayWindow.close() - self.microcontroller.close() diff --git a/software/control/gui_hcs.py b/software/control/gui_hcs.py index 1ea6d36b..ecb91bd7 100644 --- a/software/control/gui_hcs.py +++ b/software/control/gui_hcs.py @@ -587,16 +587,6 @@ def setupMultiWindowLayout(self): def makeConnections(self): self.streamHandler.signal_new_frame_received.connect(self.liveController.on_new_frame) self.streamHandler.packet_image_to_write.connect(self.imageSaver.enqueue) - # self.streamHandler.packet_image_for_tracking.connect(self.trackingController.on_new_frame) - # TODO(imo): Fix ui position updates since removal of navigation controller - # self.navigationController.xPos.connect(lambda x: self.navigationWidget.label_Xpos.setText("{:.2f}".format(x) + " mm")) - # self.navigationController.yPos.connect(lambda x: self.navigationWidget.label_Ypos.setText("{:.2f}".format(x) + " mm")) - # self.navigationController.zPos.connect(lambda x: self.navigationWidget.label_Zpos.setText("{:.2f}".format(x) + " μm")) - - # if SHOW_NAVIGATION_BAR: - # self.navigationController.xPos.connect(self.navigationBarWidget.update_x_position) - # self.navigationController.yPos.connect(self.navigationBarWidget.update_y_position) - # self.navigationController.zPos.connect(self.navigationBarWidget.update_z_position) # TODO(imo): Fix joystick after removal of navigation controller # if ENABLE_TRACKING: @@ -609,7 +599,6 @@ def makeConnections(self): if ENABLE_FLEXIBLE_MULTIPOINT: self.flexibleMultiPointWidget.signal_acquisition_started.connect(self.toggleAcquisitionStart) - # self.flexibleMultiPointWidget.signal_z_stacking.connect(self.multipointController.set_z_stacking_config) if ENABLE_STITCHER: self.flexibleMultiPointWidget.signal_stitcher_widget.connect(self.toggleStitcherWidget) self.flexibleMultiPointWidget.signal_acquisition_channels.connect(self.stitcherWidget.updateRegistrationChannels) @@ -617,7 +606,6 @@ def makeConnections(self): if ENABLE_WELLPLATE_MULTIPOINT: self.wellplateMultiPointWidget.signal_acquisition_started.connect(self.toggleAcquisitionStart) - # self.wellplateMultiPointWidget.signal_z_stacking.connect(self.multipointController.set_z_stacking_config) if ENABLE_STITCHER: self.wellplateMultiPointWidget.signal_stitcher_widget.connect(self.toggleStitcherWidget) self.wellplateMultiPointWidget.signal_acquisition_channels.connect(self.stitcherWidget.updateRegistrationChannels) diff --git a/software/control/gui_malaria.py b/software/control/gui_malaria.py deleted file mode 100644 index 440503e3..00000000 --- a/software/control/gui_malaria.py +++ /dev/null @@ -1,383 +0,0 @@ -# set QT_API environment variable -import os -os.environ["QT_API"] = "pyqt5" - -# qt libraries -from qtpy.QtCore import * -from qtpy.QtWidgets import * - -# app specific libraries -import control.widgets as widgets -import control.camera as camera -import control.core as core -import control.microcontroller as microcontroller -from control._def import * - -import pyqtgraph.dockarea as dock -import time - -SINGLE_WINDOW = True # set to False if use separate windows for display and control - -class MalariaGUI(QMainWindow): - - # variables - fps_software_trigger = 100 - - def __init__(self, is_simulation = False, *args, **kwargs): - super().__init__(*args, **kwargs) - - self.log = squid.logging.get_logger(self.__class__.__name__) - - # load objects - if is_simulation: - self.camera = camera.Camera_Simulation(rotate_image_angle=ROTATE_IMAGE_ANGLE,flip_image=FLIP_IMAGE) - self.microcontroller = microcontroller.Microcontroller(existing_serial=microcontroller.SimSerial()) - else: - try: - self.camera = camera.Camera(rotate_image_angle=ROTATE_IMAGE_ANGLE,flip_image=FLIP_IMAGE) - self.camera.open() - except: - self.camera = camera.Camera_Simulation(rotate_image_angle=ROTATE_IMAGE_ANGLE,flip_image=FLIP_IMAGE) - self.camera.open() - self.log.error("camera not detected, using simulated camera") - try: - self.microcontroller = microcontroller.Microcontroller(version=CONTROLLER_VERSION) - except: - self.log.error("Microcontroller not detected, using simulated microcontroller") - self.microcontroller = microcontroller.Microcontroller(existing_serial=microcontroller.SimSerial()) - - # reset the MCU - self.microcontroller.reset() - - # reinitialize motor drivers and DAC (in particular for V2.1 driver board where PG is not functional) - self.microcontroller.initialize_drivers() - - # configure the actuators - self.microcontroller.configure_actuators() - self.objectiveStore = core.ObjectiveStore(parent=self) - self.scanCoordinates = core.ScanCoordinates() - self.configurationManager = core.ConfigurationManager() - self.streamHandler = core.StreamHandler(display_resolution_scaling=DEFAULT_DISPLAY_CROP/100) - self.liveController = core.LiveController(self.camera,self.microcontroller,self.configurationManager) - self.navigationController = core.NavigationController(self.microcontroller, self.objectiveStore, parent=self) - self.slidePositionController = core.SlidePositionController(self.navigationController,self.liveController) - self.autofocusController = core.AutoFocusController(self.camera,self.navigationController,self.liveController) - self.multipointController = core.MultiPointController(self.camera,self.navigationController,self.liveController,self.autofocusController,self.configurationManager) - if ENABLE_TRACKING: - self.trackingController = core.TrackingController(self.camera,self.microcontroller,self.navigationController,self.configurationManager,self.liveController,self.autofocusController,self.imageDisplayWindow) - self.imageSaver = core.ImageSaver() - self.imageDisplay = core.ImageDisplay() - self.navigationViewer = core.NavigationViewer(self.objectiveStore) - - # retract the objective - self.navigationController.home_z() - # wait for the operation to finish - t0 = time.time() - while self.microcontroller.is_busy(): - time.sleep(0.005) - if time.time() - t0 > 10: - self.log.error('z homing timeout, the program will exit') - sys.exit(1) - self.log.info('objective retracted') - - # set encoder arguments - # set axis pid control enable - # only ENABLE_PID_X and HAS_ENCODER_X are both enable, can be enable to PID - if HAS_ENCODER_X == True: - self.navigationController.set_axis_PID_arguments(0, PID_P_X, PID_I_X, PID_D_X) - self.navigationController.configure_encoder(0, (SCREW_PITCH_X_MM * 1000) / ENCODER_RESOLUTION_UM_X, ENCODER_FLIP_DIR_X) - self.navigationController.set_pid_control_enable(0, ENABLE_PID_X) - if HAS_ENCODER_Y == True: - self.navigationController.set_axis_PID_arguments(1, PID_P_Y, PID_I_Y, PID_D_Y) - self.navigationController.configure_encoder(1, (SCREW_PITCH_Y_MM * 1000) / ENCODER_RESOLUTION_UM_Y, ENCODER_FLIP_DIR_Y) - self.navigationController.set_pid_control_enable(1, ENABLE_PID_Y) - if HAS_ENCODER_Z == True: - self.navigationController.set_axis_PID_arguments(2, PID_P_Z, PID_I_Z, PID_D_Z) - self.navigationController.configure_encoder(2, (SCREW_PITCH_Z_MM * 1000) / ENCODER_RESOLUTION_UM_Z, ENCODER_FLIP_DIR_Z) - self.navigationController.set_pid_control_enable(2, ENABLE_PID_Z) - - time.sleep(0.5) - - # homing, set zero and set software limit - self.navigationController.set_x_limit_pos_mm(100) - self.navigationController.set_x_limit_neg_mm(-100) - self.navigationController.set_y_limit_pos_mm(100) - self.navigationController.set_y_limit_neg_mm(-100) - self.log.info("start homing") - self.slidePositionController.move_to_slide_scanning_position() - while self.slidePositionController.slide_scanning_position_reached == False: - time.sleep(0.005) - self.log.info("homing finished") - self.navigationController.set_x_limit_pos_mm(SOFTWARE_POS_LIMIT.X_POSITIVE) - self.navigationController.set_x_limit_neg_mm(SOFTWARE_POS_LIMIT.X_NEGATIVE) - self.navigationController.set_y_limit_pos_mm(SOFTWARE_POS_LIMIT.Y_POSITIVE) - self.navigationController.set_y_limit_neg_mm(SOFTWARE_POS_LIMIT.Y_NEGATIVE) - - # set piezo arguments - if ENABLE_OBJECTIVE_PIEZO is True: - if OBJECTIVE_PIEZO_CONTROL_VOLTAGE_RANGE == 5: - OUTPUT_GAINS.CHANNEL7_GAIN = True - else: - OUTPUT_GAINS.CHANNEL7_GAIN = False - - # set output's gains - div = 1 if OUTPUT_GAINS.REFDIV is True else 0 - gains = OUTPUT_GAINS.CHANNEL0_GAIN << 0 - gains += OUTPUT_GAINS.CHANNEL1_GAIN << 1 - gains += OUTPUT_GAINS.CHANNEL2_GAIN << 2 - gains += OUTPUT_GAINS.CHANNEL3_GAIN << 3 - gains += OUTPUT_GAINS.CHANNEL4_GAIN << 4 - gains += OUTPUT_GAINS.CHANNEL5_GAIN << 5 - gains += OUTPUT_GAINS.CHANNEL6_GAIN << 6 - gains += OUTPUT_GAINS.CHANNEL7_GAIN << 7 - self.microcontroller.configure_dac80508_refdiv_and_gain(div, gains) - - # set illumination intensity factor - global ILLUMINATION_INTENSITY_FACTOR - self.microcontroller.set_dac80508_scaling_factor_for_illumination(ILLUMINATION_INTENSITY_FACTOR) - - # set software limit - self.navigationController.set_x_limit_pos_mm(SOFTWARE_POS_LIMIT.X_POSITIVE) - self.navigationController.set_x_limit_neg_mm(SOFTWARE_POS_LIMIT.X_NEGATIVE) - self.navigationController.set_y_limit_pos_mm(SOFTWARE_POS_LIMIT.Y_POSITIVE) - self.navigationController.set_y_limit_neg_mm(SOFTWARE_POS_LIMIT.Y_NEGATIVE) - self.navigationController.set_z_limit_pos_mm(SOFTWARE_POS_LIMIT.Z_POSITIVE) - - # open the camera - # camera start streaming - # self.camera.set_reverse_x(CAMERA_REVERSE_X) # these are not implemented for the cameras in use - # self.camera.set_reverse_y(CAMERA_REVERSE_Y) # these are not implemented for the cameras in use - self.camera.set_software_triggered_acquisition() #self.camera.set_continuous_acquisition() - self.camera.set_callback(self.streamHandler.on_new_frame) - self.camera.enable_callback() - - # only toupcam need reset strobe argument when camera's argument change - if CAMERA_TYPE == "Toupcam": - self.camera.set_reset_strobe_delay_function(self.liveController.reset_strobe_arugment) - - # load widgets - self.objectivesWidget = widgets.ObjectivesWidget(self.objectiveStore) - self.contrastManager = core.ContrastManager() - self.cameraSettingWidget = widgets.CameraSettingsWidget(self.camera, include_gain_exposure_time=False) - self.liveControlWidget = widgets.LiveControlWidget(self.streamHandler,self.liveController,self.configurationManager,show_display_options=True) - self.navigationWidget = widgets.NavigationWidget(self.navigationController,self.slidePositionController,widget_configuration='malaria') - self.dacControlWidget = widgets.DACControWidget(self.microcontroller) - self.autofocusWidget = widgets.AutoFocusWidget(self.autofocusController) - self.recordingControlWidget = widgets.RecordingWidget(self.streamHandler,self.imageSaver) - self.focusMapWidget = widgets.FocusMapWidget(self.autofocusController) - if ENABLE_TRACKING: - self.trackingControlWidget = widgets.TrackingControllerWidget(self.trackingController,self.configurationManager,show_configurations=TRACKING_SHOW_MICROSCOPE_CONFIGURATIONS) - - self.imageDisplayTabs = QTabWidget() - if USE_NAPARI_FOR_LIVE_VIEW: - self.napariLiveWidget = widgets.NapariLiveWidget(self.streamHandler, self.liveController, self.navigationController, self.configurationManager, self.contrastManager) - self.imageDisplayTabs.addTab(self.napariLiveWidget, "Live View") - else: - if ENABLE_TRACKING: - self.imageDisplayWindow = core.ImageDisplayWindow(draw_crosshairs=True) - self.imageDisplayWindow.show_ROI_selector() - else: - self.imageDisplayWindow = core.ImageDisplayWindow(draw_crosshairs=True) - self.imageDisplayTabs.addTab(self.imageDisplayWindow.widget, "Live View") - - if USE_NAPARI_FOR_MULTIPOINT: - self.napariMultiChannelWidget = widgets.NapariMultiChannelWidget(self.objectiveStore, self.contrastManager) - self.imageDisplayTabs.addTab(self.napariMultiChannelWidget, "Multichannel Acquisition") - else: - self.imageArrayDisplayWindow = core.ImageArrayDisplayWindow() - self.imageDisplayTabs.addTab(self.imageArrayDisplayWindow.widget, "Multichannel Acquisition") - - if SHOW_TILED_PREVIEW: - if USE_NAPARI_FOR_TILED_DISPLAY: - self.napariTiledDisplayWidget = widgets.NapariTiledDisplayWidget(self.objectiveStore, self.contrastManager) - self.imageDisplayTabs.addTab(self.napariTiledDisplayWidget, "Tiled Preview") - else: - self.imageDisplayWindow_scan_preview = core.ImageDisplayWindow(draw_crosshairs=True) - self.imageDisplayTabs.addTab(self.imageDisplayWindow_scan_preview.widget, "Tiled Preview") - - if USE_NAPARI_FOR_MOSAIC_DISPLAY: - self.napariMosaicDisplayWidget = widgets.NapariMosaicDisplayWidget(self.objectiveStore, self.contrastManager) - self.imageDisplayTabs.addTab(self.napariMosaicDisplayWidget, "Mosaic View") - - self.multiPointWidget = widgets.MultiPointWidget(self.multipointController,self.configurationManager) - self.multiPointWidgetGrid = widgets.MultiPointWidgetGrid(self.navigationController, self.navigationViewer, self.multipointController, self.objectiveStore, self.configurationManager, self.scanCoordinates, self.napariMosaicDisplayWidget) - - self.recordTabWidget = QTabWidget() - self.recordTabWidget.addTab(self.multiPointWidget, "Multipoint Acquisition") - if ENABLE_SCAN_GRID: - self.recordTabWidget.addTab(self.multiPointWidgetGrid, "Auto-Grid Multipoint") - self.recordTabWidget.addTab(self.focusMapWidget, "Contrast Focus Map") - if ENABLE_TRACKING: - self.recordTabWidget.addTab(self.trackingControlWidget, "Tracking") - #self.recordTabWidget.addTab(self.recordingControlWidget, "Simple Recording") - self.recordTabWidget.currentChanged.connect(lambda: self.resizeCurrentTab(self.recordTabWidget)) - self.resizeCurrentTab(self.recordTabWidget) - - frame = QFrame() - frame.setFrameStyle(QFrame.Panel | QFrame.Raised) - # Creating the top row layout and adding widgets - top_row_layout = QHBoxLayout() - top_row_layout.addWidget(self.objectivesWidget) - top_row_layout.setContentsMargins(1, 1, 1, 1) - frame.setLayout(top_row_layout) # Set the layout on the frame - - - # layout widgets - layout = QVBoxLayout() - #layout.addWidget(self.cameraSettingWidget) - layout.addWidget(self.liveControlWidget) - layout.addWidget(self.navigationWidget) - if SHOW_DAC_CONTROL: - layout.addWidget(self.dacControlWidget) - layout.addWidget(self.autofocusWidget) - layout.addWidget(self.recordTabWidget) - layout.addWidget(frame) - layout.addWidget(self.navigationViewer) - layout.addStretch() - - # transfer the layout to the central widget - self.centralWidget = QWidget() - self.centralWidget.setLayout(layout) - # self.centralWidget.setFixedSize(self.centralWidget.minimumSize()) - # self.centralWidget.setFixedWidth(self.centralWidget.minimumWidth()) - # self.centralWidget.setMaximumWidth(self.centralWidget.minimumWidth()) - self.centralWidget.setFixedWidth(self.centralWidget.minimumSizeHint().width()) - - if SINGLE_WINDOW: - dock_display = dock.Dock('Image Display', autoOrientation = False) - dock_display.showTitleBar() - dock_display.addWidget(self.imageDisplayTabs) - dock_display.setStretch(x=100,y=None) - dock_controlPanel = dock.Dock('Controls', autoOrientation = False) - # dock_controlPanel.showTitleBar() - dock_controlPanel.addWidget(self.centralWidget) - dock_controlPanel.setStretch(x=1,y=None) - dock_controlPanel.setFixedWidth(dock_controlPanel.minimumSizeHint().width()) - main_dockArea = dock.DockArea() - main_dockArea.addDock(dock_display) - main_dockArea.addDock(dock_controlPanel,'right') - self.setCentralWidget(main_dockArea) - desktopWidget = QDesktopWidget() - height_min = 0.9*desktopWidget.height() - width_min = 0.96*desktopWidget.width() - self.setMinimumSize(int(width_min),int(height_min)) - else: - self.setCentralWidget(self.centralWidget) - self.tabbedImageDisplayWindow = QMainWindow() - self.tabbedImageDisplayWindow.setCentralWidget(self.imageDisplayTabs) - self.tabbedImageDisplayWindow.setWindowFlags(self.windowFlags() | Qt.CustomizeWindowHint) - self.tabbedImageDisplayWindow.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint) - desktopWidget = QDesktopWidget() - width = 0.96*desktopWidget.height() - height = width - self.tabbedImageDisplayWindow.setFixedSize(width,height) - self.tabbedImageDisplayWindow.show() - - # make connections - self.streamHandler.signal_new_frame_received.connect(self.liveController.on_new_frame) - self.streamHandler.packet_image_to_write.connect(self.imageSaver.enqueue) - # self.streamHandler.packet_image_for_tracking.connect(self.trackingController.on_new_frame) - self.navigationController.xPos.connect(lambda x:self.navigationWidget.label_Xpos.setText("{:.2f}".format(x))) - self.navigationController.yPos.connect(lambda x:self.navigationWidget.label_Ypos.setText("{:.2f}".format(x))) - self.navigationController.zPos.connect(lambda x:self.navigationWidget.label_Zpos.setText("{:.2f}".format(x))) - if ENABLE_TRACKING: - self.navigationController.signal_joystick_button_pressed.connect(self.trackingControlWidget.slot_joystick_button_pressed) - else: - self.navigationController.signal_joystick_button_pressed.connect(self.autofocusController.autofocus) - self.multipointController.signal_current_configuration.connect(self.liveControlWidget.set_microscope_mode) - self.multiPointWidget.signal_acquisition_started.connect(self.navigationWidget.toggle_navigation_controls) - if ENABLE_SCAN_GRID: - self.multiPointWidgetGrid.signal_acquisition_started.connect(self.navigationWidget.toggle_navigation_controls) - - if USE_NAPARI_FOR_LIVE_VIEW: - self.autofocusController.image_to_display.connect(lambda image: self.napariLiveWidget.updateLiveLayer(image, from_autofocus=True)) - self.streamHandler.image_to_display.connect(lambda image: self.napariLiveWidget.updateLiveLayer(image, from_autofocus=False)) - self.multipointController.image_to_display.connect(lambda image: self.napariLiveWidget.updateLiveLayer(image, from_autofocus=False)) - self.napariLiveWidget.signal_coordinates_clicked.connect(self.navigationController.move_from_click) - else: - self.streamHandler.image_to_display.connect(self.imageDisplay.enqueue) - self.autofocusController.image_to_display.connect(self.imageDisplayWindow.display_image) - self.multipointController.image_to_display.connect(self.imageDisplayWindow.display_image) - self.imageDisplay.image_to_display.connect(self.imageDisplayWindow.display_image) - self.imageDisplayWindow.image_click_coordinates.connect(self.navigationController.move_from_click) - - if USE_NAPARI_FOR_MULTIPOINT: - self.multiPointWidget.signal_acquisition_channels.connect(self.napariMultiChannelWidget.initChannels) - self.multiPointWidget.signal_acquisition_shape.connect(self.napariMultiChannelWidget.initLayersShape) - if ENABLE_SCAN_GRID: - self.multiPointWidgetGrid.signal_acquisition_channels.connect(self.napariMultiChannelWidget.initChannels) - self.multiPointWidgetGrid.signal_acquisition_shape.connect(self.napariMultiChannelWidget.initLayersShape) - - self.multipointController.napari_layers_init.connect(self.napariMultiChannelWidget.initLayers) - self.multipointController.napari_layers_update.connect(self.napariMultiChannelWidget.updateLayers) - - else: - self.multipointController.image_to_display_multi.connect(self.imageArrayDisplayWindow.display_image) - - if SHOW_TILED_PREVIEW: - if USE_NAPARI_FOR_TILED_DISPLAY: - self.multiPointWidget.signal_acquisition_channels.connect(self.napariTiledDisplayWidget.initChannels) - self.multiPointWidget.signal_acquisition_shape.connect(self.napariTiledDisplayWidget.initLayersShape) - if ENABLE_SCAN_GRID: - self.multiPointWidgetGrid.signal_acquisition_channels.connect(self.napariTiledDisplayWidget.initChannels) - self.multiPointWidgetGrid.signal_acquisition_shape.connect(self.napariTiledDisplayWidget.initLayersShape) - - self.multipointController.napari_layers_init.connect(self.napariTiledDisplayWidget.initLayers) - self.multipointController.napari_layers_update.connect(self.napariTiledDisplayWidget.updateLayers) - self.napariTiledDisplayWidget.signal_coordinates_clicked.connect(self.navigationController.scan_preview_move_from_click) - - else: - self.multipointController.image_to_display_tiled_preview.connect(self.imageDisplayWindow_scan_preview.display_image) - self.imageDisplayWindow_scan_preview.image_click_coordinates.connect(self.navigationController.scan_preview_move_from_click) - - if USE_NAPARI_FOR_MOSAIC_DISPLAY: - self.multiPointWidget.signal_acquisition_channels.connect(self.napariMosaicDisplayWidget.initChannels) - self.multiPointWidget.signal_acquisition_shape.connect(self.napariMosaicDisplayWidget.initLayersShape) - if ENABLE_SCAN_GRID: - self.multiPointWidgetGrid.signal_acquisition_channels.connect(self.napariMosaicDisplayWidget.initChannels) - self.multiPointWidgetGrid.signal_acquisition_shape.connect(self.napariMosaicDisplayWidget.initLayersShape) - self.multiPointWidgetGrid.signal_draw_shape.connect(self.napariMosaicDisplayWidget.enable_shape_drawing) - self.napariMosaicDisplayWidget.signal_shape_drawn.connect(self.multiPointWidgetGrid.update_manual_shape) - - self.multipointController.napari_mosaic_update.connect(self.napariMosaicDisplayWidget.updateMosaic) - self.napariMosaicDisplayWidget.signal_coordinates_clicked.connect(self.navigationController.move_from_click_mosaic) - self.napariMosaicDisplayWidget.signal_update_viewer.connect(self.navigationViewer.update_slide) - - self.liveControlWidget.signal_newExposureTime.connect(self.cameraSettingWidget.set_exposure_time) - self.liveControlWidget.signal_newAnalogGain.connect(self.cameraSettingWidget.set_analog_gain) - self.liveControlWidget.update_camera_settings() - - self.slidePositionController.signal_slide_loading_position_reached.connect(self.navigationWidget.slot_slide_loading_position_reached) - self.slidePositionController.signal_slide_loading_position_reached.connect(self.multiPointWidget.disable_the_start_aquisition_button) - self.slidePositionController.signal_slide_scanning_position_reached.connect(self.navigationWidget.slot_slide_scanning_position_reached) - self.slidePositionController.signal_slide_scanning_position_reached.connect(self.multiPointWidget.enable_the_start_aquisition_button) - self.slidePositionController.signal_clear_slide.connect(self.navigationViewer.clear_slide) - self.objectivesWidget.signal_objective_changed.connect(self.navigationViewer.on_objective_changed) - self.navigationController.xyPos.connect(self.navigationViewer.update_current_location) - self.multipointController.signal_register_current_fov.connect(self.navigationViewer.register_fov) - - self.navigationController.move_to_cached_position() - - def resizeCurrentTab(self, tabWidget): - current_widget = tabWidget.currentWidget() - if current_widget: - total_height = current_widget.sizeHint().height() + tabWidget.tabBar().height() - tabWidget.resize(tabWidget.width(), total_height) - tabWidget.setMaximumHeight(total_height) - tabWidget.updateGeometry() - self.updateGeometry() - - def closeEvent(self, event): - self.navigationController.cache_current_position() - event.accept() - self.navigationController.turnoff_axis_pid_control() - - self.liveController.stop_live() - self.camera.close() - self.imageSaver.close() - self.imageDisplay.close() - if not SINGLE_WINDOW: - self.imageDisplayWindow.close() - self.imageArrayDisplayWindow.close() - self.tabbedImageDisplayWindow.close() - self.microcontroller.close() diff --git a/software/control/widgets.py b/software/control/widgets.py index 6e3f07f4..eee3eb09 100644 --- a/software/control/widgets.py +++ b/software/control/widgets.py @@ -1375,6 +1375,18 @@ def __init__(self, stage: AbstractStage, slidePositionController=None, main=None self.add_components() self.setFrameStyle(QFrame.Panel | QFrame.Raised) + self.position_update_timer = QTimer() + self.position_update_timer.setInterval(100) + self.position_update_timer.timeout.connect(self._update_position) + self.position_update_timer.start() + + def _update_position(self): + pos = self.stage.get_pos() + self.label_Xpos.setNum(pos.x_mm) + self.label_Ypos.setNum(pos.y_mm) + # NOTE: The z label is in um + self.label_Zpos.setNum(pos.z_mm * 1000) + def add_components(self): x_label = QLabel('X :') x_label.setFixedWidth(20) @@ -1666,6 +1678,16 @@ def __init__(self, stage: Optional[AbstractStage], slidePositionController=None, self.add_z_buttons = add_z_buttons self.initUI() + self.position_update_timer = QTimer() + self.position_update_timer.setInterval(100) + self.position_update_timer.timeout.connect(self._update_position) + + def _update_position(self): + pos = self.stage.get_pos() + self.label_Xpos.setText(f"{pos.x_mm:.3f} mm") + self.label_Ypos.setText(f"{pos.y_mm:.3f} mm") + self.label_Zpos.setText(f"{pos.z_mm * 1000:.3f} μm") + def initUI(self): layout = QHBoxLayout() layout.setContentsMargins(5, 2, 5, 4) # Reduce vertical margins to make it thinner @@ -1735,15 +1757,6 @@ def initUI(self): self.setFixedHeight(self.sizeHint().height()) # Set fixed height to make it as thin as possible self.connect_signals() - def update_x_position(self, x): - self.label_Xpos.setText(f"{x:.3f} mm") - - def update_y_position(self, y): - self.label_Ypos.setText(f"{y:.3f} mm") - - def update_z_position(self, z): - self.label_Zpos.setText(f"{z:.3f} μm") - def home_z(self): msg = QMessageBox() msg.setIcon(QMessageBox.Information)