def create_panel_widget( self, ui: Facade.UserInterface, document_controller: Facade.DocumentWindow) -> Facade.ColumnWidget: stem_controller_ = typing.cast( stem_controller.STEMController, Registry.get_component("stem_controller")) self.__scan_hardware_source_choice_model = ui._ui.create_persistent_string_model( "scan_acquisition_hardware_source_id") self.__scan_hardware_source_choice = HardwareSourceChoice.HardwareSourceChoice( self.__scan_hardware_source_choice_model, lambda hardware_source: hardware_source.features.get( "is_scanning", False)) self.__camera_hardware_source_choice_model = ui._ui.create_persistent_string_model( "scan_acquisition_camera_hardware_source_id") self.__camera_hardware_source_choice = HardwareSourceChoice.HardwareSourceChoice( self.__camera_hardware_source_choice_model, lambda hardware_source: hardware_source.features.get( "is_camera", False)) self.__scan_hardware_source_stream = HardwareSourceChoice.HardwareSourceChoiceStream( self.__scan_hardware_source_choice).add_ref() self.__camera_hardware_source_stream = HardwareSourceChoice.HardwareSourceChoiceStream( self.__camera_hardware_source_choice).add_ref() def clear_scan_context_fields() -> None: self.__roi_description.text = _("Scan context not active") self.__scan_label_widget.text = None self.__scan_specifier.scan_context = stem_controller.ScanContext() self.__scan_specifier.scan_count = 1 self.__scan_specifier.size = None self.__scan_specifier.drift_interval_lines = 0 self.__scan_specifier.drift_interval_scans = 0 self.__acquire_button._widget.enabled = self.__acquisition_state == SequenceState.scanning # focus will be on the SI data, so enable if scanning self.__scan_pixels = 0 def update_context() -> None: assert self.__scan_hardware_source_choice scan_hardware_source = typing.cast( scan_base.ScanHardwareSource, self.__scan_hardware_source_choice.hardware_source) if not scan_hardware_source: clear_scan_context_fields() return scan_context = scan_hardware_source.scan_context scan_context_size = scan_context.size exposure_ms = self.__exposure_time_ms_value_model.value or 0.0 if self.__exposure_time_ms_value_model else 0.0 if scan_context.is_valid and scan_hardware_source.line_scan_enabled and scan_hardware_source.line_scan_vector: assert scan_context_size calibration = scan_context.calibration start = Geometry.FloatPoint.make( scan_hardware_source.line_scan_vector[0]) end = Geometry.FloatPoint.make( scan_hardware_source.line_scan_vector[1]) length = int( Geometry.distance(start, end) * scan_context_size.height) max_dim = max(scan_context_size.width, scan_context_size.height) length_str = calibration.convert_to_calibrated_size_str( length, value_range=(0, max_dim), samples=max_dim) line_str = _("Line Scan") self.__roi_description.text = f"{line_str} {length_str} ({length} px)" scan_str = _("Scan (1D)") scan_length = max(self.__scan_width, 1) self.__scan_label_widget.text = f"{scan_str} {scan_length} px" self.__scan_pixels = scan_length self.__scan_specifier.scan_context = copy.deepcopy( scan_context) self.__scan_specifier.scan_count = max(self.__scan_count, 1) self.__scan_specifier.size = 1, scan_length self.__scan_specifier.drift_interval_lines = 0 self.__scan_specifier.drift_interval_scans = 0 self.__acquire_button._widget.enabled = True elif scan_context.is_valid and scan_hardware_source.subscan_enabled and scan_hardware_source.subscan_region: assert scan_context_size calibration = scan_context.calibration width = scan_hardware_source.subscan_region.width * scan_context_size.width height = scan_hardware_source.subscan_region.height * scan_context_size.height width_str = calibration.convert_to_calibrated_size_str( width, value_range=(0, scan_context_size.width), samples=scan_context_size.width) height_str = calibration.convert_to_calibrated_size_str( height, value_range=(0, scan_context_size.height), samples=scan_context_size.height) rect_str = _("Subscan") self.__roi_description.text = f"{rect_str} {width_str} x {height_str} ({int(width)} px x {int(height)} px)" scan_str = _("Scan (2D)") scan_width = self.__scan_width scan_height = int(self.__scan_width * height / width) drift_lines = scan_hardware_source.calculate_drift_lines( scan_width, exposure_ms / 1000) if scan_hardware_source else 0 drift_str = f" / Drift {drift_lines} lines" if drift_lines > 0 else str( ) drift_scans = scan_hardware_source.calculate_drift_scans() drift_str = f" / Drift {drift_scans} scans" if drift_scans > 0 else drift_str self.__scan_label_widget.text = f"{scan_str} {scan_width} x {scan_height} px" + drift_str self.__scan_pixels = scan_width * scan_height self.__scan_specifier.scan_context = copy.deepcopy( scan_context) self.__scan_specifier.scan_count = max(self.__scan_count, 1) self.__scan_specifier.size = scan_height, scan_width self.__scan_specifier.drift_interval_lines = drift_lines self.__scan_specifier.drift_interval_scans = drift_scans self.__acquire_button._widget.enabled = True elif scan_context.is_valid: assert scan_context_size calibration = scan_context.calibration width = scan_context_size.width height = scan_context_size.height width_str = calibration.convert_to_calibrated_size_str( width, value_range=(0, scan_context_size.width), samples=scan_context_size.width) height_str = calibration.convert_to_calibrated_size_str( height, value_range=(0, scan_context_size.height), samples=scan_context_size.height) data_str = _("Context Scan") self.__roi_description.text = f"{data_str} {width_str} x {height_str} ({int(width)} x {int(height)})" scan_str = _("Scan (2D)") scan_width = self.__scan_width scan_height = int(self.__scan_width * height / width) drift_lines = scan_hardware_source.calculate_drift_lines( scan_width, exposure_ms / 1000) if scan_hardware_source else 0 drift_str = f" / Drift {drift_lines} lines" if drift_lines > 0 else str( ) drift_scans = scan_hardware_source.calculate_drift_scans() drift_str = f" / Drift {drift_scans} scans" if drift_scans > 0 else drift_str self.__scan_label_widget.text = f"{scan_str} {scan_width} x {scan_height} px" + drift_str self.__scan_pixels = scan_width * scan_height self.__scan_specifier.scan_context = copy.deepcopy( scan_context) self.__scan_specifier.scan_count = max(self.__scan_count, 1) self.__scan_specifier.size = scan_height, scan_width self.__scan_specifier.drift_interval_lines = drift_lines self.__scan_specifier.drift_interval_scans = drift_scans self.__acquire_button._widget.enabled = True else: clear_scan_context_fields() self.__scan_count_widget.text = Converter.IntegerToStringConverter( ).convert(self.__scan_count) self.__scan_width_widget.text = Converter.IntegerToStringConverter( ).convert(self.__scan_width) self.__update_estimate() def stem_controller_property_changed(key: str) -> None: if key in ("subscan_state", "subscan_region", "subscan_rotation", "line_scan_state", "line_scan_vector", "drift_channel_id", "drift_region", "drift_settings"): document_controller._document_controller.event_loop.call_soon_threadsafe( update_context) def scan_context_changed() -> None: # this can be triggered from a thread, so use call soon to transfer it to the UI thread. document_controller._document_controller.event_loop.call_soon_threadsafe( update_context) self.__stem_controller_property_listener = None self.__scan_context_changed_listener = None if stem_controller_: self.__stem_controller_property_listener = stem_controller_.property_changed_event.listen( stem_controller_property_changed) self.__scan_context_changed_listener = stem_controller_.scan_context_changed_event.listen( scan_context_changed) column = ui.create_column_widget() self.__styles_list_model = ListModel.ListModel[ ScanAcquisitionProcessing](items=[ ScanAcquisitionProcessing.SUM_PROJECT, ScanAcquisitionProcessing.NONE ]) self.__styles_list_property_model = ListModel.ListPropertyModel( self.__styles_list_model) self.__style_combo_box = ui.create_combo_box_widget( self.__styles_list_property_model.value, item_text_getter=operator.attrgetter("value.display_name")) self.__style_combo_box._widget.set_property("min-width", 100) items_binding = Binding.PropertyBinding( self.__styles_list_property_model, "value") items_binding.source_setter = None typing.cast(UserInterfaceModule.ComboBoxWidget, self.__style_combo_box._widget).bind_items(items_binding) self.__style_combo_box.current_index = 0 self.__acquire_button = ui.create_push_button_widget(_("Acquire")) self.__progress_bar = ui.create_progress_bar_widget() # self.__progress_bar.enabled = False self.__roi_description = ui.create_label_widget() self.__scan_count_widget = ui.create_line_edit_widget() self.__scan_count_widget._widget.set_property("width", 72) self.__scan_processing_widget = ui.create_combo_box_widget( items=["Raw", "Sum", "Raw + Sum"]) self.__scan_width_widget = ui.create_line_edit_widget() self.__exposure_time_widget = ui.create_line_edit_widget() self.__estimate_label_widget = ui.create_label_widget() self.__scan_label_widget = ui.create_label_widget() class ComboBoxWidget: def __init__(self, widget: UserInterfaceModule.ComboBoxWidget) -> None: self.__combo_box_widget = widget @property def _widget(self) -> UserInterfaceModule.ComboBoxWidget: return self.__combo_box_widget camera_row = ui.create_row_widget() camera_row.add_spacing(12) camera_row.add( ComboBoxWidget( self.__camera_hardware_source_choice.create_combo_box(ui._ui))) camera_row.add_spacing(12) camera_row.add(self.__style_combo_box) camera_row.add_spacing(12) camera_row.add_stretch() scan_choice_row = ui.create_row_widget() scan_choice_row.add_spacing(12) scan_choice_row.add( ComboBoxWidget( self.__scan_hardware_source_choice.create_combo_box(ui._ui))) scan_choice_row.add_spacing(12) scan_choice_row.add_stretch() scan_count_row = ui.create_row_widget() scan_count_row.add_spacing(12) scan_count_row.add(ui.create_label_widget("Scan Count")) scan_count_row.add_spacing(12) scan_count_row.add(self.__scan_count_widget) scan_count_row.add_spacing(12) scan_count_row.add(self.__scan_processing_widget) scan_count_row.add_spacing(12) scan_count_row.add_stretch() roi_size_row = ui.create_row_widget() roi_size_row.add_spacing(12) roi_size_row.add(self.__roi_description) roi_size_row.add_spacing(12) roi_size_row.add_stretch() scan_spacing_pixels_row = ui.create_row_widget() scan_spacing_pixels_row.add_spacing(12) scan_spacing_pixels_row.add( ui.create_label_widget("Scan Width (pixels)")) scan_spacing_pixels_row.add_spacing(12) scan_spacing_pixels_row.add(self.__scan_width_widget) scan_spacing_pixels_row.add_spacing(12) scan_spacing_pixels_row.add_stretch() eels_exposure_row = ui.create_row_widget() eels_exposure_row.add_spacing(12) eels_exposure_row.add( ui.create_label_widget("Camera Exposure Time (ms)")) eels_exposure_row.add_spacing(12) eels_exposure_row.add(self.__exposure_time_widget) eels_exposure_row.add_spacing(12) eels_exposure_row.add_stretch() scan_row = ui.create_row_widget() scan_row.add_spacing(12) scan_row.add(self.__scan_label_widget) scan_row.add_stretch() estimate_row = ui.create_row_widget() estimate_row.add_spacing(12) estimate_row.add(self.__estimate_label_widget) estimate_row.add_stretch() acquire_sequence_button_row = ui.create_row_widget() acquire_sequence_button_row.add(self.__acquire_button) acquire_sequence_button_row.add_spacing(8) acquire_sequence_button_row.add(self.__progress_bar) acquire_sequence_button_row.add_spacing(8) if self.__scan_hardware_source_choice.hardware_source_count > 1: column.add_spacing(8) column.add(scan_choice_row) column.add_spacing(8) column.add(camera_row) column.add_spacing(8) column.add(scan_count_row) column.add_spacing(8) column.add(roi_size_row) column.add_spacing(8) column.add(scan_spacing_pixels_row) column.add_spacing(8) column.add(eels_exposure_row) column.add_spacing(8) column.add(scan_row) column.add_spacing(8) column.add(estimate_row) column.add_spacing(8) column.add(acquire_sequence_button_row) column.add_spacing(8) column.add_stretch() def camera_hardware_source_changed( hardware_source: typing.Optional[HardwareSource.HardwareSource] ) -> None: styles_list_model = self.__styles_list_model self.disconnect_camera_hardware_source() if hardware_source and styles_list_model: self.connect_camera_hardware_source(hardware_source) if hardware_source.features.get("has_masked_sum_option"): styles_list_model.items = [ ScanAcquisitionProcessing.SUM_PROJECT, ScanAcquisitionProcessing.NONE, ScanAcquisitionProcessing.SUM_MASKED ] else: styles_list_model.items = [ ScanAcquisitionProcessing.SUM_PROJECT, ScanAcquisitionProcessing.NONE ] self.__camera_hardware_changed_event_listener = self.__camera_hardware_source_choice.hardware_source_changed_event.listen( camera_hardware_source_changed) camera_hardware_source_changed( self.__camera_hardware_source_choice.hardware_source) def style_current_item_changed(current_item: str) -> None: self.__update_estimate() self.__style_combo_box.on_current_item_changed = style_current_item_changed def scan_count_changed(text: str) -> None: scan_count = Converter.IntegerToStringConverter().convert_back( text) or 1 scan_count = max(scan_count, 1) if scan_count != self.__scan_count: self.__scan_count = scan_count update_context() self.__scan_count_widget.request_refocus() self.__scan_count_widget.on_editing_finished = scan_count_changed def scan_width_changed(text: str) -> None: scan_width = Converter.IntegerToStringConverter().convert_back( text) or 1 scan_width = max(scan_width, 1) if scan_width != self.__scan_width: self.__scan_width = scan_width update_context() self.__scan_width_widget.request_refocus() self.__scan_width_widget.on_editing_finished = scan_width_changed def acquisition_state_changed( acquisition_state: SequenceState) -> None: self.__acquisition_state = acquisition_state async def update_state(is_idle: bool) -> None: self.__acquire_button.text = _("Acquire") if is_idle else _( "Cancel") # self.__progress_bar.enabled = not is_idle update_context() # update the cancel button if is_idle and self.__progress_task: self.__progress_task.cancel() self.__progress_task = None self.__progress_bar.value = 100 if not is_idle and not self.__progress_task: async def update_progress() -> None: while True: if self.__scan_acquisition_controller: self.__progress_bar.value = int( 100 * self.__scan_acquisition_controller.progress ) await asyncio.sleep(0.25) self.__progress_task = document_controller._document_window.event_loop.create_task( update_progress()) if acquisition_state == SequenceState.idle: self.__scan_acquisition_controller = None if self.__acquisition_state_changed_event_listener: self.__acquisition_state_changed_event_listener.close() self.__acquisition_state_changed_event_listener = None document_controller._document_window.event_loop.create_task( update_state(True)) else: document_controller._document_window.event_loop.create_task( update_state(False)) def acquire_sequence() -> None: if self.__scan_acquisition_controller: if self.__scan_acquisition_controller: self.__scan_acquisition_controller.cancel() else: scan_hardware_source_choice = self.__scan_hardware_source_choice assert scan_hardware_source_choice if scan_hardware_source_choice.hardware_source: scan_hardware_source = self.__api.get_hardware_source_by_id( scan_hardware_source_choice.hardware_source. hardware_source_id, version="1.0") else: scan_hardware_source = None camera_hardware_source_choice = self.__camera_hardware_source_choice assert camera_hardware_source_choice if camera_hardware_source_choice.hardware_source: camera_hardware_source = self.__api.get_hardware_source_by_id( camera_hardware_source_choice.hardware_source. hardware_source_id, version="1.0") else: camera_hardware_source = None if scan_hardware_source and camera_hardware_source: self.__scan_acquisition_controller = ScanAcquisitionController( self.__api, document_controller, scan_hardware_source, camera_hardware_source, self.__scan_specifier) self.__acquisition_state_changed_event_listener = self.__scan_acquisition_controller.acquisition_state_changed_event.listen( acquisition_state_changed) scan_processing = ScanProcessing( self.__scan_processing_widget.current_index in (0, 2), self.__scan_processing_widget.current_index in (1, 2)) scan_acquisition_processing = self.__style_combo_box.current_item if self.__style_combo_box and self.__style_combo_box.current_item else ScanAcquisitionProcessing.NONE self.__scan_acquisition_controller.start( scan_acquisition_processing, scan_processing) self.__acquire_button.on_clicked = acquire_sequence self.__update_estimate() update_context() return column
def test_refcounts(self) -> None: # list model model = ListModel.ListModel[typing.Any]("items") model_ref = weakref.ref(model) del model self.assertIsNone(model_ref()) # filtered model l = ListModel.ListModel[typing.Any]("items") model2 = ListModel.FilteredListModel(container=l, items_key="items") model_ref2 = weakref.ref(model2) del model2 self.assertIsNone(model_ref2()) # nested filtered model l = ListModel.ListModel[typing.Any]("items") l2 = ListModel.FilteredListModel(container=l, items_key="items") model3 = ListModel.FilteredListModel(container=l2, items_key="items") model_ref3 = weakref.ref(model3) del model3 self.assertIsNone(model_ref3()) # filtered model with item changed event l = ListModel.ListModel[typing.Any]("items") l.append_item(C()) model4 = ListModel.FilteredListModel(container=l, items_key="items") model_ref4 = weakref.ref(model4) del model4 self.assertIsNone(model_ref4()) # mapped model l = ListModel.ListModel[typing.Any]("items") model5 = ListModel.MappedListModel(container=l, master_items_key="items", items_key="items") model_ref5 = weakref.ref(model5) del model5 self.assertIsNone(model_ref5()) # mapped model of filtered model l = ListModel.ListModel[typing.Any]("items") l2 = ListModel.FilteredListModel(container=l, items_key="items") model6 = ListModel.MappedListModel(container=l2, master_items_key="items", items_key="items") model_ref6 = weakref.ref(model6) del model6 self.assertIsNone(model_ref6()) # flattened model l = ListModel.ListModel[typing.Any]("items") model7 = ListModel.FlattenedListModel(container=l, master_items_key="items", child_items_key="items", items_key="items") model_ref7 = weakref.ref(model7) del model7 self.assertIsNone(model_ref7()) # flattened model with items l = ListModel.ListModel[typing.Any]("items") l.append_item(ListModel.ListModel[typing.Any]("items")) model8 = ListModel.FlattenedListModel(container=l, master_items_key="items", child_items_key="items", items_key="items") model_ref8 = weakref.ref(model8) del model8 self.assertIsNone(model_ref8()) # list property model l = ListModel.ListModel[typing.Any]("items") model9 = ListModel.ListPropertyModel(l) model_ref9 = weakref.ref(model9) del model9 self.assertIsNone(model_ref9())