示例#1
0
 def _get_new_filename(self):
     conf = get_acqui_conf()
     return os.path.join(
         conf.last_path,
         # u"%s%s" % (time.strftime("%Y%m%d-%H%M%S"), conf.last_extension)
         u"%s.tiff" % (time.strftime("%Y%m%d-%H%M%S"),)
     )
示例#2
0
    def __init__(self, tab_data, main_frame, settings_controller, roa, vas):
        """
        tab_data (MicroscopyGUIData): the representation of the microscope GUI
        main_frame: (wx.Frame): the frame which contains the 4 viewports
        settings_controller (SettingsController)
        roa (VA): VA of the ROA
        vas (list of VAs): all the VAs which might affect acquisition (time)
        """
        self._tab_data_model = tab_data
        self._main_data_model = tab_data.main
        self._main_frame = main_frame
        self._roa = roa
        self._vas = vas

        # For file selection
        self.conf = conf.get_acqui_conf()

        # for saving/restoring the settings
        self._settings_controller = settings_controller
        self._orig_settings = {} # Entry -> value to restore

        # TODO: this should be the date at which the user presses the acquire
        # button (or when the last settings were changed)!
        # At least, we must ensure it's a new date after the acquisition
        # is done.
        # Filename to save the acquisition
        self.filename = model.StringVA(self._get_default_filename())
        self.filename.subscribe(self._onFilename, init=True)

        # For acquisition
        # a ProgressiveFuture if the acquisition is going on
        self.btn_acquire = self._main_frame.btn_sparc_acquire
        self.btn_change_file = self._main_frame.btn_sparc_change_file
        self.btn_cancel = self._main_frame.btn_sparc_cancel
        self.acq_future = None
        self.gauge_acq = self._main_frame.gauge_sparc_acq
        self.lbl_acqestimate = self._main_frame.lbl_sparc_acq_estimate
        self._acq_future_connector = None

        # TODO: share an executor with the whole GUI.
        self._executor = futures.ThreadPoolExecutor(max_workers=2)

        # Link buttons
        self.btn_acquire.Bind(wx.EVT_BUTTON, self.on_acquisition)
        self.btn_change_file.Bind(wx.EVT_BUTTON, self.on_change_file)
        self.btn_cancel.Bind(wx.EVT_BUTTON, self.on_cancel)

        self.gauge_acq.Hide()
        self._main_frame.Layout()

        # TODO: we need to be informed if the user closes suddenly the window
        # self.Bind(wx.EVT_CLOSE, self.on_close)

        # Event binding
        pub.subscribe(self.on_setting_change, 'setting.changed')
        # TODO We should also listen to repetition, in case it's modified after we've
        # received the new ROI. Or maybe always compute acquisition time a bit delayed?
        for va in vas:
            va.subscribe(self.onAnyVA)
        roa.subscribe(self.onROA, init=True)
示例#3
0
文件: cli_rgb.py 项目: delmic/odemis
 def _get_new_filename(self):
     conf = get_acqui_conf()
     # Use TIFF by default, as it's a little bit more user-friendly for simple
     # coloured images.
     return os.path.join(
         conf.last_path,
         u"%s%s" % (time.strftime("%Y%m%d-%H%M%S"), ".tiff")
     )
示例#4
0
    def __init__(self, tab_data, tab_panel, streambar_controller):
        """
        tab_data (MicroscopyGUIData): the representation of the microscope GUI
        tab_panel: (wx.Frame): the frame which contains the 4 viewports
        stream_ctrl (StreamBarController): controller to pause/resume the streams
        """
        self._tab_data_model = tab_data
        self._main_data_model = tab_data.main
        self._tab_panel = tab_panel
        self._streambar_controller = streambar_controller

        # For file selection
        self.conf = conf.get_acqui_conf()

        # TODO: this should be the date at which the user presses the acquire
        # button (or when the last settings were changed)!
        # At least, we must ensure it's a new date after the acquisition
        # is done.
        # Filename to save the acquisition
        self.filename = model.StringVA(self._get_default_filename())
        self.filename.subscribe(self._onFilename, init=True)

        # For acquisition
        # a ProgressiveFuture if the acquisition is going on
        self.btn_acquire = self._tab_panel.btn_sparc_acquire
        self.btn_change_file = self._tab_panel.btn_sparc_change_file
        self.btn_cancel = self._tab_panel.btn_sparc_cancel
        self.acq_future = None
        self.gauge_acq = self._tab_panel.gauge_sparc_acq
        self.lbl_acqestimate = self._tab_panel.lbl_sparc_acq_estimate
        self._acq_future_connector = None

        self._stream_paused = ()

        # TODO: share an executor with the whole GUI.
        self._executor = futures.ThreadPoolExecutor(max_workers=2)

        # Link buttons
        self.btn_acquire.Bind(wx.EVT_BUTTON, self.on_acquisition)
        self.btn_change_file.Bind(wx.EVT_BUTTON, self.on_change_file)
        self.btn_cancel.Bind(wx.EVT_BUTTON, self.on_cancel)

        self.gauge_acq.Hide()
        self._tab_panel.Parent.Layout()

        # TODO: we need to be informed if the user closes suddenly the window
        # self.Bind(wx.EVT_CLOSE, self.on_close)

        # Listen to change of streams to update the acquisition time
        self._prev_streams = set() # set of streams already listened to
        tab_data.streams.subscribe(self._onStreams, init=True)
        # also listen to .semStream, which is not in .streams
        for va in self._get_settings_vas(tab_data.semStream):
            va.subscribe(self._onAnyVA)

        self._roa = tab_data.semStream.roi
        self._roa.subscribe(self._onROA, init=True)
示例#5
0
    def _get_snapshot_info(self, dialog=False):
        config = conf.get_acqui_conf()

        tab, filepath, exporter = self._main_data_model.tab.value, None, None

        if dialog:
            format_info = get_available_formats()
            wildcards, formats = formats_to_wildcards(format_info)
            # The default file name should be empty because otherwise the
            # dialog will add an extension that won't change when the user
            # selects a different file type in the dialog.
            dlg = wx.FileDialog(self._main_frame,
                                "Save Snapshot",
                                config.last_path,
                                "",
                                wildcard=wildcards,
                                style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)

            # Select the last format used
            try:
                idx = formats.index(config.last_format)
            except ValueError:
                idx = 0
            dlg.SetFilterIndex(idx)

            if dlg.ShowModal() == wx.ID_OK:
                path = dlg.GetPath()
                fmt = formats[dlg.GetFilterIndex()]
                extension = format_info[fmt][0]

                # Prevent double extensions when an old file is selected
                filepath, _ = os.path.splitext(path)
                filepath = filepath + extension

                config.last_path = os.path.dirname(path)
                config.last_format = fmt
                config.last_extension = extension
                config.write()
                exporter = dataio.get_exporter(config.last_format)

            dlg.Destroy()
        else:
            extension = config.last_extension
            dirname = get_picture_folder()
            basename = time.strftime("%Y%m%d-%H%M%S", time.localtime())
            filepath = os.path.join(dirname, basename + extension)
            exporter = dataio.get_exporter(config.last_format)

            if os.path.exists(filepath):
                msg = "File '%s' already exists, cancelling snapshot"
                logging.warning(msg, filepath)
                tab, filepath, exporter = None, None, None

        return tab, filepath, exporter
示例#6
0
文件: settings.py 项目: delmic/odemis
    def add_file_btn(self, label, value=None, tooltip=None, clear=None, style=wx.FD_OPEN):
        config = guiconf.get_acqui_conf()
        lbl_ctrl, value_ctrl = self.panel.add_file_button(label, value or config.last_path, clear, style)

        if tooltip is not None:
            lbl_ctrl.SetToolTipString(tooltip)
            value_ctrl.SetToolTipString(tooltip)

        # Add the corresponding setting entry
        ne = SettingEntry(name=label, lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl)
        self.entries.append(ne)
        return ne
示例#7
0
文件: sracq.py 项目: delmic/odemis
    def _on_filename(self, fn):
        # Make the name "fn" -> "fn-XXXXXX.ext"
        bn, ext = os.path.splitext(fn)
        self._fntmpl = bn + "-%06d" + ext
        if not ext.endswith(".tiff"):
            logging.warning("Only TIFF format is recommended to use")

        # Store the directory so that next filename is in the same place
        conf = get_acqui_conf()
        p, bn = os.path.split(fn)
        if p:
            conf.last_path = p
示例#8
0
    def open_image(self, dlg):
        tab = self.main_app.main_data.getTabByName("analysis")
        tab_data = tab.tab_data_model
        fi = tab_data.acq_fileinfo.value

        if fi and fi.file_name:
            path, _ = os.path.split(fi.file_name)
        else:
            config = get_acqui_conf()
            path = config.last_path

        # Find the available formats (and corresponding extensions)
        formats_to_ext = dataio.get_available_formats(os.O_RDONLY)
        wildcards, formats = guiutil.formats_to_wildcards(formats_to_ext, include_all=True)
        dialog = wx.FileDialog(dlg,
                               message="Choose a file to load",
                               defaultDir=path,
                               defaultFile="",
                               style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST,
                               wildcard=wildcards)

        # Show the dialog and check whether is was accepted or cancelled
        if dialog.ShowModal() != wx.ID_OK:
            return None

        # Detect the format to use
        filename = dialog.GetPath()

        data = udataio.open_acquisition(filename)[0]
        try:
            data = self._ensureGrayscale(data)
        except ValueError as ex:
            box = wx.MessageDialog(dlg, str(ex), "Failed to open image",
                                   wx.OK | wx.ICON_STOP)
            box.ShowModal()
            box.Destroy()
            return None

        self.crop_top.range = (0, data.shape[0] // 2)
        self.crop_bottom.range = (0, data.shape[0] // 2)
        self.crop_left.range = (0, data.shape[1] // 2)
        self.crop_right.range = (0, data.shape[1] // 2)

        data.metadata[model.MD_POS] = (0, 0)
        data.metadata[model.MD_PIXEL_SIZE] = (1e-9, 1e-9)

        basename = os.path.splitext(os.path.split(filename)[1])[0]
        return stream.StaticSEMStream(basename, data)
示例#9
0
    def _get_snapshot_info(self, dialog=False):
        config = conf.get_acqui_conf()

        tab, filepath, exporter = self._main_data_model.tab.value, None, None

        extension = config.last_extension
        basename = time.strftime("%Y%m%d-%H%M%S", time.localtime())
        if dialog:
            filepath = os.path.join(config.last_path, basename + extension)
            # filepath will be None if cancelled by user
            filepath = ShowAcquisitionFileDialog(self._main_frame, filepath)
        else:
            dirname = get_picture_folder()
            filepath = os.path.join(dirname, basename + extension)

            if os.path.exists(filepath):
                msg = "File '%s' already exists, cancelling snapshot"
                logging.warning(msg, filepath)
                tab, filepath = None, None

        exporter = dataio.get_converter(config.last_format)

        return tab, filepath, exporter
示例#10
0
文件: export.py 项目: delmic/odemis
    def __init__(self, tab_data, main_frame, tab_panel, viewports):
        """
        tab_data: MicroscopyGUIData -- the representation of the microscope GUI
        main_frame: (wx.Frame): the whole GUI frame
        """

        self._data_model = tab_data
        self._main_data_model = tab_data.main
        self._main_frame = main_frame
        self._tab_panel = tab_panel
        self._conf = get_acqui_conf()

        # Listen to "acquire image" button
        self._tab_panel.btn_secom_export.Bind(wx.EVT_BUTTON, self.on_export)

        self._viewports = viewports.keys()

        self._main_frame.Bind(wx.EVT_MENU, self.on_export, id=self._main_frame.menu_item_export_as.GetId())

        self._main_frame.menu_item_export_as.Enable(False)

        # subscribe to get notified about tab changes
        self._prev_streams = None # To unsubscribe afterwards
        self._main_data_model.tab.subscribe(self.on_tab_change, init=True)
示例#11
0
文件: quickcl.py 项目: delmic/odemis
    def __init__(self, microscope, main_app):
        super(QuickCLPlugin, self).__init__(microscope, main_app)
        # Can only be used with a SPARC with CL detector (or monochromator)
        if not microscope:
            return
        main_data = self.main_app.main_data
        if not main_data.ebeam or not (main_data.cld or main_data.monochromator):
            return

        self.conf = get_acqui_conf()
        self.filename = model.StringVA("")
        self.filename.subscribe(self._on_filename)

        self.expectedDuration = model.VigilantAttribute(1, unit="s", readonly=True)

        self.hasDatabar = model.BooleanVA(False)

        # Only put the VAs that do directly define the image as local, everything
        # else should be global. The advantage is double: the global VAs will
        # set the hardware even if another stream (also using the e-beam) is
        # currently playing, and if the VAs are changed externally, the settings
        # will be displayed correctly (and not reset the values on next play).
        emtvas = set()
        hwemtvas = set()
        for vaname in get_local_vas(main_data.ebeam, main_data.hw_settings_config):
            if vaname in ("resolution", "dwellTime", "scale"):
                emtvas.add(vaname)
            else:
                hwemtvas.add(vaname)

        self._sem_stream = stream.SEMStream(
            "Secondary electrons",
            main_data.sed,
            main_data.sed.data,
            main_data.ebeam,
            focuser=main_data.ebeam_focus,
            hwemtvas=hwemtvas,
            hwdetvas=None,
            emtvas=emtvas,
            detvas=get_local_vas(main_data.sed, main_data.hw_settings_config),
        )

        # This stream is used both for rendering and acquisition.
        # LiveCLStream is more or less like a SEMStream, but ensures the icon in
        # the merge slider is correct, and provide a few extra.
        if main_data.cld:
            self._cl_stream = LiveCLStream(
                "CL intensity",
                main_data.cld,
                main_data.cld.data,
                main_data.ebeam,
                focuser=main_data.ebeam_focus,
                emtvas=emtvas,
                detvas=get_local_vas(main_data.cld, main_data.hw_settings_config),
                opm=main_data.opm,
            )
            # TODO: allow to type in the resolution of the CL?
            # TODO: add the cl-filter axis (or reset it to pass-through?)
            self.logScale = self._cl_stream.logScale

            if hasattr(self._cl_stream, "detGain"):
                self._cl_stream.detGain.subscribe(self._on_cl_gain)

            # Update the acquisition time when it might change (ie, the scan settings
            # change)
            self._cl_stream.emtDwellTime.subscribe(self._update_exp_dur)
            self._cl_stream.emtResolution.subscribe(self._update_exp_dur)

        # Note: for now we don't really support SPARC with BOTH CL-detector and
        # monochromator.
        if main_data.monochromator:
            self._mn_stream = LiveCLStream(
                "Monochromator",
                main_data.monochromator,
                main_data.monochromator.data,
                main_data.ebeam,
                focuser=main_data.ebeam_focus,
                emtvas=emtvas,
                detvas=get_local_vas(main_data.monochromator, main_data.hw_settings_config),
                opm=main_data.opm,
            )
            self._mn_stream.emtDwellTime.subscribe(self._update_exp_dur)
            self._mn_stream.emtResolution.subscribe(self._update_exp_dur)

            # spg = self._getAffectingSpectrograph(main_data.spectrometer)
            # TODO: show axes

        self._dlg = None

        self.addMenu("Acquisition/Quick CL...\tF2", self.start)
示例#12
0
文件: util.py 项目: pieleric/odemis
def create_setting_entry(container, name, va, hw_comp, conf=None, change_callback=None):
    """ Determine what type on control to use for a setting and have the container create it

    Args:
        container (SettingsController or StreamController): Controller in charge of the settings
        name (str): Name of the setting
        va (VigilantAttribute): The va containing the value of the setting
        hw_comp (Component): The hardware component to which the setting belongs
        conf ({} or None): The optional configuration options for the control
        change_callback (callable): Callable to bind to the control's change event

    Returns:
        SettingEntry

    """

    value_ctrl = lbl_ctrl = setting_entry = None

    # If no conf provided, set it to an empty dictionary
    conf = conf or {}

    # Get the range and choices
    min_val, max_val, choices, unit = process_setting_metadata(hw_comp, va, conf)
    # Format the provided choices
    choices_formatted, choices_si_prefix = format_choices(choices)
    # Determine the control type to use, either from config or some 'smart' default
    control_type = determine_control_type(hw_comp, va, choices_formatted, conf)

    # Special case, early stop
    if control_type == odemis.gui.CONTROL_NONE:
        # No value, not even a label, just an empty entry, so that the settings are saved
        # during acquisition
        return SettingEntry(name=name, va=va, hw_comp=hw_comp)

    # Format label
    label_text = conf.get('label', label_to_human(name))
    tooltip = conf.get('tooltip', "")

    logging.debug("Adding VA %s", label_text)
    # Create the needed wxPython controls
    if control_type == odemis.gui.CONTROL_READONLY:
        val = va.value
        accuracy = conf.get('accuracy', 3)
        val_str = value_to_str(val, unit, accuracy, pretty_time=True)
        lbl_ctrl, value_ctrl = container.add_readonly_field(label_text, val_str)

        def set_ctrl(value, ctrl=value_ctrl, u=unit, acc=accuracy):
            ctrl.SetValue(value_to_str(value, u, acc, pretty_time=True))

        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     va_2_ctrl=set_ctrl)

    elif control_type == odemis.gui.CONTROL_TEXT:
        val = va.value
        accuracy = conf.get('accuracy', 3)
        val_str = value_to_str(val, unit, accuracy)  # No pretty_time, as we don't support reading it back
        lbl_ctrl, value_ctrl = container.add_text_field(label_text, val_str)

        # To set the value on the control (when the VA is updated)
        def set_ctrl(value, ctrl=value_ctrl, u=unit, acc=accuracy):
            ctrl.SetValue(value_to_str(value, u, acc))

        # To retrieve the actual value, which may contain prefix and unit
        def text_get(ctrl=value_ctrl, va=va):
            ctrl_value = ctrl.GetValue()
            va_val = va.value
            try:
                new_val = str_to_value(ctrl_value, va)
            except (ValueError, TypeError):
                logging.warning("Value %s couldn't be understood", ctrl_value, exc_info=True)
                new_val = va_val  # To force going back to last value

            # if it ends up being the same value as before the VA will
            # not update, so force reformatting it
            if va_val == new_val:
                set_ctrl(va_val)
            return new_val

        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     va_2_ctrl=set_ctrl, ctrl_2_va=text_get,
                                     events=wx.EVT_TEXT_ENTER)

        if change_callback:
            value_ctrl.Bind(wx.EVT_TEXT_ENTER, change_callback)

    elif control_type in (odemis.gui.CONTROL_SAVE_FILE, odemis.gui.CONTROL_OPEN_FILE):
        val = va.value
        if not val:
            config = guiconf.get_acqui_conf()
            val = config.last_path

        if control_type == odemis.gui.CONTROL_SAVE_FILE:
            dialog_style = wx.FD_SAVE
        else:  # odemis.gui.CONTROL_OPEN_FILE
            dialog_style = wx.FD_OPEN

        clearlabel = conf.get('clearlabel')  # Text to show when no filename (+ allow to clear the filename)
        wildcard = conf.get('wildcard', "*.*")  # File extension wildcard string
        lbl_ctrl, value_ctrl = container.add_file_button(label_text,
                                                         val,
                                                         clearlabel,
                                                         wildcard=wildcard,
                                                         dialog_style=dialog_style)

        # Add the corresponding setting entry
        setting_entry = SettingEntry(name=label_text, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     events=EVT_FILE_SELECT)

    elif control_type == odemis.gui.CONTROL_SLIDER:
        # The slider is accompanied by an extra number text field

        if "type" in conf:
            if conf["type"] == "integer":
                # add_integer_slider
                factory = container.add_integer_slider
            elif conf["type"] == "slider":
                factory = container.add_slider
            else:
                factory = container.add_float_slider
        else:
            # guess from value(s)
            known_values = [va.value, min_val, max_val]
            if choices is not None:
                known_values.extend(list(choices))
            if any(isinstance(v, float) for v in known_values):
                factory = container.add_float_slider
            else:
                factory = container.add_integer_slider

        # The event configuration determines what event will signal that the
        # setting entry has changed value.
        update_event = conf.get("event", wx.EVT_SLIDER)
        if update_event.typeId not in (wx.EVT_SCROLL_CHANGED.typeId, wx.EVT_SLIDER.typeId):
            raise ValueError("Illegal event type %d for Slider setting entry!" % (update_event.typeId,))

        ctrl_conf = {
            'min_val': min_val,
            'max_val': max_val,
            'scale': conf.get('scale', None),
            'unit': unit,
            'accuracy': conf.get('accuracy', 4),
        }

        lbl_ctrl, value_ctrl = factory(label_text, va.value, ctrl_conf)
        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     events=update_event)

        if change_callback:
            if not isinstance(update_event, collections.Iterable):
                update_event = (update_event,)
            for event in update_event:
                value_ctrl.Bind(event, change_callback)

    elif control_type == odemis.gui.CONTROL_INT:
        if unit == "":  # don't display unit prefix if no unit
            unit = None

        ctrl_conf = {
            'min_val': min_val,
            'max_val': max_val,
            'unit': unit,
            'choices': choices,
        }

        if 'key_step' in conf:
            ctrl_conf['key_step'] = conf['key_step']
        if 'key_step_min' in conf:
            ctrl_conf['key_step_min'] = conf['key_step_min']

        lbl_ctrl, value_ctrl = container.add_int_field(label_text, conf=ctrl_conf)

        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     events=wx.EVT_COMMAND_ENTER)

        if change_callback:
            value_ctrl.Bind(wx.EVT_COMMAND_ENTER, change_callback)

    elif control_type == odemis.gui.CONTROL_FLT:
        if unit == "":  # don't display unit prefix if no unit
            unit = None

        ctrl_conf = {
            'min_val': min_val,
            'max_val': max_val,
            'unit': unit,
            'choices': choices,
            'accuracy': conf.get('accuracy', 5),
        }

        if 'key_step' in conf:
            ctrl_conf['key_step'] = conf['key_step']
        if 'key_step_min' in conf:
            ctrl_conf['key_step_min'] = conf['key_step_min']

        lbl_ctrl, value_ctrl = container.add_float_field(label_text, conf=ctrl_conf)

        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     events=wx.EVT_COMMAND_ENTER)

        if change_callback:
            value_ctrl.Bind(wx.EVT_COMMAND_ENTER, change_callback)

    elif control_type == odemis.gui.CONTROL_CHECK:
        # Only supports boolean VAs

        lbl_ctrl, value_ctrl = container.add_checkbox_control(label_text, value=va.value)

        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     events=wx.EVT_CHECKBOX)

        if change_callback:
            value_ctrl.Bind(wx.EVT_CHECKBOX, change_callback)

    elif control_type == odemis.gui.CONTROL_RADIO:
        unit_fmt = (choices_si_prefix or "") + (unit or "")

        ctrl_conf = {
            'size': (-1, 16),
            'units': unit_fmt,
            'choices': [v for v, _ in choices_formatted],
            'labels': [l for _, l in choices_formatted],
        }

        lbl_ctrl, value_ctrl = container.add_radio_control(label_text, conf=ctrl_conf)

        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     events=wx.EVT_BUTTON)

        if change_callback:
            value_ctrl.Bind(wx.EVT_BUTTON, change_callback)

    elif control_type == odemis.gui.CONTROL_COMBO:

        accuracy = conf.get('accuracy', 3)

        # TODO: Might need size=(100, 16)!!
        cbconf = {}
        if hasattr(va, "choices") and isinstance(va.choices, collections.Iterable):
            # Enumerated VA => don't allow entering other values
            cbconf["style"] = wx.CB_READONLY
        lbl_ctrl, value_ctrl = container.add_combobox_control(label_text, conf=cbconf)

        # Set choices
        if choices_si_prefix:
            for choice, formatted in choices_formatted:
                value_ctrl.Append(u"%s %s" % (formatted, choices_si_prefix + unit), choice)
        else:
            for choice, formatted in choices_formatted:
                value_ctrl.Append(u"%s%s" % (formatted, unit), choice)

        # A small wrapper function makes sure that the value can
        # be set by passing the actual value (As opposed to the text label)
        def cb_set(value, va=va, ctrl=value_ctrl, u=unit, acc=accuracy):
            # Re-read the value from the VA because it'll be called via
            # CallAfter(), and if the value is changed multiple times, it might
            # not be in chronological order.
            value = va.value
            for i in range(ctrl.GetCount()):
                d = ctrl.GetClientData(i)
                if (d == value or
                    (all(isinstance(v, float) for v in (value, d)) and
                     util.almost_equal(d, value))
                   ):
                    logging.debug("Setting combobox value to %s", ctrl.Items[i])
                    ctrl.SetSelection(i)
                    break
            else:
                logging.debug("No existing label found for value %s in combobox ctrl %d",
                              value, id(ctrl))
                # entering value as free text
                txt = value_to_str(value, u, acc)
                ctrl.SetValue(txt)

        # equivalent wrapper function to retrieve the actual value
        def cb_get(ctrl=value_ctrl, va=va, u=unit):
            ctrl_value = ctrl.GetValue()
            # Try to use the predefined value if it's available
            i = ctrl.GetSelection()

            # Warning: if the text contains an unknown value, GetSelection will
            # not return wx.NOT_FOUND (as expected), but the last selection value
            if i != wx.NOT_FOUND and ctrl.Items[i] == ctrl_value:
                logging.debug("Getting item value %s from combobox control",
                              ctrl.GetClientData(i))
                return ctrl.GetClientData(i)
            else:
                va_val = va.value
                try:
                    new_val = str_to_value(ctrl_value, va)
                except (ValueError, TypeError):
                    logging.warning("Value %s couldn't be understood", ctrl_value, exc_info=True)
                    new_val = va_val  # To force going back to last value

                # if it ends up being the same value as before the combobox will
                # not update, so force it now
                if va_val == new_val:
                    cb_set(va_val)
                return new_val

        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     va_2_ctrl=cb_set, ctrl_2_va=cb_get,
                                     events=(wx.EVT_COMBOBOX, wx.EVT_TEXT_ENTER))
        if change_callback:
            value_ctrl.Bind(wx.EVT_COMBOBOX, change_callback)
            value_ctrl.Bind(wx.EVT_TEXT_ENTER, change_callback)

    else:
        logging.error("Unknown control type %s", control_type)

    value_ctrl.SetToolTip(tooltip)
    lbl_ctrl.SetToolTip(tooltip)

    return setting_entry
示例#13
0
 def _get_new_filename(self):
     conf = get_acqui_conf()
     return os.path.join(
         conf.last_path,
         u"%s%s" % (time.strftime("sr-%Y%m%d-%H%M%S"), ".tiff"))
示例#14
0
    def __init__(self, parent, orig_tab_data):
        xrcfr_acq.__init__(self, parent)

        self.conf = get_acqui_conf()

        for n in presets:
            self.cmb_presets.Append(n)
        # TODO: record and reuse the preset used?
        self.cmb_presets.Select(0)

        self.filename = model.StringVA(create_filename(self.conf.last_path, self.conf.fn_ptn,
                                                       self.conf.last_extension, self.conf.fn_count))
        self.filename.subscribe(self._onFilename, init=True)

        # The name of the last file that got written to disk (used for auto viewing on close)
        self.last_saved_file = None
        
        # True when acquisition occurs
        self.acquiring = False

        # 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)

        # Create a new settings controller for the acquisition dialog
        self._settings_controller = SecomSettingsController(
            self,
            self._tab_data_model,
            highlight_change=True  # also adds a "Reset" context menu
        )

        orig_view = orig_tab_data.focussedView.value
        self._view = self._tab_data_model.focussedView.value
        self._hidden_view = StreamView("Plugin View Hidden")

        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_all_streams()

        # The list of streams ready for acquisition (just used as a cache)
        self._acq_streams = {}

        # FIXME: pass the fold_panels

        # Compute the preset values for each preset
        self._preset_values = {}  # dict string -> dict (SettingEntries -> value)
        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._orig_settings = preset_as_is(self._orig_entries)  # to detect changes
        for n, preset in presets.items():
            self._preset_values[n] = preset(self._orig_entries)
        # Presets which have been confirmed on the hardware
        self._presets_confirmed = set() # (string)

        self.start_listening_to_va()

        # If it could be possible to do fine alignment, allow the user to choose
        if self._can_fine_align(self._tab_data_model.streams.value):
            self.chkbox_fine_align.Show()
            # Set to True to make it the default, but will be automatically
            # disabled later if the current visible streams don't allow it.
            self.chkbox_fine_align.Value = True

            for s in self._tab_data_model.streams.value:
                if isinstance(s, EMStream):
                    em_det = s.detector
                    em_emt = s.emitter
                elif isinstance(s, OpticalStream) and not isinstance(s, ScannedFluoStream):
                    opt_det = s.detector
            self._ovrl_stream = stream.OverlayStream("Fine alignment", opt_det, em_emt, em_det,
                                                     opm=self._main_data_model.opm)
            self._ovrl_stream.dwellTime.value = self._main_data_model.fineAlignDwellTime.value
        else:
            self.chkbox_fine_align.Show(False)
            self.chkbox_fine_align.Value = False

        self._prev_fine_align = self.chkbox_fine_align.Value

        # 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)

        # The TOOL_ROA is not present because we don't allow the user to change
        # the ROA), so we need to explicitly request the canvas to show the ROA.
        if hasattr(self._tab_data_model, "roa") and self._tab_data_model.roa is not None:
            cnvs = self.pnl_view_acq.canvas
            self.roa_overlay = RepetitionSelectOverlay(cnvs, self._tab_data_model.roa,
                                                             self._tab_data_model.fovComp)
            cnvs.add_world_overlay(self.roa_overlay)

        self.Bind(wx.EVT_CHAR_HOOK, self.on_key)

        self.btn_cancel.Bind(wx.EVT_BUTTON, self.on_close)
        self.btn_change_file.Bind(wx.EVT_BUTTON, self.on_change_file)
        self.btn_secom_acquire.Bind(wx.EVT_BUTTON, self.on_acquire)
        self.cmb_presets.Bind(wx.EVT_COMBOBOX, self.on_preset)
        self.Bind(wx.EVT_CLOSE, self.on_close)
        # on_streams_changed is compatible because it doesn't use the args
        self.chkbox_fine_align.Bind(wx.EVT_CHECKBOX, self.on_streams_changed)

        self.on_preset(None) # will force setting the current preset

        # To update the estimated time when streams are removed/added
        self._view.stream_tree.flat.subscribe(self.on_streams_changed)
        self._hidden_view.stream_tree.flat.subscribe(self.on_streams_changed)
示例#15
0
文件: zstack.py 项目: lanery/odemis
 def _get_new_filename(self):
     conf = get_acqui_conf()
     return os.path.join(
         conf.last_path,
         u"%s%s" % (time.strftime("%Y%m%d-%H%M%S"), conf.last_extension))
示例#16
0
文件: views.py 项目: Sabrina85/odemis
    def __init__(self, main_data, tab, overview_canvas, m_view, stream_bar):
        self.main_data = main_data
        self._tab = tab
        self._data_model = tab.tab_data_model
        self.canvas = overview_canvas
        self.m_view = m_view
        self._stream_bar = stream_bar
        self.conf = get_acqui_conf()

        self.curr_s = None

        # Timer to detect when the stage ends moving
        self._timer_pos = wx.PyTimer(self.add_pos_to_history)

        if hasattr(m_view, "merge_ratio"):
            m_view.merge_ratio.subscribe(self._on_merge_ratio_change)


        # Global overview image (Delphi)
        if main_data.overview_ccd:
            # Overview camera can be RGB => in that case len(shape) == 4
            if len(main_data.overview_ccd.shape) == 4:
                overview_stream = acqstream.RGBCameraStream("Overview", main_data.overview_ccd,
                                                            main_data.overview_ccd.data, None,
                                                            acq_type=MD_AT_OVV_FULL)
            else:
                overview_stream = acqstream.BrightfieldStream("Overview", main_data.overview_ccd,
                                                              main_data.overview_ccd.data, None,
                                                              acq_type=MD_AT_OVV_FULL)
            self.m_view.addStream(overview_stream)
            # TODO: add it to self.tab_data_model.streams?
        else:
            # black image to display history overlay separately from built-up ovv image
            # controlled by merge slider
            da, _ = self._initialize_ovv_im(OVV_SHAPE)
            history_stream = acqstream.RGBUpdatableStream("History Stream", da, acq_type=MD_AT_HISTORY)
            self.m_view.addStream(history_stream)

        # Built-up overview image
        self.ovv_im, self.m_view.mpp.value = self._initialize_ovv_im(OVV_SHAPE)
        logging.debug("Overview image FoV: %s", getBoundingBox(self.ovv_im))

        # Initialize individual ovv images for optical and sem stream
        self.im_opt = copy.deepcopy(self.ovv_im)
        self.im_sem = copy.deepcopy(self.ovv_im)
        # Extra images to be used for complete overviews, shown behind the build-up images
        self._bkg_opt = copy.deepcopy(self.ovv_im)
        self._bkg_sem = copy.deepcopy(self.ovv_im)

        # Add stream to view
        self.upd_stream = acqstream.RGBUpdatableStream("Overview Stream", self.ovv_im,
                                                       acq_type=MD_AT_OVV_TILES)
        self.m_view.addStream(self.upd_stream)

        self._data_model.focussedView.subscribe(self._on_focused_view)

        if main_data.stage:
            # Update the image when the stage move
            main_data.stage.position.subscribe(self.on_stage_pos_change, init=True)
            main_data.chamberState.subscribe(self._on_chamber_state)
            self._data_model.streams.subscribe(self._on_current_stream)

            # Add a "acquire overview" button.
            self._stream_bar.btn_add_overview.Bind(wx.EVT_BUTTON, self._on_overview_acquire)
            self._acquisition_controller = acqcont.OverviewStreamAcquiController(self._data_model, tab)
            self._bkg_ovv_subs = {}  # Just used temporarily when background overview is projected
示例#17
0
    def __init__(self, parent, orig_tab_data):
        xrcfr_acq.__init__(self, parent)

        self.conf = get_acqui_conf()

        for n in presets:
            self.cmb_presets.Append(n)
        # TODO: record and reuse the preset used?
        self.cmb_presets.Select(0)

        self.filename = model.StringVA(
            create_filename(self.conf.last_path, self.conf.fn_ptn,
                            self.conf.last_extension, self.conf.fn_count))
        self.filename.subscribe(self._onFilename, init=True)

        # The name of the last file that got written to disk (used for auto viewing on close)
        self.last_saved_file = None

        # True when acquisition occurs
        self.acquiring = False

        # 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)

        # Create a new settings controller for the acquisition dialog
        self._settings_controller = SecomSettingsController(
            self,
            self._tab_data_model,
            highlight_change=True  # also adds a "Reset" context menu
        )

        orig_view = orig_tab_data.focussedView.value
        self._view = self._tab_data_model.focussedView.value
        self._hidden_view = StreamView("Plugin View Hidden")

        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_all_streams()

        # The list of streams ready for acquisition (just used as a cache)
        self._acq_streams = {}

        # FIXME: pass the fold_panels

        # Compute the preset values for each preset
        self._preset_values = {
        }  # dict string -> dict (SettingEntries -> value)
        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._orig_settings = preset_as_is(
            self._orig_entries)  # to detect changes
        for n, preset in presets.items():
            self._preset_values[n] = preset(self._orig_entries)
        # Presets which have been confirmed on the hardware
        self._presets_confirmed = set()  # (string)

        self.start_listening_to_va()

        # If it could be possible to do fine alignment, allow the user to choose
        if self._can_fine_align(self._tab_data_model.streams.value):
            self.chkbox_fine_align.Show()
            # Set to True to make it the default, but will be automatically
            # disabled later if the current visible streams don't allow it.
            self.chkbox_fine_align.Value = True

            for s in self._tab_data_model.streams.value:
                if isinstance(s, EMStream):
                    em_det = s.detector
                    em_emt = s.emitter
                elif isinstance(s, OpticalStream) and not isinstance(
                        s, ScannedFluoStream):
                    opt_det = s.detector
            self._ovrl_stream = stream.OverlayStream(
                "Fine alignment",
                opt_det,
                em_emt,
                em_det,
                opm=self._main_data_model.opm)
            self._ovrl_stream.dwellTime.value = self._main_data_model.fineAlignDwellTime.value
        else:
            self.chkbox_fine_align.Show(False)
            self.chkbox_fine_align.Value = False

        self._prev_fine_align = self.chkbox_fine_align.Value

        # 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)

        # The TOOL_ROA is not present because we don't allow the user to change
        # the ROA), so we need to explicitly request the canvas to show the ROA.
        if hasattr(self._tab_data_model,
                   "roa") and self._tab_data_model.roa is not None:
            cnvs = self.pnl_view_acq.canvas
            self.roa_overlay = RepetitionSelectOverlay(
                cnvs, self._tab_data_model.roa, self._tab_data_model.fovComp)
            cnvs.add_world_overlay(self.roa_overlay)

        self.Bind(wx.EVT_CHAR_HOOK, self.on_key)

        self.btn_cancel.Bind(wx.EVT_BUTTON, self.on_close)
        self.btn_change_file.Bind(wx.EVT_BUTTON, self.on_change_file)
        self.btn_secom_acquire.Bind(wx.EVT_BUTTON, self.on_acquire)
        self.cmb_presets.Bind(wx.EVT_COMBOBOX, self.on_preset)
        self.Bind(wx.EVT_CLOSE, self.on_close)
        # on_streams_changed is compatible because it doesn't use the args
        self.chkbox_fine_align.Bind(wx.EVT_CHECKBOX, self.on_streams_changed)

        self.on_preset(None)  # will force setting the current preset

        # To update the estimated time when streams are removed/added
        self._view.stream_tree.flat.subscribe(self.on_streams_changed)
        self._hidden_view.stream_tree.flat.subscribe(self.on_streams_changed)
示例#18
0
    def __init__(self, tab_data, tab_panel, streambar_controller):
        """
        tab_data (MicroscopyGUIData): the representation of the microscope GUI
        tab_panel: (wx.Frame): the frame which contains the 4 viewports
        stream_ctrl (StreamBarController): controller to pause/resume the streams
        """
        self._tab_data_model = tab_data
        self._main_data_model = tab_data.main
        self._tab_panel = tab_panel
        self._streambar_controller = streambar_controller

        # For file selection
        self.conf = conf.get_acqui_conf()

        # TODO: this should be the date at which the user presses the acquire
        # button (or when the last settings were changed)!
        # At least, we must ensure it's a new date after the acquisition
        # is done.
        # Filename to save the acquisition
        self.filename = model.StringVA(create_filename(self.conf.last_path, self.conf.fn_ptn,
                                                       self.conf.last_extension, self.conf.fn_count))
        self.filename.subscribe(self._onFilename, init=True)

        # For acquisition
        # a ProgressiveFuture if the acquisition is going on
        self.btn_acquire = self._tab_panel.btn_sparc_acquire
        self.btn_change_file = self._tab_panel.btn_sparc_change_file
        self.btn_cancel = self._tab_panel.btn_sparc_cancel
        self.acq_future = None
        self.gauge_acq = self._tab_panel.gauge_sparc_acq
        self.lbl_acqestimate = self._tab_panel.lbl_sparc_acq_estimate
        self.bmp_acq_status_warn = self._tab_panel.bmp_acq_status_warn
        self.bmp_acq_status_info = self._tab_panel.bmp_acq_status_info
        self._acq_future_connector = None

        # TODO: share an executor with the whole GUI.
        self._executor = futures.ThreadPoolExecutor(max_workers=2)

        # Link buttons
        self.btn_acquire.Bind(wx.EVT_BUTTON, self.on_acquisition)
        self.btn_change_file.Bind(wx.EVT_BUTTON, self.on_change_file)
        self.btn_cancel.Bind(wx.EVT_BUTTON, self.on_cancel)

        self.gauge_acq.Hide()
        self._tab_panel.Parent.Layout()

        # Animator for messages containing ellipsis character
        self._ellipsis_animator = None

        # TODO: we need to be informed if the user closes suddenly the window
        # self.Bind(wx.EVT_CLOSE, self.on_close)

        self._roa = tab_data.semStream.roi

        # Listen to change of streams to update the acquisition time
        self._prev_streams = set() # set of streams already listened to
        tab_data.streams.subscribe(self._onStreams, init=True)
        # also listen to .semStream, which is not in .streams
        for va in self._get_settings_vas(tab_data.semStream):
            va.subscribe(self._onAnyVA)
        # Extra options affecting the acquisitions globally
        tab_data.pcdActive.subscribe(self._onAnyVA)
        # TODO: should also listen to the VAs of the leeches on semStream
        tab_data.useScanStage.subscribe(self._onAnyVA)

        self._roa.subscribe(self._onROA, init=True)

        # Listen to preparation state
        self._main_data_model.is_preparing.subscribe(self.on_preparation)
示例#19
0
    def __init__(self, parent, orig_tab_data):
        xrcfr_acq.__init__(self, parent)

        self.conf = get_acqui_conf()

        for n in presets:
            self.cmb_presets.Append(n)
        # TODO: record and reuse the preset used?
        self.cmb_presets.Select(0)

        self.filename = model.StringVA(self._get_default_filename())
        self.filename.subscribe(self._onFilename, init=True)

        # The name of the last file that got written to disk (used for auto viewing on close)
        self.last_saved_file = 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)

        # Create a new settings controller for the acquisition dialog
        self._settings_controller = SecomSettingsController(
            self,
            self._tab_data_model,
            highlight_change=True  # also adds a "Reset" context menu
        )

        # To turn on/off the fan
        self._orig_fan_speed = None

        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)
        # The streams currently displayed are the one visible
        self.add_all_streams()

        # FIXME: pass the fold_panels

        # Compute the preset values for each preset
        self._preset_values = {}  # dict string -> dict (SettingEntries -> value)
        orig_entries = get_global_settings_entries(self._settings_controller)
        for sc in self.streambar_controller.stream_controllers:
            orig_entries += get_local_settings_entries(sc)
        self._orig_settings = preset_as_is(orig_entries) # to detect changes
        for n, preset in presets.items():
            self._preset_values[n] = preset(orig_entries)
        # Presets which have been confirmed on the hardware
        self._presets_confirmed = set() # (string)

        # If it could be possible to do fine alignment, allow the user to choose
        if self._can_fine_align(self._tab_data_model.streams.value):
            self.chkbox_fine_align.Show()
            # Set to True to make it the default, but will be automatically
            # disabled later if the current visible streams don't allow it.
            self.chkbox_fine_align.Value = True

            for s in self._tab_data_model.streams.value:
                if isinstance(s, EMStream):
                    em_det = s.detector
                    em_emt = s.emitter
                elif isinstance(s, OpticalStream):
                    opt_det = s.detector
            self._ovrl_stream = stream.OverlayStream("Fine alignment", opt_det, em_emt, em_det)
            if self._main_data_model.role == "delphi":
                self._main_data_model.fineAlignDwellTime.value = 0.5
            self._ovrl_stream.dwellTime.value = self._main_data_model.fineAlignDwellTime.value
        else:
            self.chkbox_fine_align.Show(False)
            self.chkbox_fine_align.Value = False

        self._prev_fine_align = self.chkbox_fine_align.Value

        # 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_change_file.Bind(wx.EVT_BUTTON, self.on_change_file)
        self.btn_secom_acquire.Bind(wx.EVT_BUTTON, self.on_acquire)
        self.cmb_presets.Bind(wx.EVT_COMBOBOX, self.on_preset)
        self.Bind(wx.EVT_CLOSE, self.on_close)
        # on_streams_changed is compatible because it doesn't use the args
        self.chkbox_fine_align.Bind(wx.EVT_CHECKBOX, self.on_streams_changed)

        self.on_preset(None) # will force setting the current preset

        # TODO: use the presets VAs and subscribe to each of them, instead of
        # using pub/sub messages
        pub.subscribe(self.on_setting_change, 'setting.changed')

        # TODO: we should actually listen to the stream tree, but it's not
        # currently possible. => listen to .flat once it's there
        # Currently just use view.lastUpdate which should be "similar"
        # (but doesn't work if the stream contains no image)
        self._view.lastUpdate.subscribe(self.on_streams_changed)
示例#20
0
文件: sracq.py 项目: delmic/odemis
 def _get_new_filename(self):
     conf = get_acqui_conf()
     return os.path.join(
         conf.last_path,
         u"%s%s" % (time.strftime("sr-%Y%m%d-%H%M%S"), ".tiff")
     )
示例#21
0
    def __init__(self, microscope, main_app):
        super(QuickCLPlugin, self).__init__(microscope, main_app)
        # Can only be used with a SPARC with CL detector (or monochromator)
        if not microscope:
            return
        main_data = self.main_app.main_data
        if not main_data.ebeam or not (main_data.cld
                                       or main_data.monochromator):
            return

        self.conf = get_acqui_conf()
        self.filename = model.StringVA("")
        self.filename.subscribe(self._on_filename)

        self.expectedDuration = model.VigilantAttribute(1,
                                                        unit="s",
                                                        readonly=True)

        self.hasDatabar = model.BooleanVA(False)

        # Only put the VAs that do directly define the image as local, everything
        # else should be global. The advantage is double: the global VAs will
        # set the hardware even if another stream (also using the e-beam) is
        # currently playing, and if the VAs are changed externally, the settings
        # will be displayed correctly (and not reset the values on next play).
        emtvas = set()
        hwemtvas = set()
        for vaname in get_local_vas(main_data.ebeam,
                                    main_data.hw_settings_config):
            if vaname in ("resolution", "dwellTime", "scale"):
                emtvas.add(vaname)
            else:
                hwemtvas.add(vaname)

        self._sem_stream = stream.SEMStream(
            "Secondary electrons",
            main_data.sed,
            main_data.sed.data,
            main_data.ebeam,
            focuser=main_data.ebeam_focus,
            hwemtvas=hwemtvas,
            hwdetvas=None,
            emtvas=emtvas,
            detvas=get_local_vas(main_data.sed, main_data.hw_settings_config),
        )

        # This stream is used both for rendering and acquisition.
        # LiveCLStream is more or less like a SEMStream, but ensures the icon in
        # the merge slider is correct, and provide a few extra.
        if main_data.cld:
            self._cl_stream = LiveCLStream(
                "CL intensity",
                main_data.cld,
                main_data.cld.data,
                main_data.ebeam,
                focuser=main_data.ebeam_focus,
                emtvas=emtvas,
                detvas=get_local_vas(main_data.cld,
                                     main_data.hw_settings_config),
                opm=main_data.opm,
            )
            # TODO: allow to type in the resolution of the CL?
            # TODO: add the cl-filter axis (or reset it to pass-through?)
            self.logScale = self._cl_stream.logScale

            if hasattr(self._cl_stream, "detGain"):
                self._cl_stream.detGain.subscribe(self._on_cl_gain)

            # Update the acquisition time when it might change (ie, the scan settings
            # change)
            self._cl_stream.emtDwellTime.subscribe(self._update_exp_dur)
            self._cl_stream.emtResolution.subscribe(self._update_exp_dur)

        # Note: for now we don't really support SPARC with BOTH CL-detector and
        # monochromator.
        if main_data.monochromator:
            self._mn_stream = LiveCLStream(
                "Monochromator",
                main_data.monochromator,
                main_data.monochromator.data,
                main_data.ebeam,
                focuser=main_data.ebeam_focus,
                emtvas=emtvas,
                detvas=get_local_vas(main_data.monochromator,
                                     main_data.hw_settings_config),
                opm=main_data.opm,
            )
            self._mn_stream.emtDwellTime.subscribe(self._update_exp_dur)
            self._mn_stream.emtResolution.subscribe(self._update_exp_dur)

            # spg = self._getAffectingSpectrograph(main_data.spectrometer)
            # TODO: show axes

        self._dlg = None

        self.addMenu("Acquisition/Quick CL...\tF2", self.start)
示例#22
0
文件: util.py 项目: ktsitsikas/odemis
def create_setting_entry(container, name, va, hw_comp, conf=None, change_callback=None):
    """ Determine what type on control to use for a setting and have the container create it

    Args:
        container (SettingsController or StreamController): Controller in charge of the settings
        name (str): Name of the setting
        va (VigilantAttribute): The va containing the value of the setting
        hw_comp (Component): The hardware component to which the setting belongs
        conf ({} or None): The optional configuration options for the control
        change_callback (callable): Callable to bind to the control's change event

    Returns:
        SettingEntry

    """

    value_ctrl = lbl_ctrl = setting_entry = None

    # If no conf provided, set it to an empty dictionary
    conf = conf or {}

    # Get the range and choices
    min_val, max_val, choices, unit = process_setting_metadata(hw_comp, va, conf)
    # Format the provided choices
    si = conf.get('si', None)
    si_unif = conf.get('si_unif', True)
    choices_formatted, choices_si_prefix = format_choices(choices, uniformat=si_unif, si=si)
    # Determine the control type to use, either from config or some 'smart' default
    control_type = determine_control_type(hw_comp, va, choices_formatted, conf)

    # Special case, early stop
    if control_type == odemis.gui.CONTROL_NONE:
        # No value, not even a label, just an empty entry, so that the settings are saved
        # during acquisition
        return SettingEntry(name=name, va=va, hw_comp=hw_comp)

    # Format label
    label_text = conf.get('label', label_to_human(name))
    tooltip = conf.get('tooltip', "")

    logging.debug("Adding VA %s", label_text)
    # Create the needed wxPython controls
    if control_type == odemis.gui.CONTROL_READONLY:
        val = va.value  # only format if it's a number
        accuracy = conf.get('accuracy', 3)
        lbl_ctrl, value_ctrl = container.add_readonly_field(label_text, val)
        value_formatter = create_formatted_setter(value_ctrl, val, unit, accuracy)
        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     va_2_ctrl=value_formatter)

    elif control_type == odemis.gui.CONTROL_TEXT:
        val = va.value  # only format if it's a number
        accuracy = conf.get('accuracy', 3)
        lbl_ctrl, value_ctrl = container.add_text_field(label_text, val)
        value_formatter = create_formatted_setter(value_ctrl, val, unit, accuracy)
        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     va_2_ctrl=value_formatter,
                                     events=wx.EVT_TEXT_ENTER)

    elif control_type in (odemis.gui.CONTROL_SAVE_FILE, odemis.gui.CONTROL_OPEN_FILE):
        val = va.value
        if not val:
            config = guiconf.get_acqui_conf()
            val = config.last_path

        if control_type == odemis.gui.CONTROL_SAVE_FILE:
            dialog_style = wx.FD_SAVE
        else:  # odemis.gui.CONTROL_OPEN_FILE
            dialog_style = wx.FD_OPEN

        clearlabel = conf.get('clearlabel')  # Text to show when no filename (+ allow to clear the filename)
        lbl_ctrl, value_ctrl = container.add_file_button(label_text,
                                                         val,
                                                         clearlabel,
                                                         dialog_style=dialog_style)

        # TODO: allow to change the wildcard via a conf key?
        # value_ctrl.SetWildcard(wildcards)

        # Add the corresponding setting entry
        setting_entry = SettingEntry(name=label_text, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     events=EVT_FILE_SELECT)

    elif control_type == odemis.gui.CONTROL_SLIDER:
        # The slider is accompanied by an extra number text field

        if "type" in conf:
            if conf["type"] == "integer":
                # add_integer_slider
                factory = container.add_integer_slider
            elif conf["type"] == "slider":
                factory = container.add_slider
            else:
                factory = container.add_float_slider
        else:
            # guess from value(s)
            known_values = [va.value, min_val, max_val]
            if choices is not None:
                known_values.extend(list(choices))
            if any(isinstance(v, float) for v in known_values):
                factory = container.add_float_slider
            else:
                factory = container.add_integer_slider

        # The event configuration determines what event will signal that the
        # setting entry has changed value.
        update_event = conf.get("event", wx.EVT_SLIDER)
        if update_event not in (wx.EVT_SCROLL_CHANGED, wx.EVT_SLIDER):
            raise ValueError("Illegal event type %d for Slider setting entry!" % (update_event,))

        ctrl_conf = {
            'min_val': min_val,
            'max_val': max_val,
            'scale': conf.get('scale', None),
            'unit': unit,
            'accuracy': conf.get('accuracy', 4),
        }

        lbl_ctrl, value_ctrl = factory(label_text, va.value, ctrl_conf)
        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     events=update_event)

        if change_callback:
            value_ctrl.Bind(wx.EVT_SLIDER, change_callback)

    elif control_type == odemis.gui.CONTROL_INT:
        if unit == "":  # don't display unit prefix if no unit
            unit = None

        ctrl_conf = {
            'min_val': min_val,
            'max_val': max_val,
            'unit': unit,
            'choices': choices,
        }

        lbl_ctrl, value_ctrl = container.add_int_field(label_text, conf=ctrl_conf)

        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     events=wx.EVT_COMMAND_ENTER)

        if change_callback:
            value_ctrl.Bind(wx.EVT_COMMAND_ENTER, change_callback)

    elif control_type == odemis.gui.CONTROL_FLT:
        if unit == "":  # don't display unit prefix if no unit
            unit = None

        ctrl_conf = {
            'min_val': min_val,
            'max_val': max_val,
            'unit': unit,
            'choices': choices,
            'accuracy': conf.get('accuracy', 5),
        }

        lbl_ctrl, value_ctrl = container.add_float_field(label_text, conf=ctrl_conf)

        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     events=wx.EVT_COMMAND_ENTER)

        if change_callback:
            value_ctrl.Bind(wx.EVT_COMMAND_ENTER, change_callback)

    elif control_type == odemis.gui.CONTROL_CHECK:
        # Only supports boolean VAs

        lbl_ctrl, value_ctrl = container.add_checkbox_control(label_text, value=va.value)

        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     events=wx.EVT_CHECKBOX)

        if change_callback:
            value_ctrl.Bind(wx.EVT_CHECKBOX, change_callback)

    elif control_type == odemis.gui.CONTROL_RADIO:
        unit_fmt = (choices_si_prefix or "") + (unit or "")

        ctrl_conf = {
            'size': (-1, 16),
            'units': unit_fmt,
            'choices': [v for v, _ in choices_formatted],
            'labels': [l for _, l in choices_formatted],
        }

        lbl_ctrl, value_ctrl = container.add_radio_control(label_text, conf=ctrl_conf)

        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     events=wx.EVT_BUTTON)

        if change_callback:
            value_ctrl.Bind(wx.EVT_BUTTON, change_callback)

    elif control_type == odemis.gui.CONTROL_COMBO:

        accuracy = conf.get('accuracy', 3)

        # TODO: Might need size=(100, 16)!!
        lbl_ctrl, value_ctrl = container.add_combobox_control(label_text)

        # Set choices
        if choices_si_prefix:
            for choice, formatted in choices_formatted:
                value_ctrl.Append(u"%s %s" % (formatted, choices_si_prefix + unit), choice)
        else:
            for choice, formatted in choices_formatted:
                value_ctrl.Append(u"%s%s" % (formatted, unit), choice)

        # A small wrapper function makes sure that the value can
        # be set by passing the actual value (As opposed to the text label)
        def cb_set(value, ctrl=value_ctrl, u=unit):
            for i in range(ctrl.Count):
                if ctrl.GetClientData(i) == value:
                    logging.debug("Setting combobox value to %s", ctrl.Items[i])
                    ctrl.SetSelection(i)
                    break
            else:
                logging.debug("No existing label found for value %s in combobox ctrl %d",
                              value, id(ctrl))
                # entering value as free text
                txt = readable_str(value, u, sig=accuracy)
                return ctrl.SetValue(txt)

        # equivalent wrapper function to retrieve the actual value
        def cb_get(ctrl=value_ctrl, va=va, u=unit):
            ctrl_value = ctrl.GetValue()
            # Try to use the predefined value if it's available
            i = ctrl.GetSelection()

            # Warning: if the text contains an unknown value, GetSelection will
            # not return wx.NOT_FOUND (as expected), but the last selection value
            if i != wx.NOT_FOUND and ctrl.Items[i] == ctrl_value:
                logging.debug("Getting item value %s from combobox control",
                              ctrl.GetClientData(i))
                return ctrl.GetClientData(i)
            else:
                logging.debug("Parsing combobox free text value %s", ctrl_value)
                va_val = va.value
                # Try to find a good corresponding value inside the string

                # Try and find an SI prefix
                str_val, str_si, _ = decompose_si_prefix(ctrl_value, unit=u)

                new_val = reproduce_typed_value(va_val, str_val)

                if isinstance(new_val, collections.Iterable):
                    # be less picky, by shortening the number of values if it's too many
                    new_val = new_val[:len(va_val)]

                # If an SI prefix was found, scale the new value
                new_val = si_scale_val(new_val, str_si)

                # if it ends up being the same value as before the combobox will not update, so
                # force it now
                if va_val == new_val:
                    cb_set(va_val)
                return new_val

        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     va_2_ctrl=cb_set, ctrl_2_va=cb_get,
                                     events=(wx.EVT_COMBOBOX, wx.EVT_TEXT_ENTER))
        if change_callback:
            value_ctrl.Bind(wx.EVT_COMBOBOX, change_callback)
            value_ctrl.Bind(wx.EVT_TEXT_ENTER, change_callback)

    else:
        logging.error("Unknown control type %s", control_type)

    value_ctrl.SetToolTipString(tooltip)
    lbl_ctrl.SetToolTipString(tooltip)

    return setting_entry
示例#23
0
    def __init__(self, parent, orig_tab_data):
        xrcfr_acq.__init__(self, parent)

        self.conf = get_acqui_conf()

        for n in presets:
            self.cmb_presets.Append(n)
        # TODO: record and reuse the preset used?
        self.cmb_presets.Select(0)

        self.filename = model.StringVA(self._get_default_filename())
        self.filename.subscribe(self._onFilename, init=True)

        # a ProgressiveFuture if the acquisition is going on
        self.acq_future = None
        self._acq_future_connector = None

        # duplicate the interface, but with only one view
        self._tab_data_model = self.duplicate_tab_data_model(orig_tab_data)

        # Create a new settings controller for the acquisition dialog
        self._settings_controller = SecomSettingsController(self,
                                                       self._tab_data_model,
                                                       highlight_change=True)
        # FIXME: pass the fold_panels

        # Compute the preset values for each preset
        self._preset_values = {} # dict string -> dict (SettingEntries -> value)
        orig_entries = self._settings_controller.entries
        self._orig_settings = preset_as_is(orig_entries) # to detect changes
        for n, preset in presets.items():
            self._preset_values[n] = preset(orig_entries)
        # Presets which have been confirmed on the hardware
        self._presets_confirmed = set() # (string)

        orig_view = orig_tab_data.focussedView.value
        view = self._tab_data_model.focussedView.value

        self.stream_controller = StreamController(self._tab_data_model,
                                                  self.pnl_secom_streams)
        # The streams currently displayed are the one visible
        self.add_all_streams(orig_view.getStreams())

        # If it could be possible to do fine alignment, allow the user to choose
        if self._can_fine_align(self._tab_data_model.streams.value):
            self.chkbox_fine_align.Show()
            # Set to True to make it the default, but will be automatically
            # disabled later if the current visible streams don't allow it.
            self.chkbox_fine_align.Value = True
            main_data = self._tab_data_model.main
            self._ovrl_stream = stream.OverlayStream("fine alignment", main_data.ccd,
                                         main_data.ebeam, main_data.sed)
            self._ovrl_stream.dwellTime.value = main_data.fineAlignDwellTime.value
        else:
            self.chkbox_fine_align.Show(False)
            self.chkbox_fine_align.Value = False

        self._prev_fine_align = self.chkbox_fine_align.Value

        # make sure the view displays the same thing as the one we are
        # duplicating
        view.view_pos.value = orig_view.view_pos.value
        view.mpp.value = orig_view.mpp.value
        view.merge_ratio.value = orig_view.merge_ratio.value

        # attach the view to the viewport
        self.pnl_view_acq.setView(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_change_file.Bind(wx.EVT_BUTTON, self.on_change_file)
        self.btn_secom_acquire.Bind(wx.EVT_BUTTON, self.on_acquire)
        self.cmb_presets.Bind(wx.EVT_COMBOBOX, self.on_preset)
        self.Bind(wx.EVT_CLOSE, self.on_close)
        # on_streams_changed is compatible because it doesn't use the args
        self.chkbox_fine_align.Bind(wx.EVT_CHECKBOX, self.on_streams_changed)

        self.on_preset(None) # will force setting the current preset

        pub.subscribe(self.on_setting_change, 'setting.changed')
        # TODO: we should actually listen to the stream tree, but it's not
        # currently possible.
        # Currently just use view.last_update which should be "similar"
        view.lastUpdate.subscribe(self.on_streams_changed)
示例#24
0
文件: util.py 项目: lanery/odemis
def create_setting_entry(container, name, va, hw_comp, conf=None, change_callback=None):
    """ Determine what type on control to use for a setting and have the container create it

    Args:
        container (SettingsController or StreamController): Controller in charge of the settings
        name (str): Name of the setting
        va (VigilantAttribute): The va containing the value of the setting
        hw_comp (Component): The hardware component to which the setting belongs
        conf ({} or None): The optional configuration options for the control
        change_callback (callable): Callable to bind to the control's change event

    Returns:
        SettingEntry or None (if CONTROL_NONE)

    """

    value_ctrl = lbl_ctrl = setting_entry = None

    # If no conf provided, set it to an empty dictionary
    conf = conf or {}
    # Get the range and choices
    min_val, max_val, choices, unit = process_setting_metadata(hw_comp, va, conf)
    # Format the provided choices
    choices_formatted, choices_si_prefix = format_choices(choices)
    # Determine the control type to use, either from config or some 'smart' default
    control_type = determine_control_type(hw_comp, va, choices_formatted, conf)

    # Special case, early stop
    if control_type == odemis.gui.CONTROL_NONE:
        return None

    # Format label
    label_text = conf.get('label', label_to_human(name))
    tooltip = conf.get('tooltip', "")

    logging.debug("Adding VA %s", label_text)
    # Create the needed wxPython controls
    if control_type == odemis.gui.CONTROL_READONLY:
        val = va.value
        accuracy = conf.get('accuracy', 3)
        val_str = value_to_str(val, unit, accuracy, pretty_time=True)
        lbl_ctrl, value_ctrl = container.add_readonly_field(label_text, val_str)

        def set_ctrl(value, ctrl=value_ctrl, u=unit, acc=accuracy):
            ctrl.SetValue(value_to_str(value, u, acc, pretty_time=True))

        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     va_2_ctrl=set_ctrl)

    elif control_type == odemis.gui.CONTROL_TEXT:
        val = va.value
        accuracy = conf.get('accuracy', 3)
        val_str = value_to_str(val, unit, accuracy)  # No pretty_time, as we don't support reading it back
        lbl_ctrl, value_ctrl = container.add_text_field(label_text, val_str)

        # To set the value on the control (when the VA is updated)
        def set_ctrl(value, ctrl=value_ctrl, u=unit, acc=accuracy):
            ctrl.SetValue(value_to_str(value, u, acc))

        # To retrieve the actual value, which may contain prefix and unit
        def text_get(ctrl=value_ctrl, va=va):
            ctrl_value = ctrl.GetValue()
            va_val = va.value
            try:
                new_val = str_to_value(ctrl_value, va)
            except (ValueError, TypeError):
                logging.warning("Value %s couldn't be understood", ctrl_value, exc_info=True)
                new_val = va_val  # To force going back to last value

            # if it ends up being the same value as before the VA will
            # not update, so force reformatting it
            if va_val == new_val:
                set_ctrl(va_val)
            return new_val

        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     va_2_ctrl=set_ctrl, ctrl_2_va=text_get,
                                     events=wx.EVT_TEXT_ENTER)

        if change_callback:
            value_ctrl.Bind(wx.EVT_TEXT_ENTER, change_callback)

    elif control_type in (odemis.gui.CONTROL_SAVE_FILE, odemis.gui.CONTROL_OPEN_FILE):
        val = va.value
        if not val:
            config = guiconf.get_acqui_conf()
            val = config.last_path

        if control_type == odemis.gui.CONTROL_SAVE_FILE:
            dialog_style = wx.FD_SAVE
        else:  # odemis.gui.CONTROL_OPEN_FILE
            dialog_style = wx.FD_OPEN

        clearlabel = conf.get('clearlabel')  # Text to show when no filename (+ allow to clear the filename)
        wildcard = conf.get('wildcard', "*.*")  # File extension wildcard string
        lbl_ctrl, value_ctrl = container.add_file_button(label_text,
                                                         val,
                                                         clearlabel,
                                                         wildcard=wildcard,
                                                         dialog_style=dialog_style)

        # Add the corresponding setting entry
        setting_entry = SettingEntry(name=label_text, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     events=EVT_FILE_SELECT)

    elif control_type == odemis.gui.CONTROL_SLIDER:
        # The slider is accompanied by an extra number text field

        if "type" in conf:
            if conf["type"] == "integer":
                # add_integer_slider
                factory = container.add_integer_slider
            elif conf["type"] == "slider":
                factory = container.add_slider
            else:
                factory = container.add_float_slider
        else:
            # guess from value(s)
            known_values = [va.value, min_val, max_val]
            if choices is not None:
                known_values.extend(list(choices))
            if any(isinstance(v, float) for v in known_values):
                factory = container.add_float_slider
            else:
                factory = container.add_integer_slider

        # The event configuration determines what event will signal that the
        # setting entry has changed value.
        update_event = conf.get("event", wx.EVT_SLIDER)
        if update_event.typeId not in (wx.EVT_SCROLL_CHANGED.typeId, wx.EVT_SLIDER.typeId):
            raise ValueError("Illegal event type %d for Slider setting entry!" % (update_event.typeId,))

        ctrl_conf = {
            'min_val': min_val,
            'max_val': max_val,
            'scale': conf.get('scale', None),
            'unit': unit,
            'accuracy': conf.get('accuracy', 4),
        }

        lbl_ctrl, value_ctrl = factory(label_text, va.value, ctrl_conf)
        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     events=update_event)

        if change_callback:
            if not isinstance(update_event, collections.Iterable):
                update_event = (update_event,)
            for event in update_event:
                value_ctrl.Bind(event, change_callback)

    elif control_type == odemis.gui.CONTROL_INT:
        if unit == "":  # don't display unit prefix if no unit
            unit = None

        ctrl_conf = {
            'min_val': min_val,
            'max_val': max_val,
            'unit': unit,
            'choices': choices,
        }

        if 'key_step' in conf:
            ctrl_conf['key_step'] = conf['key_step']
        if 'key_step_min' in conf:
            ctrl_conf['key_step_min'] = conf['key_step_min']

        lbl_ctrl, value_ctrl = container.add_int_field(label_text, conf=ctrl_conf)

        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     events=wx.EVT_COMMAND_ENTER)

        if change_callback:
            value_ctrl.Bind(wx.EVT_COMMAND_ENTER, change_callback)

    elif control_type == odemis.gui.CONTROL_FLT:
        if unit == "":  # don't display unit prefix if no unit
            unit = None

        ctrl_conf = {
            'min_val': min_val,
            'max_val': max_val,
            'unit': unit,
            'choices': choices,
            'accuracy': conf.get('accuracy', 5),
        }

        if 'key_step' in conf:
            ctrl_conf['key_step'] = conf['key_step']
        if 'key_step_min' in conf:
            ctrl_conf['key_step_min'] = conf['key_step_min']

        lbl_ctrl, value_ctrl = container.add_float_field(label_text, conf=ctrl_conf)

        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     events=wx.EVT_COMMAND_ENTER)

        if change_callback:
            value_ctrl.Bind(wx.EVT_COMMAND_ENTER, change_callback)

    elif control_type == odemis.gui.CONTROL_CHECK:
        # Only supports boolean VAs

        lbl_ctrl, value_ctrl = container.add_checkbox_control(label_text, value=va.value)

        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     events=wx.EVT_CHECKBOX)

        if change_callback:
            value_ctrl.Bind(wx.EVT_CHECKBOX, change_callback)

    elif control_type == odemis.gui.CONTROL_RADIO:
        unit_fmt = (choices_si_prefix or "") + (unit or "")

        ctrl_conf = {
            'size': (-1, 16),
            'units': unit_fmt,
            'choices': [v for v, _ in choices_formatted],
            'labels': [l for _, l in choices_formatted],
        }

        lbl_ctrl, value_ctrl = container.add_radio_control(label_text, conf=ctrl_conf)

        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     events=wx.EVT_BUTTON)

        if change_callback:
            value_ctrl.Bind(wx.EVT_BUTTON, change_callback)

    elif control_type == odemis.gui.CONTROL_COMBO:

        accuracy = conf.get('accuracy', 3)

        # TODO: Might need size=(100, 16)!!
        cbconf = {}
        if hasattr(va, "choices") and isinstance(va.choices, collections.Iterable):
            # Enumerated VA => don't allow entering other values
            cbconf["style"] = wx.CB_READONLY
        lbl_ctrl, value_ctrl = container.add_combobox_control(label_text, conf=cbconf)

        # Set choices
        if choices_si_prefix:
            for choice, formatted in choices_formatted:
                value_ctrl.Append(u"%s %s" % (formatted, choices_si_prefix + unit), choice)
        else:
            for choice, formatted in choices_formatted:
                value_ctrl.Append(u"%s%s" % (formatted, unit), choice)

        # A small wrapper function makes sure that the value can
        # be set by passing the actual value (As opposed to the text label)
        def cb_set(value, va=va, ctrl=value_ctrl, u=unit, acc=accuracy):
            # Re-read the value from the VA because it'll be called via
            # CallAfter(), and if the value is changed multiple times, it might
            # not be in chronological order.
            value = va.value
            for i in range(ctrl.GetCount()):
                d = ctrl.GetClientData(i)
                if (d == value or
                    (all(isinstance(v, float) for v in (value, d)) and
                     util.almost_equal(d, value))
                   ):
                    logging.debug("Setting combobox value to %s", ctrl.Items[i])
                    ctrl.SetSelection(i)
                    break
            else:
                logging.debug("No existing label found for value %s in combobox ctrl %d",
                              value, id(ctrl))
                # entering value as free text
                txt = value_to_str(value, u, acc)
                ctrl.SetValue(txt)

        # equivalent wrapper function to retrieve the actual value
        def cb_get(ctrl=value_ctrl, va=va, u=unit):
            ctrl_value = ctrl.GetValue()
            # Try to use the predefined value if it's available
            i = ctrl.GetSelection()

            # Warning: if the text contains an unknown value, GetSelection will
            # not return wx.NOT_FOUND (as expected), but the last selection value
            if i != wx.NOT_FOUND and ctrl.Items[i] == ctrl_value:
                logging.debug("Getting item value %s from combobox control",
                              ctrl.GetClientData(i))
                return ctrl.GetClientData(i)
            else:
                va_val = va.value
                try:
                    new_val = str_to_value(ctrl_value, va)
                except (ValueError, TypeError):
                    logging.warning("Value %s couldn't be understood", ctrl_value, exc_info=True)
                    new_val = va_val  # To force going back to last value

                # if it ends up being the same value as before the combobox will
                # not update, so force it now
                if va_val == new_val:
                    cb_set(va_val)
                return new_val

        setting_entry = SettingEntry(name=name, va=va, hw_comp=hw_comp,
                                     lbl_ctrl=lbl_ctrl, value_ctrl=value_ctrl,
                                     va_2_ctrl=cb_set, ctrl_2_va=cb_get,
                                     events=(wx.EVT_COMBOBOX, wx.EVT_TEXT_ENTER))
        if change_callback:
            value_ctrl.Bind(wx.EVT_COMBOBOX, change_callback)
            value_ctrl.Bind(wx.EVT_TEXT_ENTER, change_callback)

    else:
        logging.error("Unknown control type %s", control_type)

    value_ctrl.SetToolTip(tooltip)
    lbl_ctrl.SetToolTip(tooltip)

    return setting_entry
示例#25
0
    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.pj_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.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)

        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

        # 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 when streams are removed/added
        self._view.stream_tree.flat.subscribe(self.on_streams_changed,
                                              init=True)

        zstep = util.readable_str(ZSTEP, unit="m", sig=3)
        self.zstack_slider_step.SetLabel(zstep)
示例#26
0
def ShowAcquisitionFileDialog(parent, filename):
    """
    parent (wxFrame): parent window
    filename (string): full filename to propose by default
    Note: updates the acquisition configuration if the user did pick a new file
    return (string or None): the new filename (or the None if the user cancelled)
    """
    conf = get_acqui_conf()

    # Find the available formats (and corresponding extensions)
    formats_to_ext = dataio.get_available_formats()

    # current filename
    path, base = os.path.split(filename)

    # Note: When setting 'defaultFile' when creating the file dialog, the
    #   first filter will automatically be added to the name. Since it
    #   cannot be changed by selecting a different file type, this is big
    #   nono. Also, extensions with multiple periods ('.') are not correctly
    #   handled. The solution is to use the SetFilename method instead.
    wildcards, formats = formats_to_wildcards(formats_to_ext)
    dialog = wx.FileDialog(parent,
                           message="Choose a filename and destination",
                           defaultDir=path,
                           defaultFile="",
                           style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT,
                           wildcard=wildcards)

    # Select the last format used
    prev_fmt = conf.last_format
    try:
        idx = formats.index(conf.last_format)
    except ValueError:
        idx = 0
    dialog.SetFilterIndex(idx)

    # Strip the extension, so that if the user changes the file format,
    # it will not have 2 extensions in a row.
    if base.endswith(conf.last_extension):
        base = base[:-len(conf.last_extension)]
    dialog.SetFilename(base)

    # Show the dialog and check whether is was accepted or cancelled
    if dialog.ShowModal() != wx.ID_OK:
        return None

    # New location and name have been selected...
    # Store the path
    path = dialog.GetDirectory()
    conf.last_path = path

    # Store the format
    fmt = formats[dialog.GetFilterIndex()]
    conf.last_format = fmt

    # Check the filename has a good extension, or add the default one
    fn = dialog.GetFilename()
    ext = None
    for extension in formats_to_ext[fmt]:
        if fn.endswith(extension) and len(extension) > len(ext or ""):
            ext = extension

    if ext is None:
        if fmt == prev_fmt and conf.last_extension in formats_to_ext[fmt]:
            # if the format is the same (and extension is compatible): keep
            # the extension. This avoid changing the extension if it's not
            # the default one.
            ext = conf.last_extension
        else:
            ext = formats_to_ext[fmt][0] # default extension
        fn += ext

    conf.last_extension = ext

    return os.path.join(path, fn)
示例#27
0
def ShowAcquisitionFileDialog(parent, filename):
    """
    parent (wxFrame): parent window
    filename (string): full filename to propose by default
    Note: updates the acquisition configuration if the user did pick a new file
    return (string): the new filename (or the old one if the user cancelled)
    """
    conf = get_acqui_conf()

    # Find the available formats (and corresponding extensions)
    formats_to_ext = dataio.get_available_formats()

    # current filename
    path, base = os.path.split(filename)

    # Note: When setting 'defaultFile' when creating the file dialog, the
    #   first filter will automatically be added to the name. Since it
    #   cannot be changed by selecting a different file type, this is big
    #   nono. Also, extensions with multiple periods ('.') are not correctly
    #   handled. The solution is to use the SetFilename method instead.
    wildcards, formats = formats_to_wildcards(formats_to_ext)
    dialog = wx.FileDialog(parent,
                           message="Choose a filename and destination",
                           defaultDir=path,
                           defaultFile="",
                           style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT,
                           wildcard=wildcards)

    # Select the last format used
    prev_fmt = conf.last_format
    try:
        idx = formats.index(conf.last_format)
    except ValueError:
        idx = 0
    dialog.SetFilterIndex(idx)

    # Strip the extension, so that if the user changes the file format,
    # it will not have 2 extensions in a row.
    if base.endswith(conf.last_extension):
        base = base[:-len(conf.last_extension)]
    dialog.SetFilename(base)

    # Show the dialog and check whether is was accepted or cancelled
    if dialog.ShowModal() != wx.ID_OK:
        return filename

    # New location and name have been selected...
    # Store the path
    path = dialog.GetDirectory()
    conf.last_path = path

    # Store the format
    fmt = formats[dialog.GetFilterIndex()]
    conf.last_format = fmt

    # Check the filename has a good extension, or add the default one
    fn = dialog.GetFilename()
    ext = None
    for extension in formats_to_ext[fmt]:
        if fn.endswith(extension) and len(extension) > len(ext or ""):
            ext = extension

    if ext is None:
        if fmt == prev_fmt and conf.last_extension in formats_to_ext[fmt]:
            # if the format is the same (and extension is compatible): keep
            # the extension. This avoid changing the extension if it's not
            # the default one.
            ext = conf.last_extension
        else:
            ext = formats_to_ext[fmt][0] # default extension
        fn += ext

    conf.last_extension = ext
    conf.write()

    return os.path.join(path, fn)