def _on_current_feature_changes(self, feature): """ Update the feature panel controls when the current feature VA is modified :param feature: (CryoFeature or None) the newly selected current feature """ if self._feature_name_va_connector: self._feature_name_va_connector.disconnect() if self._feature_status_va_connector: self._feature_status_va_connector.disconnect() if self._feature_z_va_connector: self._feature_z_va_connector.disconnect() def enable_feature_ctrls(enable): self._panel.cmb_feature_status.Enable(enable) self._panel.ctrl_feature_z.Enable(enable) self._panel.btn_use_current_z.Enable(enable) self._panel.btn_go_to_feature.Enable(enable) if not feature: enable_feature_ctrls(False) self._panel.cmb_features.SetValue("No Feature Selected") self._panel.cmb_feature_status.SetValue("Not Selected") return save_features(self._tab.conf.pj_last_path, self._tab_data_model.main.features) enable_feature_ctrls(True) # Set feature list with the current feature index = self._tab_data_model.main.features.value.index(feature) self._panel.cmb_features.SetSelection(index) # Disconnect and reconnect the VA connectors to the newly selected feature self._feature_name_va_connector = VigilantAttributeConnector( feature.name, self._panel.cmb_features, events=wx.EVT_TEXT_ENTER, va_2_ctrl=self._on_feature_name, ctrl_2_va=self._on_cmb_feature_name_change, ) self._feature_status_va_connector = VigilantAttributeConnector( feature.status, self._panel.cmb_feature_status, events=wx.EVT_COMBOBOX, ctrl_2_va=self._on_cmb_feature_status_change, va_2_ctrl=self._on_feature_status) # TODO: check, it seems that sometimes the EVT_TEXT_ENTER is first received # by the VAC, before the widget itself, which prevents getting the right value. self._feature_z_va_connector = VigilantAttributeConnector( feature.pos, self._panel.ctrl_feature_z, events=wx.EVT_TEXT_ENTER, ctrl_2_va=self._on_ctrl_feature_z_change, va_2_ctrl=self._on_feature_pos)
def __init__(self, name, va=None, hw_comp=None, stream=None, lbl_ctrl=None, value_ctrl=None, va_2_ctrl=None, ctrl_2_va=None, events=None): """ See the super classes for parameter descriptions """ Entry.__init__(self, name, hw_comp, stream, lbl_ctrl, value_ctrl) # TODO: can it happen value_ctrl is None and va_2_ctrl is not None?! if va and (value_ctrl or va_2_ctrl): VigilantAttributeConnector.__init__(self, va, value_ctrl, va_2_ctrl, ctrl_2_va, events) elif any((va_2_ctrl, ctrl_2_va, events)): raise ValueError("Cannot create VigilantAttributeConnector for %s, while also " "receiving value getting and setting parameters!" % name) else: self.vigilattr = va # Attribute needed, even if there's no VAC to provide it logging.debug("Creating empty SettingEntry without VigilantAttributeConnector")
def __init__(self, btn_ctrl, va, _): self.btn = btn_ctrl self.vac = VigilantAttributeConnector(va, btn_ctrl, self._va_to_btn, self._btn_to_va, events=wx.EVT_BUTTON)
def __init__(self, tab_data, main_frame, tab_prefix): """ Binds the step and axis buttons to their appropriate Vigilant Attributes in the model.ActuatorGUIData. It only connects the buttons which exists with the actuators which exists. tab_data (ActuatorGUIData): the data model of the tab main_frame: (wx.Frame): the main frame of the GUI tab_prefix (string): common prefix of the names of the buttons """ self._tab_data_model = tab_data self._main_frame = main_frame # Check which widgets and VAs exist. Bind the matching ones. # Bind size steps (= sliders) self._va_connectors = [] for an, ss in tab_data.stepsizes.items(): slider_name = tab_prefix + "slider_" + an try: slider = getattr(main_frame, slider_name) except AttributeError: continue slider.SetRange(*ss.range) vac = VigilantAttributeConnector(ss, slider, events=wx.EVT_SLIDER) self._va_connectors.append(vac) if not self._va_connectors: logging.warning("No slider found for tab %s", tab_prefix) # Bind buttons self._btns = [] for actuator, axis in tab_data.axes: for suffix, factor in [("m", -1), ("p", 1)]: # something like "lens_align_btn_p_mirror_rz" btn_name = "%sbtn_%s_%s_%s" % (tab_prefix, suffix, actuator, axis) try: btn = getattr(main_frame, btn_name) except AttributeError: logging.debug("No button in GUI found for axis %s", axis) continue def btn_action(evt, tab_data=tab_data, actuator=actuator, axis=axis, factor=factor): # Button events don't contain key state, so check ourselves if wx.GetKeyState(wx.WXK_SHIFT): factor /= 10 tab_data.step(actuator, axis, factor) btn.Bind(wx.EVT_BUTTON, btn_action) self._btns.append(btn) tab_data.main.is_acquiring.subscribe(self._on_acquisition)
def __init__(self, tab_data, main_frame, btn_prefix): """ Binds the 'hardware' buttons to their appropriate Vigilant Attributes in the model.MainGUIData tab_data (MicroscopyGUIData): the data model of the tab main_frame: (wx.Frame): the main frame of the GUI btn_prefix (string): common prefix of the names of the buttons """ main_data = tab_data.main # Look for which buttons actually exist, and which VAs exist. Bind the # fitting ones self._callbacks = [] self._va_connectors = [] for btn_name, vaname in btn_to_va.items(): try: btn = getattr(main_frame, btn_prefix + btn_name) except AttributeError: continue try: va = getattr(main_data, vaname) except AttributeError: # This microscope is not available btn.Hide() # TODO: need to update layout? continue logging.debug("Connecting button %s to %s", btn_name, vaname) vac = VigilantAttributeConnector( va, btn, lambda s, btn=btn: btn.SetToggle(s != model.STATE_OFF), lambda btn=btn: model.STATE_ON if btn.GetToggle() else model.STATE_OFF, events=wx.EVT_BUTTON) self._va_connectors.append(vac) if not self._va_connectors: logging.warning("No microscope button found in tab %s", btn_prefix)
def __init__(self, parent, orig_tab_data): xrcfr_overview_acq.__init__(self, parent) self.conf = get_acqui_conf() # True when acquisition occurs self.acquiring = False self.data = None # a ProgressiveFuture if the acquisition is going on self.acq_future = None self._acq_future_connector = None self._main_data_model = orig_tab_data.main # duplicate the interface, but with only one view self._tab_data_model = self.duplicate_tab_data_model(orig_tab_data) # The pattern to use for storing each tile file individually # None disables storing them self.filename_tiles = create_filename(self.conf.last_path, "{datelng}-{timelng}-overview", ".ome.tiff") # Create a new settings controller for the acquisition dialog self._settings_controller = LocalizationSettingsController( self, self._tab_data_model, highlight_change=True # also adds a "Reset" context menu ) self.zsteps = model.IntContinuous(1, range=(1, 51)) self._zsteps_vac = VigilantAttributeConnector(self.zsteps, self.zstack_steps, events=wx.EVT_SLIDER) orig_view = orig_tab_data.focussedView.value self._view = self._tab_data_model.focussedView.value self.streambar_controller = StreamBarController(self._tab_data_model, self.pnl_secom_streams, static=True, ignore_view=True) # The streams currently displayed are the one visible self.add_streams() # The list of streams ready for acquisition (just used as a cache) self._acq_streams = {} # Compute the preset values for each preset self._orig_entries = get_global_settings_entries(self._settings_controller) self._orig_settings = preset_as_is(self._orig_entries) for sc in self.streambar_controller.stream_controllers: self._orig_entries += get_local_settings_entries(sc) self.start_listening_to_va() # make sure the view displays the same thing as the one we are # duplicating self._view.view_pos.value = orig_view.view_pos.value self._view.mpp.value = orig_view.mpp.value self._view.merge_ratio.value = orig_view.merge_ratio.value # attach the view to the viewport self.pnl_view_acq.canvas.fit_view_to_next_image = False self.pnl_view_acq.setView(self._view, self._tab_data_model) self.Bind(wx.EVT_CHAR_HOOK, self.on_key) self.btn_cancel.Bind(wx.EVT_BUTTON, self.on_close) self.btn_secom_acquire.Bind(wx.EVT_BUTTON, self.on_acquire) self.Bind(wx.EVT_CLOSE, self.on_close) # on_streams_changed is compatible because it doesn't use the args # To update the estimated time when streams are removed/added self._view.stream_tree.flat.subscribe(self.on_streams_changed) # Set parameters for tiled acq self.overlap = 0.2 try: # Use the stage range, which can be overridden by the MD_POS_ACTIVE_RANGE, # which can be overridden by MD_OVERVIEW_RANGE. # Note: this last one might be temporary, until we have a RoA tool provided in the GUI. stage_rng = { "x": self._main_data_model.stage.axes["x"].range, "y": self._main_data_model.stage.axes["y"].range } stage_md = self._main_data_model.stage.getMetadata() if model.MD_POS_ACTIVE_RANGE in stage_md: stage_rng.update(stage_md[model.MD_POS_ACTIVE_RANGE]) if model.MD_OVERVIEW_RANGE in stage_md: stage_rng.update(stage_md[model.MD_OVERVIEW_RANGE]) # left, bottom, right, top self.area = (stage_rng["x"][0], stage_rng["y"][0], stage_rng["x"][1], stage_rng["y"][1]) except (KeyError, IndexError): raise ValueError("Failed to find stage.MD_POS_ACTIVE_RANGE with x and y range") # Note: It should never be possible to reach here with no streams streams = self.get_acq_streams() for s in streams: self._view.addStream(s) self.update_acquisition_time()
def __init__(self, parent, orig_tab_data): xrcfr_overview_acq.__init__(self, parent) self.conf = get_acqui_conf() # True when acquisition occurs self.acquiring = False self.data = None # a ProgressiveFuture if the acquisition is going on self.acq_future = None self._acq_future_connector = None self._main_data_model = orig_tab_data.main # duplicate the interface, but with only one view self._tab_data_model = self.duplicate_tab_data_model(orig_tab_data) # Store the final image as {datelng}-{timelng}-overview # The pattern to store them in a sub folder, with the name xxxx-overview-tiles/xxx-overview-NxM.ome.tiff # The pattern to use for storing each tile file individually # None disables storing them save_dir = self.conf.last_path if isinstance(orig_tab_data, guimodel.CryoGUIData): save_dir = self.conf.pj_last_path self.filename = create_filename(save_dir, "{datelng}-{timelng}-overview", ".ome.tiff") assert self.filename.endswith(".ome.tiff") dirname, basename = os.path.split(self.filename) tiles_dir = os.path.join(dirname, basename[:-len(".ome.tiff")] + "-tiles") self.filename_tiles = os.path.join(tiles_dir, basename) # Create a new settings controller for the acquisition dialog self._settings_controller = LocalizationSettingsController( self, self._tab_data_model, ) self.zsteps = model.IntContinuous(1, range=(1, 51)) # The depth of field is an indication of how far the focus needs to move # to see the current in-focus position out-of-focus. So it's a good default # value for the zstep size. We use 2x to "really" see something else. # Typically, it's about 1 µm. dof = self._main_data_model.ccd.depthOfField.value self.zstep_size = model.FloatContinuous(2 * dof, range=(1e-9, 100e-6), unit="m") self._zstep_size_vac = VigilantAttributeConnector( self.zstep_size, self.zstep_size_ctrl, events=wx.EVT_COMMAND_ENTER) self.tiles_nx = model.IntContinuous(5, range=(1, 1000)) self.tiles_ny = model.IntContinuous(5, range=(1, 1000)) self._zsteps_vac = VigilantAttributeConnector(self.zsteps, self.zstack_steps, events=wx.EVT_SLIDER) self._tiles_n_vacx = VigilantAttributeConnector( self.tiles_nx, self.tiles_number_x, events=wx.EVT_COMMAND_ENTER) self._tiles_n_vacy = VigilantAttributeConnector( self.tiles_ny, self.tiles_number_y, events=wx.EVT_COMMAND_ENTER) self.area = None # None or 4 floats: left, top, right, bottom positions of the acquisition area (in m) orig_view = orig_tab_data.focussedView.value self._view = self._tab_data_model.focussedView.value self.streambar_controller = StreamBarController(self._tab_data_model, self.pnl_secom_streams, static=True, ignore_view=True) # The streams currently displayed are the one visible self.add_streams() # The list of streams ready for acquisition (just used as a cache) self._acq_streams = {} # Find every setting, and listen to it self._orig_entries = get_global_settings_entries( self._settings_controller) for sc in self.streambar_controller.stream_controllers: self._orig_entries += get_local_settings_entries(sc) self.start_listening_to_va() # make sure the view displays the same thing as the one we are # duplicating self._view.view_pos.value = orig_view.view_pos.value self._view.mpp.value = orig_view.mpp.value self._view.merge_ratio.value = orig_view.merge_ratio.value # attach the view to the viewport self.pnl_view_acq.canvas.fit_view_to_next_image = False self.pnl_view_acq.setView(self._view, self._tab_data_model) self.Bind(wx.EVT_CHAR_HOOK, self.on_key) self.btn_cancel.Bind(wx.EVT_BUTTON, self.on_close) self.btn_secom_acquire.Bind(wx.EVT_BUTTON, self.on_acquire) self.Bind(wx.EVT_CLOSE, self.on_close) # Set parameters for tiled acq self.overlap = 0.2 try: # Use the stage range, which can be overridden by the MD_POS_ACTIVE_RANGE. # Note: this last one might be temporary, until we have a RoA tool provided in the GUI. self._tiling_rng = { "x": self._main_data_model.stage.axes["x"].range, "y": self._main_data_model.stage.axes["y"].range } stage_md = self._main_data_model.stage.getMetadata() if model.MD_POS_ACTIVE_RANGE in stage_md: self._tiling_rng.update(stage_md[model.MD_POS_ACTIVE_RANGE]) except (KeyError, IndexError): raise ValueError( "Failed to find stage.MD_POS_ACTIVE_RANGE with x and y range") # Note: It should never be possible to reach here with no streams streams = self.get_acq_streams() for s in streams: self._view.addStream(s) # To update the estimated time & area when streams are removed/added self._view.stream_tree.flat.subscribe(self.on_streams_changed, init=True)
def resume(self): if hasattr(self, "va_2_ctrl") and self.va_2_ctrl: VigilantAttributeConnector.resume(self)
def pause(self): if hasattr(self, "va_2_ctrl") and self.va_2_ctrl: VigilantAttributeConnector.pause(self)
def __init__(self, tab_data, tab_panel, tab_prefix): """ Binds the step and axis buttons to their appropriate Vigilant Attributes in the model.ActuatorGUIData. It only connects the buttons which exists with the actuators which exists. tab_data (ActuatorGUIData): the data model of the tab tab_panel: (wx.Frame): the main frame of the GUI tab_prefix (string): common prefix of the names of the buttons """ self._tab_data_model = tab_data self._tab_panel = tab_panel # Check which widgets and VAs exist. Bind the matching ones. # Bind size steps (= sliders) self._va_connectors = [] for an, ss in tab_data.stepsizes.items(): slider_name = tab_prefix + "slider_" + an try: slider = getattr(tab_panel, slider_name) except AttributeError: continue slider.SetRange(*ss.range) vac = VigilantAttributeConnector(ss, slider, events=wx.EVT_SLIDER) self._va_connectors.append(vac) if not self._va_connectors: logging.warning("No slider found for tab %s", tab_prefix) # Bind buttons self._btns = [] for actuator, axis in tab_data.axes: for suffix, factor in [("m", -1), ("p", 1)]: # something like "lens_align_btn_p_mirror_rz" btn_name = "%sbtn_%s_%s_%s" % (tab_prefix, suffix, actuator, axis) try: btn = getattr(tab_panel, btn_name) except AttributeError: logging.debug("No button in GUI found for axis %s", axis) continue def btn_action(evt, tab_data=tab_data, actuator=actuator, axis=axis, factor=factor): # Button events don't contain key state, so check ourselves if wx.GetKeyState(wx.WXK_SHIFT): factor /= 10 tab_data.step(actuator, axis, factor) btn.Bind(wx.EVT_BUTTON, btn_action) self._btns.append(btn) # On SECOM, show the right aligner panel (X/Y or A/B) if ("aligner", "x") in tab_data.axes: tab_panel.pnl_xy_align.Show() if ("aligner", "a") in tab_data.axes: tab_panel.pnl_ab_align.Show() # On SPARC, show the Yaw/Pitch only if available if hasattr(tab_panel, 'pnl_sparc_rot'): showrot = (("mirror", "ry") in tab_data.axes or ("mirror", "rz") in tab_data.axes) tab_panel.pnl_sparc_rot.Show(showrot) # On SPARC, show the fiber aligner only if needed if hasattr(tab_panel, 'pnl_fibaligner'): showfib = ("fibaligner", "x") in tab_data.axes tab_panel.pnl_fibaligner.Show(showfib) tab_data.main.is_acquiring.subscribe(self._on_acquisition)