Exemplo n.º 1
0
    def UpdateMagnification(self):
        # Total magnification
        mag = self._mpp_screen / self._microscope_view.mpp.value
        label = u"Mag: × %s" % units.readable_str(
            units.round_significant(mag, 3))

        # Gather all different image mpp values
        mpps = set()
        for im, stream in self._microscope_view.stream_tree.getImages():
            try:
                if hasattr(stream, 'mpp'):  # im is a tuple of tuple of tiles
                    mpps.add(stream.mpp.min)
                else:
                    md = im.metadata
                    mpps.add(md[model.MD_PIXEL_SIZE][0])
            except KeyError:
                pass

        # If there's only one mpp value (i.e. there's only one image, or they
        # all have the same mpp value), indicate the digital zoom.
        if len(mpps) == 1:
            mpp_im = mpps.pop()
            # mag_im = self._mpp_screen / mpp_im  # as if 1 im.px == 1 sc.px
            mag_dig = mpp_im / self._microscope_view.mpp.value
            label += u" (Digital: × %s)" % units.readable_str(
                units.round_significant(mag_dig, 2))

        if self.bottom_legend:
            self.bottom_legend.set_mag_label(label)
Exemplo n.º 2
0
def acquire(comp_name, dataflow_names, filename):
    """
    Acquire an image from one (or more) dataflow
    comp_name (string): name of the detector to find
    dataflow_names (list of string): name of each dataflow to access
    filename (unicode): name of the output file (format depends on the extension)
    """
    component = get_detector(comp_name)

    # check the dataflow exists
    dataflows = []
    for df_name in dataflow_names:
        try:
            df = getattr(component, df_name)
        except AttributeError:
            raise ValueError("Failed to find data-flow '%s' on component %s" %
                             (df_name, comp_name))

        if not isinstance(df, model.DataFlowBase):
            raise ValueError("%s.%s is not a data-flow" % (comp_name, df_name))

        dataflows.append(df)

    images = []
    for df in dataflows:
        try:
            # Note: currently, get() uses Pyro, which is not as memory efficient
            # as .subscribe(), which uses ZMQ. So would need to use
            # _get_big_image() if very large image is requested.
            image = df.get()
        except Exception as exc:
            raise IOError("Failed to acquire image from component %s: %s" %
                          (comp_name, exc))

        logging.info("Acquired an image of dimension %r.", image.shape)
        images.append(image)

        try:
            if model.MD_PIXEL_SIZE in image.metadata:
                pxs = image.metadata[model.MD_PIXEL_SIZE]
                dim = (image.shape[0] * pxs[0], image.shape[1] * pxs[1])
                logging.info("Physical dimension of image is %s.",
                             units.readable_str(dim, unit="m", sig=3))
            else:
                logging.warning("Physical dimension of image is unknown.")

            if model.MD_SENSOR_PIXEL_SIZE in image.metadata:
                spxs = image.metadata[model.MD_SENSOR_PIXEL_SIZE]
                dim_sens = (image.shape[0] * spxs[0], image.shape[1] * spxs[1])
                logging.info("Physical dimension of sensor is %s.",
                             units.readable_str(dim_sens, unit="m", sig=3))
        except Exception as exc:
            logging.exception("Failed to read image information.")

    exporter = dataio.find_fittest_converter(filename)
    try:
        exporter.export(filename, images)
    except IOError as exc:
        raise IOError(u"Failed to save to '%s': %s" % (filename, exc))
Exemplo n.º 3
0
def acquire(comp_name, dataflow_names, filename):
    """
    Acquire an image from one (or more) dataflow
    comp_name (string): name of the detector to find
    dataflow_names (list of string): name of each dataflow to access
    filename (unicode): name of the output file (format depends on the extension)
    """
    component = get_detector(comp_name)

    # check the dataflow exists
    dataflows = []
    for df_name in dataflow_names:
        try:
            df = getattr(component, df_name)
        except AttributeError:
            raise ValueError("Failed to find data-flow '%s' on component %s" % (df_name, comp_name))

        if not isinstance(df, model.DataFlowBase):
            raise ValueError("%s.%s is not a data-flow" % (comp_name, df_name))

        dataflows.append(df)

    images = []
    for df in dataflows:
        try:
            # Note: currently, get() uses Pyro, which is not as memory efficient
            # as .subscribe(), which uses ZMQ. So would need to use
            # _get_big_image() if very large image is requested.
            image = df.get()
        except Exception as exc:
            raise IOError("Failed to acquire image from component %s: %s" % (comp_name, exc))

        logging.info("Acquired an image of dimension %r.", image.shape)
        images.append(image)

        try:
            if model.MD_PIXEL_SIZE in image.metadata:
                pxs = image.metadata[model.MD_PIXEL_SIZE]
                dim = (image.shape[0] * pxs[0], image.shape[1] * pxs[1])
                logging.info("Physical dimension of image is %s.",
                             units.readable_str(dim, unit="m", sig=3))
            else:
                logging.warning("Physical dimension of image is unknown.")

            if model.MD_SENSOR_PIXEL_SIZE in image.metadata:
                spxs = image.metadata[model.MD_SENSOR_PIXEL_SIZE]
                dim_sens = (image.shape[0] * spxs[0], image.shape[1] * spxs[1])
                logging.info("Physical dimension of sensor is %s.",
                             units.readable_str(dim_sens, unit="m", sig=3))
        except Exception as exc:
            logging.exception("Failed to read image information.")

    exporter = dataio.find_fittest_converter(filename)
    try:
        exporter.export(filename, images)
    except IOError as exc:
        raise IOError(u"Failed to save to '%s': %s" % (filename, exc))
Exemplo n.º 4
0
    def draw(self, ctx):
        # TODO: Both focuses at the same time, or 'snap' to horizontal/vertical on first motion?

        ctx.set_line_width(10)
        ctx.set_line_join(cairo.LINE_JOIN_MITER)
        ctx.set_source_rgba(1.0, 1.0, 1.0, 0.8)

        x, y = self.cnvs.ClientSize

        # Horizontal
        if self.shifts[0] is not None:
            y -= self.margin + (self.line_width // 2)
            middle = x / 2

            # don't display extremely small values, which are due to accumulation
            # of floating point error
            shiftm = self.shifts[0]
            if abs(shiftm) < 1e-12:
                shiftm = 0
            shift = shiftm * self.ppm[0]
            end_x = middle + (middle * (shift / (x / 2)))
            end_x = min(max(self.margin, end_x), x - self.margin)

            ctx.move_to(middle, y)
            ctx.line_to(end_x, y)
            ctx.stroke()

            lbl = "focus %s" % units.readable_str(shiftm, "m", 2)
            self.focus_label.text = lbl
            self.focus_label.pos = (end_x, y - 15)
            self._write_label(ctx, self.focus_label)

        # Vertical
        if self.shifts[1] is not None:
            x -= self.margin + (self.line_width // 2)
            middle = y / 2

            # don't display extremely small values, which are due to accumulation
            # of floating point error
            shiftm = self.shifts[1]
            if abs(shiftm) < 1e-12:
                shiftm = 0
            shift = shiftm * self.ppm[1]
            end_y = middle - (middle * (shift / (y / 2)))
            end_y = min(max(self.margin, end_y), y - self.margin)

            ctx.move_to(x, middle)
            ctx.line_to(x, end_y)
            ctx.stroke()

            lbl = "focus %s" % units.readable_str(shiftm, "m", 2)
            self.focus_label.text = lbl
            self.focus_label.pos = (x - 15, end_y)
            self._write_label(ctx, self.focus_label)
Exemplo n.º 5
0
    def _on_fa_done(self, future):
        logging.debug("End of overlay procedure")
        main_data = self._main_data_model
        self._acq_future = None  # To avoid holding the ref in memory
        self._faf_connector = None

        try:
            trans_val, cor_md = future.result()
            opt_md, sem_md = cor_md

            # Save the optical correction metadata straight into the CCD
            main_data.ccd.updateMetadata(opt_md)

            # The SEM correction metadata goes to the ebeam
            main_data.ebeam.updateMetadata(sem_md)
        except CancelledError:
            self._tab_panel.lbl_fine_align.Label = "Cancelled"
        except Exception:
            self._tab_panel.lbl_fine_align.Label = "Failed"
        else:
            self._tab_panel.lbl_fine_align.Label = "Successful"
            self._main_frame.menu_item_reset_finealign.Enable(True)
            # Temporary info until the GUI can actually rotate the images
            rot = math.degrees(opt_md.get(model.MD_ROTATION_COR, 0))
            shear = sem_md.get(model.MD_SHEAR_COR, 0)
            scaling_xy = sem_md.get(model.MD_PIXEL_SIZE_COR, (1, 1))
            # the worse is the rotation, the longer it's displayed
            timeout = max(2, min(abs(rot), 10))
            popup.show_message(
                self._tab_panel,
                u"Rotation applied: %s\nShear applied: %s\nX/Y Scaling applied: %s"
                % (units.readable_str(rot, unit="°", sig=3),
                   units.readable_str(shear, sig=3),
                   units.readable_str(scaling_xy, sig=3)),
                timeout=timeout
            )
            logging.warning("Fine alignment computed rotation needed of %f°, "
                            "shear needed of %s, and X/Y scaling needed of %s.",
                            rot, shear, scaling_xy)

        # As the CCD image might have different pixel size, force to fit
        self._tab_panel.vp_align_ccd.canvas.fit_view_to_next_image = True

        main_data.is_acquiring.value = False
        self._tab_panel.btn_fine_align.Bind(wx.EVT_BUTTON, self._on_fine_align)
        self._tab_panel.btn_fine_align.Label = self._fa_btn_label
        self._resume()

        self._tab_panel.lbl_fine_align.Show()
        self._tab_panel.gauge_fine_align.Hide()
        self._sizer.Layout()
Exemplo n.º 6
0
    def _on_fa_done(self, future):
        logging.debug("End of overlay procedure")
        main_data = self._main_data_model
        self._acq_future = None  # To avoid holding the ref in memory
        self._faf_connector = None

        try:
            trans_val, cor_md = future.result()
            opt_md, sem_md = cor_md

            # Save the optical correction metadata straight into the CCD
            main_data.ccd.updateMetadata(opt_md)

            # The SEM correction metadata goes to the ebeam
            main_data.ebeam.updateMetadata(sem_md)
        except CancelledError:
            self._tab_panel.lbl_fine_align.Label = "Cancelled"
        except Exception:
            self._tab_panel.lbl_fine_align.Label = "Failed"
        else:
            self._tab_panel.lbl_fine_align.Label = "Successful"
            self._main_frame.menu_item_reset_finealign.Enable(True)
            # Temporary info until the GUI can actually rotate the images
            rot = math.degrees(opt_md.get(model.MD_ROTATION_COR, 0))
            shear = sem_md.get(model.MD_SHEAR_COR, 0)
            scaling_xy = sem_md.get(model.MD_PIXEL_SIZE_COR, (1, 1))
            # the worse is the rotation, the longer it's displayed
            timeout = max(2, min(abs(rot), 10))
            popup.show_message(
                self._tab_panel,
                u"Rotation applied: %s\nShear applied: %s\nX/Y Scaling applied: %s"
                % (units.readable_str(
                    rot, unit="°", sig=3), units.readable_str(
                        shear, sig=3), units.readable_str(scaling_xy, sig=3)),
                timeout=timeout)
            logging.warning(
                "Fine alignment computed rotation needed of %f°, "
                "shear needed of %s, and X/Y scaling needed of %s.", rot,
                shear, scaling_xy)

        # As the CCD image might have different pixel size, force to fit
        self._tab_panel.vp_align_ccd.canvas.fit_view_to_next_image = True

        main_data.is_acquiring.value = False
        self._tab_panel.btn_fine_align.Bind(wx.EVT_BUTTON, self._on_fine_align)
        self._tab_panel.btn_fine_align.Label = self._fa_btn_label
        self._resume()

        self._tab_panel.lbl_fine_align.Show()
        self._tab_panel.gauge_fine_align.Hide()
        self._sizer.Layout()
Exemplo n.º 7
0
 def test_readable_str(self):
     #         (input) (expected output)
     values = [
         ((1.0, None), "1"),
         ((1, None), "1"),
         ((-1.234, "m"), "-1.234 m"),
         ((-1234, "g"), "-1.234 kg"),
         ((160000, None), "160000"),
         ((16, None), "16"),
         ((160001, None, 3), "160000"),
         ((16000000.0, None), "16000000"),
         ((16.3000000001, "px", 3), "16.3 px"),
         ((numpy.float64(16.3), "px", 3), "16.3 px"),
         ((-1600, "N"), "-1.6 kN"),
         ((-1601, "N", 3), "-1.6 kN"),  # sig=3
         ((0.0001236, None), "0.0001236"),
         ((0.0012, "V"), "1.2 mV"),
         ((999.999, "V", 3), "1 kV"),
         ((999.5, "V"), "999.5 V"),
         ((999.5, "V", 3), "1 kV"),
         ((999.4, "V", 3), "999 V"),
         ((200e-6, "m"), u"200 µm"),
         ((0.0, "m"), "0 m"),
         (([1500, 1200, 150], None), "1500 x 1200 x 150"),
         (([0.0001236, 0.00014], "m"), u"123.6 x 140 µm"),
         (([0.0001236, 12.0], "m"), "0.0001236 x 12 m"),
         (([1200,
            1000], "px"), "1200 x 1000 px"),  # special non-prefix unit
         (([-float("inf"), float("NaN")], "m"), u"-∞ x unknown m"),
     ]
     for (i, eo) in values:
         o = units.readable_str(*i)
         self.assertEqual(o, eo,
                          u"%s is '%s' while expected '%s'" % (i, o, eo))
Exemplo n.º 8
0
 def test_readable_str(self):
     #         (input) (expected output)
     values = [((1.0, None), "1"),
               ((1, None), "1"),
               ((-1.234, "m"), "-1.234 m"),
               ((-1234, "g"), "-1.234 kg"),
               ((160000, None), "160000"),
               ((16L, None), "16"),
               ((160001, None, 3), "160000"),
               ((16000000.0, None), "16000000"),
               ((-1600, "N"), "-1.6 kN"),
               ((-1601, "N", 3), "-1.6 kN"), # sig=3
               ((0.0001236, None), "0.0001236"),
               ((0.0012, "V"), "1.2 mV"),
               ((200e-6, "m"), u"200 µm"),
               ((0.0, "m"), "0 m"),
               (([1500, 1200, 150], None), "1500 x 1200 x 150"),
               (([0.0001236, 0.00014], "m"), u"123.6 x 140 µm"),
               (([0.0001236, 12.0], "m"), "0.0001236 x 12 m"),
               (([1200, 1000], "px"), "1200 x 1000 px"), # special non-prefix unit
               (([-float("inf"), float("NaN")], "m"), u"-∞ x unknown m"),
               ]
     for (i, eo) in values:
         o = units.readable_str(*i)
         self.assertEquals(o, eo,
                           u"%s is '%s' while expected '%s'" % (i, o, eo))
Exemplo n.º 9
0
    def Draw(self, ctx):
        """
        Draws the crosshair
        dc (wx.DC)
        """
        # TODO: handle displaying the focus 0 (horizontally)

        if self.shifts[1]:
            ctx.set_line_width(10)
            ctx.set_line_join(cairo.LINE_JOIN_MITER)
            ctx.set_source_rgba(1.0, 1.0, 1.0, 0.8)

            x, y = self.cnvs.ClientSize
            x -= self.margin + (self.line_width // 2)
            middle = y / 2

            shift = self.shifts[1] * 1e6 # typically within µm
            end_y = middle - (middle * (shift / (y / 2)))
            end_y = min(max(self.margin, end_y), y - self.margin)

            ctx.move_to(x, middle)
            ctx.line_to(x, end_y)
            ctx.stroke()

            lbl = "focus %s" % units.readable_str(self.shifts[1], 'm', 2)
            self.focus_label.text = lbl
            self.focus_label.pos = (x - 15, end_y)
            self._write_label(ctx, self.focus_label)
Exemplo n.º 10
0
 def _display_pretty(self):
     if self._number_value is None:
         str_val = u""
     elif self._number_value == 0 and self.unit not in units.IGNORE_UNITS:
         # Special case with 0: readable_str return just "0 unit", without
         # prefix. This is technically correct, but quite inconvenient and
         # a little strange when the typical value has a prefix (eg, nm, kV).
         # => use prefix of key_step (as it's a "small value")
         if self.key_step:
             _, prefix = units.get_si_scale(self.key_step)
         elif self.key_step_min:
             _, prefix = units.get_si_scale(self.key_step_min)
         else:
             prefix = ""
         str_val = "0 %s%s" % (prefix, self.unit)
     else:
         str_val = units.readable_str(self._number_value, self.unit,
                                      self.accuracy)
     # Get the length of the number, without the unit (number and unit are separated by a space)
     number_length = str_val.find(" ")
     if number_length < 0:  # No space found -> only numbers
         number_length = len(str_val)
     wx.TextCtrl.ChangeValue(self, str_val)
     # Select the number value
     wx.CallAfter(self.SetSelection, number_length, number_length)
Exemplo n.º 11
0
    def add_metadata(self, key, value):
        """ Adds an entry representing specific metadata

        According to the metadata key, the right representation is used for the value.

        :param key: (model.MD_*) the metadata key
        :param value: (depends on the metadata) the value to display

        """

        # By default the key is a nice user-readable string
        label = str(key)

        # Convert value to a nice string according to the metadata type
        try:
            if key == model.MD_ACQ_DATE:
                # convert to a date using the user's preferences
                nice_str = time.strftime("%c", time.localtime(value))
                # In Python 2, we still need to convert it to unicode
                if isinstance(nice_str, bytes):
                    nice_str = nice_str.decode(locale.getpreferredencoding())
            else:
                # Still try to beautify a bit if it's a number
                if (isinstance(value, (int, long, float)) or
                    (isinstance(value, collections.Iterable) and len(value) > 0
                     and isinstance(value[0], (int, long, float)))):
                    nice_str = readable_str(value, sig=3)
                else:
                    nice_str = str(value)
        except Exception:
            logging.exception("Trying to convert metadata %s", key)
            nice_str = "N/A"

        self.panel.add_readonly_field(label, nice_str)
Exemplo n.º 12
0
    def Draw(self, ctx):
        """
        Draws the crosshair
        dc (wx.DC)
        """
        # TODO: handle displaying the focus 0 (horizontally)

        if self.shifts[1]:
            ctx.set_line_width(10)
            ctx.set_line_join(cairo.LINE_JOIN_MITER)
            ctx.set_source_rgba(1.0, 1.0, 1.0, 0.8)

            x, y = self.cnvs.ClientSize
            x -= self.margin + (self.line_width // 2)
            middle = y / 2

            shift = self.shifts[1] * 1e6  # typically within µm
            end_y = middle - (middle * (shift / (y / 2)))
            end_y = min(max(self.margin, end_y), y - self.margin)

            ctx.move_to(x, middle)
            ctx.line_to(x, end_y)
            ctx.stroke()

            lbl = "focus %s" % units.readable_str(self.shifts[1], 'm', 2)
            self.focus_label.text = lbl
            self.focus_label.pos = (x - 15, end_y)
            self._write_label(ctx, self.focus_label)
Exemplo n.º 13
0
    def add_metadata(self, key, value):
        """ Adds an entry representing specific metadata

        According to the metadata key, the right representation is used for the value.

        :param key: (model.MD_*) the metadata key
        :param value: (depends on the metadata) the value to display

        """

        # By default the key is a nice user-readable string
        label = unicode(key)

        # Convert value to a nice string according to the metadata type
        try:
            if key == model.MD_ACQ_DATE:
                # convert to a date using the user's preferences
                nice_str = time.strftime(u"%c", time.localtime(value))
            else:
                # Still try to beautify a bit if it's a number
                if isinstance(value, (int, long, float)) or (
                    isinstance(value, collections.Iterable)
                    and len(value) > 0
                    and isinstance(value[0], (int, long, float))
                ):
                    nice_str = readable_str(value, sig=3)
                else:
                    nice_str = unicode(value)
        except Exception:
            logging.exception("Trying to convert metadata %s", key)
            nice_str = "N/A"

        self.panel.add_readonly_field(label, nice_str)
Exemplo n.º 14
0
    def Draw(self, ctx, shift=(0, 0), scale=1.0):

        if self.w_start_pos and self.w_end_pos:
            ctx.save()

            # Important: We need to use the world positions, in order to draw everything at the
            # right scale.
            offset = self.cnvs.get_half_buffer_size()
            b_pos = (self.cnvs.world_to_buffer(self.w_start_pos, offset) +
                     self.cnvs.world_to_buffer(self.w_end_pos, offset))
            b_pos = self._normalize(b_pos)
            self.update_from_buffer(b_pos[:2], b_pos[2:4], shift + (scale, ))

            #logging.warn("%s %s", shift, world_to_buffer_pos(shift))
            rect = (b_pos[0] + 0.5, b_pos[1] + 0.5, b_pos[2] - b_pos[0],
                    b_pos[3] - b_pos[1])

            # draws a light black background for the rectangle
            ctx.set_line_width(4)
            ctx.set_source_rgba(0, 0, 0, 0.5)
            ctx.rectangle(*rect)
            ctx.stroke()

            # draws the dotted line
            ctx.set_line_width(2)
            ctx.set_dash([3])
            ctx.set_line_join(cairo.LINE_JOIN_MITER)
            ctx.set_source_rgba(*self.colour)
            ctx.rectangle(*rect)
            ctx.stroke()

            # Label
            if (self.selection_mode
                    in (base.SEL_MODE_EDIT, base.SEL_MODE_CREATE)
                    and self.cnvs.microscope_view):
                w, h = self.cnvs.selection_to_real_size(
                    self.w_start_pos, self.w_end_pos)
                w = units.readable_str(w, 'm', sig=2)
                h = units.readable_str(h, 'm', sig=2)
                size_lbl = u"{} x {}".format(w, h)

                pos = (b_pos[2] + 8, b_pos[3] - 10)

                self.position_label.pos = pos
                self.position_label.text = size_lbl
                self._write_labels(ctx)
            ctx.restore()
Exemplo n.º 15
0
def format_axis_choices(name, axis_def):
    """
    Transform the given choices for an axis into an user friendly display

    name (str): the name of the axis
    axis_def (Axis): the axis definition

    returns:
      choices_formatted (None or list of (value, str): axis value/user-friendly
         display name (including the unit). None if axis doesn't support choices.
    """

    try:
        choices = axis_def.choices
    except AttributeError:
        return None

    if not choices:
        return None

    unit = axis_def.unit

    if isinstance(choices, dict):
        choices_formatted = list(choices.items())
        # In this case, normally the values are already formatted, but for
        # wavelength band, the "formatted" value is still a band info (ie, two
        # values in m)
        if name == "band":

            def to_readable_band(v):
                if (isinstance(v, (tuple, list)) and len(v) > 1 and
                        all(isinstance(c, numbers.Real) for c in v)):
                    return fluo.to_readable_band(v)
                else:
                    return v

            choices_formatted = [(k, to_readable_band(v)) for k, v in choices_formatted]
    elif len(choices) > 1 and all(isinstance(c, numbers.Real) for c in choices):
        choices_formatted = None
        try:
            choices = sorted(choices)
            # Can we fit them (more or less) all with the same unit prefix?
            mn_non0 = min(c for c in choices if c != 0)
            if abs(choices[-1] / mn_non0) < 1000:
                fmt, choices_si_prefix = utun.si_scale_list(choices)
                fmt = [utun.to_string_pretty(c, 3, unit) for c in fmt]
                choices_formatted = list(zip(choices, fmt))
        except Exception:
            logging.exception("Formatting error for %s", choices)
        if choices_formatted is None:
            choices_formatted = [(c, readable_str(c, unit=unit, sig=3)) for c in choices]
    else:
        choices_formatted = [(c, u"%s %s" % (choice_to_str(c), unit)) for c in choices]

    if not isinstance(choices, OrderedDict):
        # sort 2-tuples = according to first value in tuple
        choices_formatted = sorted(choices_formatted)

    return choices_formatted
Exemplo n.º 16
0
def format_axis_choices(name, axis_def):
    """
    Transform the given choices for an axis into an user friendly display

    name (str): the name of the axis
    axis_def (Axis): the axis definition

    returns:
      choices_formatted (None or list of (value, str): axis value/user-friendly
         display name (including the unit). None if axis doesn't support choices.
    """

    try:
        choices = axis_def.choices
    except AttributeError:
        return None

    if not choices:
        return None

    unit = axis_def.unit

    if isinstance(choices, dict):
        choices_formatted = choices.items()
        # In this case, normally the values are already formatted, but for
        # wavelength band, the "formatted" value is still a band info (ie, two
        # values in m)
        if name == "band":

            def to_readable_band(v):
                if (isinstance(v, (tuple, list)) and len(v) > 1 and
                        all(isinstance(c, numbers.Real) for c in v)):
                    return fluo.to_readable_band(v)
                else:
                    return v

            choices_formatted = [(k, to_readable_band(v)) for k, v in choices_formatted]
    elif len(choices) > 1 and all(isinstance(c, numbers.Real) for c in choices):
        choices_formatted = None
        try:
            choices = sorted(choices)
            # Can we fit them (more or less) all with the same unit prefix?
            mn_non0 = min(c for c in choices if c != 0)
            if abs(choices[-1] / mn_non0) < 1000:
                fmt, choices_si_prefix = utun.si_scale_list(choices)
                fmt = [utun.to_string_pretty(c, 3, unit) for c in fmt]
                choices_formatted = zip(choices, fmt)
        except Exception:
            logging.exception("Formatting error for %s", choices)
        if choices_formatted is None:
            choices_formatted = [(c, readable_str(c, unit=unit, sig=3)) for c in choices]
    else:
        choices_formatted = [(c, u"%s %s" % (choice_to_str(c), unit)) for c in choices]

    if not isinstance(choices, OrderedDict):
        # sort 2-tuples = according to first value in tuple
        choices_formatted = sorted(choices_formatted)

    return choices_formatted
Exemplo n.º 17
0
def move(comp_name, moves, check_distance=True, to_radians=False):
    """
    move (relatively) the axis of the given component by the specified amount of µm
    comp_name (str): name of the component
    moves (dict str -> str): axis -> distance (as text, and in µm for distances)
    check_distance (bool): if the axis is in meters, check that the move is not
      too big.
    to_radians (bool): will convert from degrees to radians if the axis is in radians,
      otherwise will fail
    """
    # for safety reason, we use µm instead of meters, as it's harder to type a
    # huge distance
    component = get_component(comp_name)

    act_mv = {} # axis -> value
    for axis_name, str_distance in moves.items():
        try:
            if axis_name not in component.axes:
                raise ValueError("Actuator %s has no axis '%s'" % (comp_name, axis_name))
            ad = component.axes[axis_name]
        except (TypeError, AttributeError):
            raise ValueError("Component %s is not an actuator" % comp_name)

        if ad.unit == "m":
            try:
                # Use convert_to_object() to allow typing negative values with e:
                # -1e-6 => '!!float -1.0e-6'. It's not very nice, but does work.
                distance = float(convert_to_object(str_distance)) * 1e-6  # µm -> m
            except ValueError:
                raise ValueError("Distance '%s' cannot be converted to a number" %
                                 str_distance)

            if check_distance and abs(distance) > MAX_DISTANCE:
                raise IOError("Distance of %f m is too big (> %f m), use '--big-distance' to allow the move." %
                              (abs(distance), MAX_DISTANCE))
        else:
            distance = convert_to_object(str_distance)

        if to_radians:
            if ad.unit == "rad":
                distance = math.radians(distance)
            else:
                raise ValueError("Axis %s is in %s, doesn't support value in degrees" % (axis_name, ad.unit))

        act_mv[axis_name] = distance
        logging.info(u"Will move %s.%s by %s", comp_name, axis_name,
                     units.readable_str(distance, ad.unit, sig=3))

    try:
        m = component.moveRel(act_mv)
        try:
            m.result(120)
        except KeyboardInterrupt:
            logging.warning("Cancelling relative move of component %s", comp_name)
            m.cancel()
            raise
    except Exception as exc:
        raise IOError("Failed to move component %s by %s: %s" %
                      (comp_name, act_mv, exc))
Exemplo n.º 18
0
 def UpdateHFWLabel(self):
     """ Physical width of the display"""
     if not self._microscope_view:
         return
     hfw = self._microscope_view.mpp.value * self.GetClientSize()[0]
     hfw = units.round_significant(hfw, 4)
     label = u"HFW: %s" % units.readable_str(hfw, "m", sig=3)
     self.bottom_legend.set_hfw_label(label)
Exemplo n.º 19
0
    def _update_label(self, pressure_val):
        """ Set a formatted pressure value as the label of the button """

        str_value = units.readable_str(
            pressure_val, sig=2, unit=self.main_data.chamber.pressure.unit)
        if self.btn.Label != str_value:
            self.btn.Label = str_value
            self.btn.Refresh()
Exemplo n.º 20
0
    def Draw(self, ctx, shift=(0, 0), scale=1.0):

        if self.w_start_pos and self.w_end_pos:
            ctx.save()

            # Important: We need to use the world positions, in order to draw everything at the
            # right scale.
            offset = self.cnvs.get_half_buffer_size()
            b_pos = (self.cnvs.world_to_buffer(self.w_start_pos, offset) +
                     self.cnvs.world_to_buffer(self.w_end_pos, offset))
            b_pos = self._normalize(b_pos)
            self.update_from_buffer(b_pos[:2], b_pos[2:4], shift + (scale,))

            #logging.warn("%s %s", shift, world_to_buffer_pos(shift))
            rect = (b_pos[0] + 0.5, b_pos[1] + 0.5,
                    b_pos[2] - b_pos[0], b_pos[3] - b_pos[1])

            # draws a light black background for the rectangle
            ctx.set_line_width(4)
            ctx.set_source_rgba(0, 0, 0, 0.5)
            ctx.rectangle(*rect)
            ctx.stroke()

            # draws the dotted line
            ctx.set_line_width(2)
            ctx.set_dash([3])
            ctx.set_line_join(cairo.LINE_JOIN_MITER)
            ctx.set_source_rgba(*self.colour)
            ctx.rectangle(*rect)
            ctx.stroke()

            # Label
            if (self.selection_mode in (base.SEL_MODE_EDIT, base.SEL_MODE_CREATE) and
                    self.cnvs.microscope_view):
                w, h = self.cnvs.selection_to_real_size(self.w_start_pos, self.w_end_pos)
                w = units.readable_str(w, 'm', sig=2)
                h = units.readable_str(h, 'm', sig=2)
                size_lbl = u"{} x {}".format(w, h)

                pos = (b_pos[2] + 8, b_pos[3] - 10)

                self.position_label.pos = pos
                self.position_label.text = size_lbl
                self._write_labels(ctx)
            ctx.restore()
Exemplo n.º 21
0
    def Draw(self, ctx):
        # TODO: Both focuses at the same time, or 'snap' to horizontal/vertical on first motion?

        ctx.set_line_width(10)
        ctx.set_line_join(cairo.LINE_JOIN_MITER)
        ctx.set_source_rgba(1.0, 1.0, 1.0, 0.8)

        x, y = self.cnvs.ClientSize

        # Horizontal
        if self.shifts[0]:
            y -= self.margin + (self.line_width // 2)
            middle = x / 2

            shift = self.shifts[0] * 1e6  # typically within µm
            end_x = middle + (middle * (shift / (x / 2)))
            end_x = min(max(self.margin, end_x), x - self.margin)

            ctx.move_to(middle, y)
            ctx.line_to(end_x, y)
            ctx.stroke()

            lbl = "focus %s" % units.readable_str(self.shifts[0], 'm', 2)
            self.focus_label.text = lbl
            self.focus_label.pos = (end_x, y - 15)
            self._write_label(ctx, self.focus_label)

        # Vertical
        if self.shifts[1]:
            x -= self.margin + (self.line_width // 2)
            middle = y / 2

            shift = self.shifts[1] * 1e6  # typically within µm
            end_y = middle - (middle * (shift / (y / 2)))
            end_y = min(max(self.margin, end_y), y - self.margin)

            ctx.move_to(x, middle)
            ctx.line_to(x, end_y)
            ctx.stroke()

            lbl = "focus %s" % units.readable_str(self.shifts[1], 'm', 2)
            self.focus_label.text = lbl
            self.focus_label.pos = (x - 15, end_y)
            self._write_label(ctx, self.focus_label)
Exemplo n.º 22
0
    def Draw(self, ctx):
        # TODO: Both focuses at the same time, or 'snap' to horizontal/vertical on first motion?

        ctx.set_line_width(10)
        ctx.set_line_join(cairo.LINE_JOIN_MITER)
        ctx.set_source_rgba(1.0, 1.0, 1.0, 0.8)

        x, y = self.cnvs.ClientSize

        # Horizontal
        if self.shifts[0]:
            y -= self.margin + (self.line_width // 2)
            middle = x / 2

            shift = self.shifts[0] * 1e6  # typically within µm
            end_x = middle + (middle * (shift / (x / 2)))
            end_x = min(max(self.margin, end_x), x - self.margin)

            ctx.move_to(middle, y)
            ctx.line_to(end_x, y)
            ctx.stroke()

            lbl = "focus %s" % units.readable_str(self.shifts[0], 'm', 2)
            self.focus_label.text = lbl
            self.focus_label.pos = (end_x, y - 15)
            self._write_label(ctx, self.focus_label)

        # Vertical
        if self.shifts[1]:
            x -= self.margin + (self.line_width // 2)
            middle = y / 2

            shift = self.shifts[1] * 1e6  # typically within µm
            end_y = middle - (middle * (shift / (y / 2)))
            end_y = min(max(self.margin, end_y), y - self.margin)

            ctx.move_to(x, middle)
            ctx.line_to(x, end_y)
            ctx.stroke()

            lbl = "focus %s" % units.readable_str(self.shifts[1], 'm', 2)
            self.focus_label.text = lbl
            self.focus_label.pos = (x - 15, end_y)
            self._write_label(ctx, self.focus_label)
Exemplo n.º 23
0
    def on_paint(self, _):
        if self._value_range is None:
            return

        # shared function with the export method
        self._tick_list, self._vtp_ratio = calculate_ticks(
            self._value_range, self.ClientSize, self._orientation,
            self._tick_spacing)
        csize = self.ClientSize

        # Set Font
        font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
        ctx = wx.lib.wxcairo.ContextFromDC(wx.PaintDC(self))
        ctx.select_font_face(font.GetFaceName(), cairo.FONT_SLANT_NORMAL,
                             cairo.FONT_WEIGHT_NORMAL)
        ctx.set_font_size(font.GetPointSize())

        ctx.set_source_rgb(*self.tick_colour)
        ctx.set_line_width(2)
        ctx.set_line_join(cairo.LINE_JOIN_MITER)

        max_width = 0
        prev_lpos = 0 if self._orientation == wx.HORIZONTAL else csize.y

        for i, (pos, val) in enumerate(self._tick_list):
            label = units.readable_str(val, self.unit, 3)
            _, _, lbl_width, lbl_height, _, _ = ctx.text_extents(label)

            if self._orientation == wx.HORIZONTAL:
                lpos = pos - (lbl_width // 2)
                lpos = max(min(lpos, csize.x - lbl_width - 2), 2)
                # print (i, prev_right, lpos)
                if prev_lpos < lpos:
                    ctx.move_to(lpos, lbl_height + 8)
                    ctx.show_text(label)
                    ctx.move_to(pos, 5)
                    ctx.line_to(pos, 0)
                prev_lpos = lpos + lbl_width
            else:
                max_width = max(max_width, lbl_width)
                lpos = pos + (lbl_height // 2)
                lpos = max(min(lpos, csize.y), 2)

                if prev_lpos >= lpos + 20 or i == 0:
                    ctx.move_to(csize.x - lbl_width - 9, lpos)
                    ctx.show_text(label)
                    ctx.move_to(csize.x - 5, pos)
                    ctx.line_to(csize.x, pos)
                prev_lpos = lpos + lbl_height

            ctx.stroke()

        if self._orientation == wx.VERTICAL and max_width != self._max_tick_width:
            self._max_tick_width = max_width
            self.SetMinSize((self._max_tick_width + 14, -1))
            self.Parent.GetSizer().Layout()
Exemplo n.º 24
0
    def Draw(self, ctx, shift=(0, 0), scale=1.0):

        if self.w_start_pos and self.w_end_pos:
            offset = [v // 2 for v in self.cnvs._bmp_buffer_size]
            b_pos = (self.cnvs.world_to_buffer(self.w_start_pos, offset) +
                     self.cnvs.world_to_buffer(self.w_end_pos, offset))
            b_pos = util.normalize_rect(b_pos)
            self.update_from_buffer(b_pos[:2], b_pos[2:4], shift + (scale,))

            #logging.warn("%s %s", shift, world_to_buffer_pos(shift))
            rect = (b_pos[0] + 0.5, b_pos[1] + 0.5,
                    b_pos[2] - b_pos[0], b_pos[3] - b_pos[1])

            # draws a light black background for the rectangle
            ctx.set_line_width(2.5)
            ctx.set_source_rgba(0, 0, 0, 0.5)
            ctx.rectangle(*rect)
            ctx.stroke()

            # draws the dotted line
            ctx.set_line_width(2)
            ctx.set_dash([3,])
            ctx.set_line_join(cairo.LINE_JOIN_MITER)
            ctx.set_source_rgba(*self.colour)
            ctx.rectangle(*rect)
            ctx.stroke()

            # Label

            if (self.dragging or self.edit) and self.cnvs.microscope_view:
                w, h = self.cnvs.selection_to_real_size(
                                            self.w_start_pos,
                                            self.w_end_pos
                )
                w = units.readable_str(w, 'm', sig=2)
                h = units.readable_str(h, 'm', sig=2)
                size_lbl = u"{} x {}".format(w, h)

                pos = (b_pos[2] + 10, b_pos[3] + 5)

                self.position_label.pos = pos
                self.position_label.text = size_lbl
                self._write_labels(ctx)
Exemplo n.º 25
0
 def _display_pretty(self):
     if self._number_value is None:
         str_val = u""
     else:
         str_val = units.readable_str(self._number_value, self.unit, self.accuracy)
     # Get the length of the number (string length, minus the unit length)
     number_length = len(str_val.rstrip(string.ascii_letters + u" µ"))
     wx.TextCtrl.ChangeValue(self, str_val)
     # Select the number value
     wx.CallAfter(self.SetSelection, number_length, number_length)
Exemplo n.º 26
0
    def draw(self, ctx):
        ctx.set_line_width(self.line_width)
        ctx.set_dash([3])
        ctx.set_line_join(cairo.LINE_JOIN_MITER)
        ctx.set_source_rgba(*self.colour)

        if self.val.value is not None:
            val = self.val.value
            if self.map_y_from_x:
                # Maps Y and also snap X to the closest X value in the data
                val = self.cnvs.val_x_to_val(val[0])
            v_pos = self.cnvs.val_to_pos(val)

            self.x_label = units.readable_str(val[0], self.cnvs.unit_x, 3)
            self.y_label = units.readable_str(val[1], self.cnvs.unit_y, 3)

            # v_posx, v_posy = self.v_pos.value
            if self.orientation & self.VERTICAL:
                ctx.move_to(v_pos[0], 0)
                ctx.line_to(v_pos[0], self.cnvs.ClientSize.y)
                ctx.stroke()

            if self.orientation & self.HORIZONTAL:
                ctx.move_to(0, v_pos[1])
                ctx.line_to(self.cnvs.ClientSize.x, v_pos[1])
                ctx.stroke()

            if self.x_label.text:
                self.x_label.pos = (v_pos[0] + 5, self.cnvs.ClientSize.y)
                self._write_label(ctx, self.x_label)

            if self.y_label.text:
                yp = max(0, v_pos[1] - 5)  # Padding from line
                # Increase bottom margin if x label is close
                label_padding = 30 if v_pos[0] < 50 else 0
                yn = min(self.view_height - label_padding, yp)
                self.y_label.pos = (2, yn)
                self._write_label(ctx, self.y_label)

            r, g, b, a = conversion.change_brightness(self.colour, -0.2)
            ctx.set_source_rgba(r, g, b, 0.5)
            ctx.arc(v_pos[0], v_pos[1], 5.5, 0, 2 * math.pi)
            ctx.fill()
Exemplo n.º 27
0
    def on_paint(self, _):
        if self._value_range is None:
            return

        # shared function with the export method
        self._tick_list, self._vtp_ratio = calculate_ticks(self._value_range, self.ClientSize, self._orientation, self._tick_spacing)
        csize = self.ClientSize

        # Set Font
        font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
        ctx = wx.lib.wxcairo.ContextFromDC(wx.PaintDC(self))
        ctx.select_font_face(font.GetFaceName(), cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
        ctx.set_font_size(font.GetPointSize())

        ctx.set_source_rgb(*self.tick_colour)
        ctx.set_line_width(2)
        ctx.set_line_join(cairo.LINE_JOIN_MITER)

        max_width = 0
        prev_lpos = 0 if self._orientation == wx.HORIZONTAL else csize.y

        for i, (pos, val) in enumerate(self._tick_list):
            label = units.readable_str(val, self.unit, 3)
            _, _, lbl_width, lbl_height, _, _ = ctx.text_extents(label)

            if self._orientation == wx.HORIZONTAL:
                lpos = pos - (lbl_width // 2)
                lpos = max(min(lpos, csize.x - lbl_width - 2), 2)
                # print (i, prev_right, lpos)
                if prev_lpos < lpos:
                    ctx.move_to(lpos, lbl_height + 8)
                    ctx.show_text(label)
                    ctx.move_to(pos, 5)
                    ctx.line_to(pos, 0)
                prev_lpos = lpos + lbl_width
            else:
                max_width = max(max_width, lbl_width)
                lpos = pos + (lbl_height // 2)
                lpos = max(min(lpos, csize.y), 2)

                if prev_lpos >= lpos + 20 or i == 0:
                    ctx.move_to(csize.x - lbl_width - 9, lpos)
                    ctx.show_text(label)
                    ctx.move_to(csize.x - 5, pos)
                    ctx.line_to(csize.x, pos)
                prev_lpos = lpos + lbl_height

            ctx.stroke()

        if self._orientation == wx.VERTICAL and max_width != self._max_tick_width:
            self._max_tick_width = max_width
            self.SetMinSize((self._max_tick_width + 14, -1))
            self.Parent.GetSizer().Layout()
Exemplo n.º 28
0
 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)
Exemplo n.º 29
0
 def cb_set(value, ctrl=value_ctrl, unit=unit):
     for i in range(ctrl.Count):
         if ((isinstance(value, float) and util.almost_equal(ctrl.GetClientData(i), value)) or
             ctrl.GetClientData(i) == value):
             logging.debug("Setting ComboBox value to %s", ctrl.Items[i])
             ctrl.SetSelection(i)
             break
     else:
         logging.warning("No existing label found for value %s", value)
         # entering value as free text
         txt = readable_str(value, unit)
         ctrl.SetValue(txt)
Exemplo n.º 30
0
    def on_paint(self, evt=None):

        if not hasattr(self.Parent.canvas,
                       'has_data') or not self.Parent.canvas.has_data():
            self.clear()
            return

        ctx = wx.lib.wxcairo.ContextFromDC(wx.PaintDC(self))

        font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
        ctx.select_font_face(font.GetFaceName(), cairo.FONT_SLANT_NORMAL,
                             cairo.FONT_WEIGHT_NORMAL)
        ctx.set_font_size(font.GetPointSize())

        # TODO: only put label for some of the ticks
        if not self.ticks:
            self.calc_ticks(ctx)

        ctx.set_source_rgb(*self.tick_colour)

        ctx.set_line_width(2)
        ctx.set_line_join(cairo.LINE_JOIN_MITER)

        max_width = 0

        for i, (pos, val) in enumerate(self.ticks):
            label = units.readable_str(val, self.unit, 3)
            _, _, lbl_width, lbl_height, _, _ = ctx.text_extents(label)

            if self.orientation == wx.HORIZONTAL:
                lpos = pos - (lbl_width / 2)
                lpos = max(min(lpos, self.ClientSize.x - lbl_width - 2), 2)
                ctx.move_to(lpos, lbl_height + 8)
                ctx.show_text(label)
                ctx.move_to(pos, 5)
                ctx.line_to(pos, 0)
            else:
                max_width = max(max_width, lbl_width)

                lpos = pos + (lbl_height / 2)
                lpos = max(min(lpos, self.ClientSize.y), 2)
                ctx.move_to(self.ClientSize.x - lbl_width - 9, lpos)
                ctx.show_text(label)
                ctx.move_to(self.ClientSize.x - 5, pos)
                ctx.line_to(self.ClientSize.x, pos)

            ctx.stroke()

        if self.orientation == wx.VERTICAL and max_width != self.max_tick_width:
            self.max_tick_width = max_width
            self.SetMinSize((self.max_tick_width + 14, -1))
            self.Parent.GetSizer().Layout()
Exemplo n.º 31
0
    def UpdateMagnification(self):
        # Total magnification
        mag = self._mpp_screen / self._microscope_view.mpp.value
        label = u"Mag: × %s" % units.readable_str(units.round_significant(mag, 3))

        # Gather all different image mpp values
        mpps = set()
        for im in self._microscope_view.stream_tree.getImages():
            try:
                mpps.add(im.metadata[model.MD_PIXEL_SIZE][0])
            except KeyError:
                pass

        # If there's only one mpp value (i.e. there's only one image, or they
        # all have the same mpp value), indicate the digital zoom.
        if len(mpps) == 1:
            mpp_im = mpps.pop()
#             mag_im = self._mpp_screen / mpp_im  # as if 1 im.px == 1 sc.px
            mag_dig = mpp_im / self._microscope_view.mpp.value
            label += u" (Digital: × %s)" % units.readable_str(units.round_significant(mag_dig, 2))

        self.bottom_legend.set_mag_label(label)
Exemplo n.º 32
0
def move(comp_name, moves, check_distance=True):
    """
    move (relatively) the axis of the given component by the specified amount of µm
    comp_name (str): name of the component
    check_distance (bool): if the axis is in meters, check that the move is not
      too big.
    moves (dict str -> str): axis -> distance (as text, and in µm for distances)
    """
    # for safety reason, we use µm instead of meters, as it's harder to type a
    # huge distance
    component = get_component(comp_name)

    act_mv = {} # axis -> value
    for axis_name, str_distance in moves.items():
        try:
            if axis_name not in component.axes:
                raise ValueError("Actuator %s has no axis '%s'" % (comp_name, axis_name))
            ad = component.axes[axis_name]
        except (TypeError, AttributeError):
            raise ValueError("Component %s is not an actuator" % comp_name)

        if ad.unit == "m":
            try:
                # Use convert_to_object() to allow typing negative values with e:
                # -1e-6 => '!!float -1.0e-6'. It's not very nice, but does work.
                distance = float(convert_to_object(str_distance)) * 1e-6  # µm -> m
            except ValueError:
                raise ValueError("Distance '%s' cannot be converted to a number" %
                                 str_distance)

            if check_distance and abs(distance) > MAX_DISTANCE:
                raise IOError("Distance of %f m is too big (> %f m), use '--big-distance' to allow the move." %
                              (abs(distance), MAX_DISTANCE))
        else:
            distance = convert_to_object(str_distance)

        act_mv[axis_name] = distance
        logging.info(u"Will move %s.%s by %s", comp_name, axis_name,
                     units.readable_str(distance, ad.unit, sig=3))

    try:
        m = component.moveRel(act_mv)
        try:
            m.result(120)
        except KeyboardInterrupt:
            logging.warning("Cancelling relative move of component %s", comp_name)
            m.cancel()
            raise
    except Exception as exc:
        raise IOError("Failed to move component %s by %s: %s" %
                      (comp_name, act_mv, exc))
Exemplo n.º 33
0
    def on_paint(self, evt=None):

        if not hasattr(self.Parent.canvas, 'has_data') or not self.Parent.canvas.has_data():
            self.clear()
            return

        ctx = wx.lib.wxcairo.ContextFromDC(wx.PaintDC(self))

        font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
        ctx.select_font_face(font.GetFaceName(), cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
        ctx.set_font_size(font.GetPointSize())

        # TODO: only put label for some of the ticks
        if not self.ticks:
            self.calc_ticks(ctx)

        ctx.set_source_rgb(*self.tick_colour)

        ctx.set_line_width(2)
        ctx.set_line_join(cairo.LINE_JOIN_MITER)

        max_width = 0

        for i, (pos, val) in enumerate(self.ticks):
            label = units.readable_str(val, self.unit, 3)
            _, _, lbl_width, lbl_height, _, _ = ctx.text_extents(label)

            if self.orientation == wx.HORIZONTAL:
                lpos = pos - (lbl_width / 2)
                lpos = max(min(lpos, self.ClientSize.x - lbl_width - 2), 2)
                ctx.move_to(lpos, lbl_height + 8)
                ctx.show_text(label)
                ctx.move_to(pos, 5)
                ctx.line_to(pos, 0)
            else:
                max_width = max(max_width, lbl_width)

                lpos = pos + (lbl_height / 2)
                lpos = max(min(lpos, self.ClientSize.y), 2)
                ctx.move_to(self.ClientSize.x - lbl_width - 9, lpos)
                ctx.show_text(label)
                ctx.move_to(self.ClientSize.x - 5, pos)
                ctx.line_to(self.ClientSize.x, pos)

            ctx.stroke()

        if self.orientation == wx.VERTICAL and max_width != self.max_tick_width:
            self.max_tick_width = max_width
            self.SetMinSize((self.max_tick_width + 14, -1))
            self.Parent.GetSizer().Layout()
Exemplo n.º 34
0
 def cb_set(value, ctrl=value_ctrl, u=unit, acc=accuracy):
     for i in range(ctrl.Count):
         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 = readable_str(value, u, sig=acc)
         ctrl.SetValue(txt)
Exemplo n.º 35
0
 def _display_pretty(self):
     if self._number_value is None:
         str_val = u""
     elif self._number_value == 0 and self.key_step and self.unit not in units.IGNORE_UNITS:
         # Special case with 0: readable_str return just "0 unit", without
         # prefix. This is technically correct, but quite inconvenient and
         # a little strange when the typical value has a prefix (eg, nm, kV).
         # => use prefix of key_step (as it's a "small value")
         _, prefix = units.get_si_scale(self.key_step)
         str_val = "0 %s%s" % (prefix, self.unit)
     else:
         str_val = units.readable_str(self._number_value, self.unit, self.accuracy)
     # Get the length of the number (string length, minus the unit length)
     number_length = len(str_val.rstrip(string.ascii_letters + u" µ"))
     wx.TextCtrl.ChangeValue(self, str_val)
     # Select the number value
     wx.CallAfter(self.SetSelection, number_length, number_length)
Exemplo n.º 36
0
 def _display_pretty(self):
     if self._number_value is None:
         str_val = u""
     elif self._number_value == 0 and self.key_step and self.unit not in units.IGNORE_UNITS:
         # Special case with 0: readable_str return just "0 unit", without
         # prefix. This is technically correct, but quite inconvenient and
         # a little strange when the typical value has a prefix (eg, nm, kV).
         # => use prefix of key_step (as it's a "small value")
         _, prefix = units.get_si_scale(self.key_step)
         str_val = "0 %s%s" % (prefix, self.unit)
     else:
         str_val = units.readable_str(self._number_value, self.unit,
                                      self.accuracy)
     # Get the length of the number (string length, minus the unit length)
     number_length = len(str_val.rstrip(string.ascii_letters + u" µ"))
     wx.TextCtrl.ChangeValue(self, str_val)
     # Select the number value
     wx.CallAfter(self.SetSelection, number_length, number_length)
Exemplo n.º 37
0
    def on_paint(self, event=None):

        if not self.Parent.canvas.has_data():
            self.clear()
            return

        if not self.ticks:
            self.calc_ticks()

        ctx = wx.lib.wxcairo.ContextFromDC(wx.PaintDC(self))

        font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
        ctx.select_font_face(
                font.GetFaceName(),
                cairo.FONT_SLANT_NORMAL,
                cairo.FONT_WEIGHT_NORMAL
        )
        ctx.set_font_size(font.GetPointSize())

        ctx.set_source_rgb(*self.tick_colour)

        ctx.set_line_width(2)
        ctx.set_line_join(cairo.LINE_JOIN_MITER)

        for i, (pos, val) in enumerate(self.ticks):
            label = units.readable_str(val, self.unit, 3)
            _, _, width, height, _, _ = ctx.text_extents(label)

            if self.orientation == self.HORIZONTAL:
                lpos = pos - (width / 2)
                lpos = max(min(lpos, self.ClientSize.x - width - 2), 2)
                ctx.move_to(lpos, height + 8)
                ctx.show_text(label)
                ctx.move_to(pos, 5)
                ctx.line_to(pos, 0)
            else:
                lpos = pos + (height / 2)
                lpos = max(min(lpos, self.ClientSize.y - height - 2), 2)
                ctx.move_to(self.ClientSize.x - width - 9, lpos)
                ctx.show_text(label)
                ctx.move_to(self.ClientSize.x - 5, pos)
                ctx.line_to(self.ClientSize.x, pos)

            ctx.stroke()
Exemplo n.º 38
0
    def on_paint(self, event=None):

        if not self.Parent.canvas.has_data():
            self.clear()
            return

        if not self.ticks:
            self.calc_ticks()

        ctx = wx.lib.wxcairo.ContextFromDC(wx.PaintDC(self))

        font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
        ctx.select_font_face(font.GetFaceName(), cairo.FONT_SLANT_NORMAL,
                             cairo.FONT_WEIGHT_NORMAL)
        ctx.set_font_size(font.GetPointSize())

        ctx.set_source_rgb(*self.tick_colour)

        ctx.set_line_width(2)
        ctx.set_line_join(cairo.LINE_JOIN_MITER)

        for i, (pos, val) in enumerate(self.ticks):
            label = units.readable_str(val, self.unit, 3)
            _, _, width, height, _, _ = ctx.text_extents(label)

            if self.orientation == self.HORIZONTAL:
                lpos = pos - (width / 2)
                lpos = max(min(lpos, self.ClientSize.x - width - 2), 2)
                ctx.move_to(lpos, height + 8)
                ctx.show_text(label)
                ctx.move_to(pos, 5)
                ctx.line_to(pos, 0)
            else:
                lpos = pos + (height / 2)
                lpos = max(min(lpos, self.ClientSize.y - height - 2), 2)
                ctx.move_to(self.ClientSize.x - width - 9, lpos)
                ctx.show_text(label)
                ctx.move_to(self.ClientSize.x - 5, pos)
                ctx.line_to(self.ClientSize.x, pos)

            ctx.stroke()
Exemplo n.º 39
0
    def _on_fa_done(self, future):
        logging.debug("End of overlay procedure")
        main_data = self._main_data_model
        try:
            trans_val, cor_md = future.result()
            # The magic: save the correction metadata straight into the CCD
            main_data.ccd.updateMetadata(cor_md)
        except CancelledError:
            self._main_frame.lbl_fine_align.Label = "Cancelled"
        except Exception:
            logging.warning("Failed to run the fine alignment, a report "
                            "should be available in ~/odemis-overlay-report.")
            self._main_frame.lbl_fine_align.Label = "Failed"
        else:
            self._main_frame.lbl_fine_align.Label = "Successful"
            self._main_frame.menu_item_reset_finealign.Enable(True)
            # Temporary info until the GUI can actually rotate the images
            if model.MD_ROTATION_COR in cor_md:
                rot = math.degrees(cor_md[model.MD_ROTATION_COR])
                # the worse is the rotation, the longer it's displayed
                timeout = max(2, min(abs(rot), 10))
                Message.show_message(
                    self._main_frame,
                    u"Rotation applied: %s" %
                    (units.readable_str(rot, unit="°", sig=3)),
                    timeout=timeout)
                logging.warning(
                    "Fine alignment computed rotation needed of %f°", rot)

        # As the CCD image might have different pixel size, force to fit
        self._main_frame.vp_align_ccd.canvas.fit_view_to_next_image = True

        main_data.is_acquiring.value = False
        self._main_frame.btn_fine_align.Bind(wx.EVT_BUTTON,
                                             self._on_fine_align)
        self._main_frame.btn_fine_align.Label = self._fa_btn_label
        self._resume()

        self._main_frame.lbl_fine_align.Show()
        self._main_frame.gauge_fine_align.Hide()
        self._sizer.Layout()
Exemplo n.º 40
0
def move(comp_name, axis_name, str_distance):
    """
    move (relatively) the axis of the given component by the specified amount of µm
    """
    # for safety reason, we use µm instead of meters, as it's harder to type a
    # huge distance
    component = get_component(comp_name)

    try:
        if axis_name not in component.axes:
            raise ValueError("Actuator %s has no axis '%s'" %
                             (comp_name, axis_name))
        ad = component.axes[axis_name]
    except (TypeError, AttributeError):
        raise ValueError("Component %s is not an actuator" % comp_name)

    if ad.unit == "m":
        try:
            distance = float(str_distance) * 1e-6  # µm -> m
        except ValueError:
            raise ValueError("Distance '%s' cannot be converted to a number" %
                             str_distance)

        if abs(distance) > MAX_DISTANCE:
            raise IOError("Distance of %f m is too big (> %f m)" %
                          (abs(distance), MAX_DISTANCE))
    else:
        distance = convertToObject(str_distance)

    try:
        logging.info(u"Will move %s.%s by %s", comp_name, axis_name,
                     units.readable_str(distance, ad.unit, sig=3))
    except Exception:
        pass

    try:
        m = component.moveRel({axis_name: distance})
        m.result()
    except Exception as exc:
        raise IOError("Failed to move axis %s of component %s: %s" %
                      (axis_name, comp_name, exc))
Exemplo n.º 41
0
def move_abs(comp_name, axis_name, str_position):
    """
    move (in absolute) the axis of the given component to the specified position in µm
    """
    component = get_component(comp_name)

    try:
        if axis_name not in component.axes:
            raise ValueError("Actuator %s has no axis '%s'" %
                             (comp_name, axis_name))
        ad = component.axes[axis_name]
    except (TypeError, AttributeError):
        raise ValueError("Component %s is not an actuator" % comp_name)

    if ad.unit == "m":
        try:
            position = float(str_position)
        except ValueError:
            raise ValueError("Position '%s' cannot be converted to a number" %
                             str_position)

        # compare to the current position, to see if the new position sounds reasonable
        cur_pos = component.position.value[axis_name]
        if abs(cur_pos - position) > MAX_DISTANCE:
            raise IOError("Distance of %f m is too big (> %f m)" %
                          (abs(cur_pos - position), MAX_DISTANCE))
    else:
        position = convertToObject(str_position)

    try:
        logging.info(u"Will move %s.%s to %s", comp_name, axis_name,
                     units.readable_str(position, ad.unit, sig=3))
    except Exception:
        pass

    try:
        m = component.moveAbs({axis_name: position})
        m.result()
    except Exception as exc:
        raise IOError("Failed to move axis %s of component %s: %s" %
                      (axis_name, comp_name, exc))
Exemplo n.º 42
0
    def _on_fa_done(self, future):
        logging.debug("End of overlay procedure")
        main_data = self._main_data_model
        try:
            trans_val, cor_md = future.result()
            # The magic: save the correction metadata straight into the CCD
            main_data.ccd.updateMetadata(cor_md)
        except CancelledError:
            self._main_frame.lbl_fine_align.Label = "Cancelled"
        except Exception:
            logging.warning("Failed to run the fine alignment, a report "
                            "should be available in ~/odemis-overlay-report.")
            self._main_frame.lbl_fine_align.Label = "Failed"
        else:
            self._main_frame.lbl_fine_align.Label = "Successful"
            self._main_frame.menu_item_reset_finealign.Enable(True)
            # Temporary info until the GUI can actually rotate the images
            if model.MD_ROTATION_COR in cor_md:
                rot = math.degrees(cor_md[model.MD_ROTATION_COR])
                # the worse is the rotation, the longer it's displayed
                timeout = max(2, min(abs(rot), 10))
                Message.show_message(
                    self._main_frame,
                    u"Rotation applied: %s" % (units.readable_str(rot, unit="°", sig=3)),
                    timeout=timeout
                )
                logging.warning("Fine alignment computed rotation needed of %f°",
                                rot)

        # As the CCD image might have different pixel size, force to fit
        self._main_frame.vp_align_ccd.canvas.fit_view_to_next_image = True

        main_data.is_acquiring.value = False
        self._main_frame.btn_fine_align.Bind(wx.EVT_BUTTON, self._on_fine_align)
        self._main_frame.btn_fine_align.Label = self._fa_btn_label
        self._resume()

        self._main_frame.lbl_fine_align.Show()
        self._main_frame.gauge_fine_align.Hide()
        self._sizer.Layout()
Exemplo n.º 43
0
def move(comp_name, axis_name, str_distance):
    """
    move (relatively) the axis of the given component by the specified amount of µm
    """
    # for safety reason, we use µm instead of meters, as it's harder to type a
    # huge distance
    component = get_component(comp_name)

    try:
        if axis_name not in component.axes:
            raise ValueError("Actuator %s has no axis '%s'" % (comp_name, axis_name))
        ad = component.axes[axis_name]
    except (TypeError, AttributeError):
        raise ValueError("Component %s is not an actuator" % comp_name)

    if ad.unit == "m":
        try:
            distance = float(str_distance) * 1e-6 # µm -> m
        except ValueError:
            raise ValueError("Distance '%s' cannot be converted to a number" %
                             str_distance)

        if abs(distance) > MAX_DISTANCE:
            raise IOError("Distance of %f m is too big (> %f m)" %
                          (abs(distance), MAX_DISTANCE))
    else:
        distance = convertToObject(str_distance)

    try:
        logging.info(u"Will move %s.%s by %s", comp_name, axis_name,
                     units.readable_str(distance, ad.unit, sig=3))
    except Exception:
        pass

    try:
        m = component.moveRel({axis_name: distance})
        m.result()
    except Exception as exc:
        raise IOError("Failed to move axis %s of component %s: %s" %
                      (axis_name, comp_name, exc))
Exemplo n.º 44
0
    def Draw(self, dc):
        # return self.DrawGC(dc)
        nod = self.nod
        vmiddle = self.GetClientSize()[1] // 2

        background_col = self.Parent.GetBackgroundColour()
        foreground_col = self.Parent.GetForegroundColour()

        dc.SetBackgroundMode(wx.BRUSHSTYLE_SOLID)
        dc.SetBackground(wx.Brush(background_col))
        dc.Clear()

        if not self.mpp:  # unknown mpp => blank
            return

        dc.SetFont(self.GetFont())
        dc.SetTextForeground(foreground_col)
        dc.SetTextBackground(background_col)

        length, actual = self.GetLineWidth(dc)

        # Draw the text below
        charSize = dc.GetTextExtent("M")
        height = self.gap + charSize[1] + self.nod
        main_line_y = vmiddle - (height // 2) + self.nod

        dc.DrawText(units.readable_str(actual, "m", sig=2), 0,
                    main_line_y + self.gap)

        # Draw the scale itself
        pen = wx.Pen(foreground_col, self.line_width)
        pen.Cap = wx.CAP_PROJECTING  # how to draw the border of the lines
        dc.SetPen(pen)

        # main line
        lines = [(0, main_line_y, length, main_line_y)]
        # nods at each end
        lines += [(0, main_line_y - nod, 0, main_line_y)]
        lines += [(length, main_line_y - nod, length, main_line_y)]
        dc.DrawLineList(lines)
Exemplo n.º 45
0
    def Draw(self, dc):
        # return self.DrawGC(dc)
        nod = self.nod
        vmiddle = self.GetClientSize()[1] // 2

        background_col = self.Parent.GetBackgroundColour()
        foreground_col = self.Parent.GetForegroundColour()

        dc.SetBackgroundMode(wx.BRUSHSTYLE_SOLID)
        dc.SetBackground(wx.Brush(background_col))
        dc.Clear()

        if not self.mpp: # unknown mpp => blank
            return

        dc.SetFont(self.GetFont())
        dc.SetTextForeground(foreground_col)
        dc.SetTextBackground(background_col)

        length, actual = self.GetLineWidth(dc)

        # Draw the text below
        charSize = dc.GetTextExtent("M")
        height = self.gap + charSize[1] + self.nod
        main_line_y = vmiddle - (height // 2) + self.nod

        dc.DrawText(units.readable_str(actual, "m", sig=2),
                    0, main_line_y + self.gap)

        # Draw the scale itself
        pen = wx.Pen(foreground_col, self.line_width)
        pen.Cap = wx.CAP_PROJECTING # how to draw the border of the lines
        dc.SetPen(pen)

        # main line
        lines = [(0, main_line_y, length, main_line_y)]
        # nods at each end
        lines += [(0, main_line_y - nod, 0, main_line_y)]
        lines += [(length, main_line_y - nod, length, main_line_y)]
        dc.DrawLineList(lines)
Exemplo n.º 46
0
    def UpdateMagnification(self):
        # TODO: shall we use the real density of the screen?
        # We could use real density but how much important is it?
        mppScreen = 0.00025  # 0.25 mm/px
        label = u"Mag: "

        # three possibilities:
        # * no image => total mag (using current mpp)
        # * all images have same mpp => mag instrument * mag digital
        # * >1 mpp => total mag

        # get all the mpps
        mpps = set()
        for im in self._microscope_view.stream_tree.getImages():
            try:
                mpps.add(im.metadata[model.MD_PIXEL_SIZE][0])
            except KeyError:
                pass

        if len(mpps) == 1:
            # two magnifications
            im_mpp = mpps.pop()
            magIm = mppScreen / im_mpp  # as if 1 im.px == 1 sc.px
            if magIm >= 1:
                label += u"×" + units.readable_str(
                    units.round_significant(magIm, 3))
            else:
                label += u"÷" + units.readable_str(
                    units.round_significant(1.0 / magIm, 3))
            magDig = im_mpp / self._microscope_view.mpp.value
            if magDig >= 1:
                label += u" ×" + units.readable_str(
                    units.round_significant(magDig, 3))
            else:
                label += u" ÷" + units.readable_str(
                    units.round_significant(1.0 / magDig, 3))
        else:
            # one magnification
            mag = mppScreen / self._microscope_view.mpp.value
            if mag >= 1:
                label += u"×" + units.readable_str(
                    units.round_significant(mag, 3))
            else:
                label += u"÷" + units.readable_str(
                    units.round_significant(1.0 / mag, 3))

        self.legend.set_mag_label(label)
Exemplo n.º 47
0
def move_abs(comp_name, axis_name, str_position):
    """
    move (in absolute) the axis of the given component to the specified position in µm
    """
    component = get_component(comp_name)

    try:
        if axis_name not in component.axes:
            raise ValueError("Actuator %s has no axis '%s'" % (comp_name, axis_name))
        ad = component.axes[axis_name]
    except (TypeError, AttributeError):
        raise ValueError("Component %s is not an actuator" % comp_name)

    if ad.unit == "m":
        try:
            position = float(str_position)
        except ValueError:
            raise ValueError("Position '%s' cannot be converted to a number" % str_position)

        # compare to the current position, to see if the new position sounds reasonable
        cur_pos = component.position.value[axis_name]
        if abs(cur_pos - position) > MAX_DISTANCE:
            raise IOError("Distance of %f m is too big (> %f m)" %
                           (abs(cur_pos - position), MAX_DISTANCE))
    else:
        position = convertToObject(str_position)

    try:
        logging.info(u"Will move %s.%s to %s", comp_name, axis_name,
                     units.readable_str(position, ad.unit, sig=3))
    except Exception:
        pass

    try:
        m = component.moveAbs({axis_name: position})
        m.result()
    except Exception as exc:
        raise IOError("Failed to move axis %s of component %s: %s" %
                      (axis_name, comp_name, exc))
Exemplo n.º 48
0
 def test_readable_str(self):
     #         (input) (expected output)
     values = [
         ((1.0, None), "1"),
         ((1, None), "1"),
         ((-1.234, "m"), "-1.234 m"),
         ((-1234, "g"), "-1.234 kg"),
         ((160000, None), "160000"),
         ((-1600, "N"), "-1.6 kN"),
         ((-1601, "N", 3), "-1.6 kN"),  # sig=3
         ((0.0001236, None), "0.0001236"),
         ((0.0012, "V"), "1.2 mV"),
         ((200e-6, "m"), u"200 µm"),
         ((0.0, "m"), "0 m"),
         (([1500, 1200, 150], None), "1500 x 1200 x 150"),
         (([0.0001236, 0.00014], "m"), u"123.6 x 140 µm"),
         (([0.0001236, 12.0], "m"), "0.0001236 x 12 m"),
         (([1200,
            1000], "px"), "1200 x 1000 px"),  # special non-prefix unit
     ]
     for (i, eo) in values:
         o = units.readable_str(*i)
         self.assertEquals(o, eo,
                           u"%s is '%s' while expected '%s'" % (i, o, eo))
Exemplo n.º 49
0
    def UpdateMagnification(self):
        # TODO: shall we use the real density of the screen?
        # We could use real density but how much important is it?
        mppScreen = 0.00025 # 0.25 mm/px
        label = u"Mag: "

        # three possibilities:
        # * no image => total mag (using current mpp)
        # * all images have same mpp => mag instrument * mag digital
        # * >1 mpp => total mag

        # get all the mpps
        mpps = set()
        for im in self._microscope_view.stream_tree.getImages():
            try:
                mpps.add(im.metadata[model.MD_PIXEL_SIZE][0])
            except KeyError:
                pass

        if len(mpps) == 1:
            # two magnifications
            im_mpp = mpps.pop()
            magIm = mppScreen / im_mpp # as if 1 im.px == 1 sc.px
            if magIm >= 1:
                label += u"×" + units.readable_str(units.round_significant(magIm, 3))
            else:
                label += u"÷" + units.readable_str(units.round_significant(1.0 / magIm, 3))
            magDig = im_mpp / self._microscope_view.mpp.value
            if magDig >= 1:
                label += u" ×" + units.readable_str(units.round_significant(magDig, 3))
            else:
                label += u" ÷" + units.readable_str(units.round_significant(1.0 / magDig, 3))
        else:
            # one magnification
            mag = mppScreen / self._microscope_view.mpp.value
            if mag >= 1:
                label += u"×" + units.readable_str(units.round_significant(mag, 3))
            else:
                label += u"÷" + units.readable_str(units.round_significant(1.0 / mag, 3))

        self.legend.set_mag_label(label)
Exemplo n.º 50
0
    def _runAcquisition(self, future):
        self._data = []
        self._md = {}

        wls = self.startWavelength.value
        wle = self.endWavelength.value
        res = self.numberOfPixels.value
        dt = self.dwellTime.value
        trig = self._detector.softwareTrigger
        df = self._detector.data

        # Prepare the hardware
        self._emitter.resolution.value = (1, 1)  # Force one pixel only
        self._emitter.translation.value = self.emtTranslation.value
        self._emitter.dwellTime.value = dt

        df.synchronizedOn(trig)
        df.subscribe(self._on_mchr_data)

        wllist = []
        if wle == wls:
            res = 1

        if res <= 1:
            res = 1
            wli = 0
        else:
            wli = (wle - wls) / (res - 1)

        try:
            for i in range(res):
                left = (res - i) * (dt + 0.05)
                future.set_progress(end=time.time() + left)

                cwl = wls + i * wli  # requested value
                self._sgr.moveAbs({"wavelength": cwl}).result()
                if future._acq_state == CANCELLED:
                    raise CancelledError()
                cwl = self._sgr.position.value["wavelength"]  # actual value
                logging.info("Acquiring point %d/%d @ %s", i + 1, res,
                             units.readable_str(cwl, unit="m", sig=3))

                self._pt_acq.clear()
                trig.notify()
                if not self._pt_acq.wait(dt * 5 + 1):
                    raise IOError("Timeout waiting for the data")
                if future._acq_state == CANCELLED:
                    raise CancelledError()
                wllist.append(cwl)

            # Done
            df.unsubscribe(self._on_mchr_data)
            df.synchronizedOn(None)

            # Convert the sequence of data into one spectrum in a DataArray

            if wls > wle:  # went backward? => sort back the spectrum
                logging.debug("Inversing spectrum as acquisition went from %g to %g m", wls, wls)
                self._data.reverse()
                wllist.reverse()

            na = numpy.array(self._data)  # keeps the dtype
            na.shape += (1, 1, 1, 1)  # make it 5th dim to indicate a channel
            md = self._md
            md[model.MD_WL_LIST] = wllist
            if model.MD_OUT_WL in md:
                # The MD_OUT_WL on the monochromator contains the current cw, which we don't want
                del md[model.MD_OUT_WL]

            # MD_POS should already be at the correct position (from the e-beam metadata)

            # MD_PIXEL_SIZE is not meaningful but handy for the display in Odemis
            # (it's the size of the square on top of the SEM survey => BIG!)
            sempxs = self._emitter.pixelSize.value
            md[model.MD_PIXEL_SIZE] = (sempxs[0] * 50, sempxs[1] * 50)

            spec = model.DataArray(na, md)

            with future._acq_lock:
                if future._acq_state == CANCELLED:
                    raise CancelledError()
                future._acq_state = FINISHED

            return [spec]

        except CancelledError:
            raise  # Just don't log the exception
        except Exception:
            logging.exception("Failure during monochromator scan")
        finally:
            # In case it was stopped before the end
            df.unsubscribe(self._on_mchr_data)
            df.synchronizedOn(None)

            future._acq_done.set()
Exemplo n.º 51
0
    def on_paint(self, _):
        if self._value_range is None:
            return

        # shared function with the export method
        self._tick_list, self._vtp_ratio = calculate_ticks(self._value_range, self.ClientSize, self._orientation, self._tick_spacing)
        csize = self.ClientSize

        if self.lock_va is not None and self.lock_va.value == False:
            if self._orientation == wx.HORIZONTAL:
                csize.x -= 24

        # If min and max are very close, we need more significant numbers to
        # ensure the values displayed are different (ex 17999 -> 18003)
        rng = self._value_range
        if rng[0] == rng[1]:
            sig = None
        else:
            ratio_rng = max(abs(v) for v in rng) / (max(rng) - min(rng))
            sig = max(3, 1 + math.ceil(math.log10(ratio_rng * len(self._tick_list))))

        # Set Font
        font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
        ctx = wx.lib.wxcairo.ContextFromDC(wx.PaintDC(self))
        ctx.select_font_face(font.GetFaceName(), cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
        ctx.set_font_size(font.GetPointSize())

        ctx.set_source_rgb(*self.tick_colour)
        ctx.set_line_width(2)
        ctx.set_line_join(cairo.LINE_JOIN_MITER)

        max_width = 0
        prev_lpos = 0 if self._orientation == wx.HORIZONTAL else csize.y

        for i, (pos, val) in enumerate(self._tick_list):
            label = units.readable_str(val, self.unit, sig)

            if i == 0 and self._lo_ellipsis:
                label = u"…" + label

            if i == len(self._tick_list) - 1 and self._hi_ellipsis:
                label = label + u"…"

            _, _, lbl_width, lbl_height, _, _ = ctx.text_extents(label)

            if self._orientation == wx.HORIZONTAL:
                lpos = pos - (lbl_width // 2)
                lpos = max(min(lpos, csize.x - lbl_width - 2), 2)
                # print (i, prev_right, lpos)
                if prev_lpos < lpos:
                    ctx.move_to(lpos, lbl_height + 8)
                    ctx.show_text(label)
                    ctx.move_to(pos, 5)
                    ctx.line_to(pos, 0)
                prev_lpos = lpos + lbl_width
            else:
                max_width = max(max_width, lbl_width)
                lpos = pos + (lbl_height // 2)
                lpos = max(min(lpos, csize.y), 2)

                if prev_lpos >= lpos + 20 or i == 0:
                    ctx.move_to(csize.x - lbl_width - 9, lpos)
                    ctx.show_text(label)
                    ctx.move_to(csize.x - 5, pos)
                    ctx.line_to(csize.x, pos)
                prev_lpos = lpos + lbl_height

            ctx.stroke()

        if self._orientation == wx.VERTICAL and max_width != self._max_tick_width:
            self._max_tick_width = max_width
            self.SetMinSize((self._max_tick_width + 14, -1))
            self.Parent.GetSizer().Layout()
Exemplo n.º 52
0
    def _runAcquisition(self, future):
        self._data = []
        self._md = {}

        wls = self.startWavelength.value
        wle = self.endWavelength.value
        res = self.numberOfPixels.value
        dt = self.dwellTime.value
        trig = self._detector.softwareTrigger
        df = self._detector.data

        # Prepare the hardware
        self._emitter.resolution.value = (1, 1)  # Force one pixel only
        self._emitter.translation.value = self.emtTranslation.value
        self._emitter.dwellTime.value = dt

        df.synchronizedOn(trig)
        df.subscribe(self._on_mchr_data)

        wllist = []
        if wle == wls:
            res = 1

        if res <= 1:
            res = 1
            wli = 0
        else:
            wli = (wle - wls) / (res - 1)

        try:
            for i in range(res):
                left = (res - i) * (dt + 0.05)
                future.set_progress(end=time.time() + left)

                cwl = wls + i * wli  # requested value
                self._sgr.moveAbs({"wavelength": cwl}).result()
                if future._acq_state == CANCELLED:
                    raise CancelledError()
                cwl = self._sgr.position.value["wavelength"]  # actual value
                logging.info("Acquiring point %d/%d @ %s", i + 1, res,
                             units.readable_str(cwl, unit="m", sig=3))

                self._pt_acq.clear()
                trig.notify()
                if not self._pt_acq.wait(dt * 5 + 1):
                    raise IOError("Timeout waiting for the data")
                if future._acq_state == CANCELLED:
                    raise CancelledError()
                wllist.append(cwl)

            # Done
            df.unsubscribe(self._on_mchr_data)
            df.synchronizedOn(None)

            # Convert the sequence of data into one spectrum in a DataArray

            if wls > wle:  # went backward? => sort back the spectrum
                logging.debug(
                    "Inverting spectrum as acquisition went from %g to %g m",
                    wls, wls)
                self._data.reverse()
                wllist.reverse()

            na = numpy.array(self._data)  # keeps the dtype
            na.shape += (1, 1, 1, 1)  # make it 5th dim to indicate a channel
            md = self._md
            md[model.MD_WL_LIST] = wllist
            if model.MD_OUT_WL in md:
                # The MD_OUT_WL on the monochromator contains the current cw, which we don't want
                del md[model.MD_OUT_WL]

            # MD_POS should already be at the correct position (from the e-beam metadata)

            # MD_PIXEL_SIZE is not meaningful but handy for the display in Odemis
            # (it's the size of the square on top of the SEM survey => BIG!)
            sempxs = self._emitter.pixelSize.value
            md[model.MD_PIXEL_SIZE] = (sempxs[0] * 50, sempxs[1] * 50)

            spec = model.DataArray(na, md)

            with future._acq_lock:
                if future._acq_state == CANCELLED:
                    raise CancelledError()
                future._acq_state = FINISHED

            return [spec]

        except CancelledError:
            raise  # Just don't log the exception
        except Exception:
            logging.exception("Failure during monochromator scan")
        finally:
            # In case it was stopped before the end
            df.unsubscribe(self._on_mchr_data)
            df.synchronizedOn(None)

            future._acq_done.set()
Exemplo n.º 53
0
 def value_formatter(value, unit=val_unit):
     value_ctrl.SetValue(readable_str(value, unit, sig=sig))
Exemplo n.º 54
0
def move_abs(comp_name, moves, check_distance=True):
    """
    move (in absolute) the axis of the given component to the specified position
    comp_name (str): name of the component
    check_distance (bool): if the axis is in meters, check that the move is not
      too big.
    moves (dict str -> str): axis -> position (as text)
    """
    component = get_component(comp_name)

    act_mv = {} # axis -> value
    for axis_name, str_position in moves.items():
        try:
            if axis_name not in component.axes:
                raise ValueError("Actuator %s has no axis '%s'" % (comp_name, axis_name))
            ad = component.axes[axis_name]
        except (TypeError, AttributeError):
            raise ValueError("Component %s is not an actuator" % comp_name)

        # Allow the user to indicate the position via the user-friendly choice entry
        position = None
        if (hasattr(ad, "choices") and isinstance(ad.choices, dict)):
            for key, value in ad.choices.items():
                if value == str_position:
                    logging.info("Converting '%s' into %s", str_position, key)
                    position = key
                    # Even if it's a big distance, we don't complain as it's likely
                    # that all choices are safe
                    break

        if position is None:
            if ad.unit == "m":
                try:
                    position = float(str_position)
                except ValueError:
                    raise ValueError("Position '%s' cannot be converted to a number" % str_position)

                # compare to the current position, to see if the new position sounds reasonable
                cur_pos = component.position.value[axis_name]
                if check_distance and abs(cur_pos - position) > MAX_DISTANCE:
                    raise IOError("Distance of %f m is too big (> %f m), use '--big-distance' to allow the move." %
                                  (abs(cur_pos - position), MAX_DISTANCE))
            else:
                position = convert_to_object(str_position)

            # If only a couple of positions are possible, and asking for a float,
            # avoid the rounding error by looking for the closest possible
            if (isinstance(position, numbers.Real) and
                hasattr(ad, "choices") and
                isinstance(ad.choices, collections.Iterable) and
                position not in ad.choices):
                closest = util.find_closest(position, ad.choices)
                if util.almost_equal(closest, position, rtol=1e-3):
                    logging.debug("Adjusting value %.15g to %.15g", position, closest)
                    position = closest

        act_mv[axis_name] = position
        logging.info(u"Will move %s.%s to %s", comp_name, axis_name,
                     units.readable_str(position, ad.unit, sig=3))

    try:
        m = component.moveAbs(act_mv)
        try:
            m.result(120)
        except KeyboardInterrupt:
            logging.warning("Cancelling absolute move of component %s", comp_name)
            m.cancel()
            raise
    except Exception as exc:
        raise IOError("Failed to move component %s to %s: %s" %
                      (comp_name, act_mv, exc))
Exemplo n.º 55
0
    def _on_fa_done(self, future):
        logging.debug("End of overlay procedure")
        main_data = self._main_data_model
        self._acq_future = None  # To avoid holding the ref in memory
        self._faf_connector = None

        try:
            # DEBUG
            try:
                trans_val, cor_md = future.result()
            except Exception:
                cor_md = {}, {}
            opt_md, sem_md = cor_md

            # Save the optical correction metadata straight into the CCD
            main_data.ccd.updateMetadata(opt_md)

            # The SEM correction metadata goes to the ebeam
            main_data.ebeam.updateMetadata(sem_md)
        except CancelledError:
            self._tab_panel.lbl_fine_align.Label = "Cancelled"
        except Exception as ex:
            logging.warning("Failure during overlay: %s", ex)
            self._tab_panel.lbl_fine_align.Label = "Failed"
        else:
            self._main_frame.menu_item_reset_finealign.Enable(True)

            # Check whether the values make sense. If not, we still accept them,
            # but hopefully make it clear enough to the user that the calibration
            # should not be trusted.
            rot = opt_md.get(model.MD_ROTATION_COR, 0)
            rot0 = (rot + math.pi) % (2 *
                                      math.pi) - math.pi  # between -pi and pi
            rot_deg = math.degrees(rot0)
            opt_scale = opt_md.get(model.MD_PIXEL_SIZE_COR, (1, 1))[0]
            shear = sem_md.get(model.MD_SHEAR_COR, 0)
            scaling_xy = sem_md.get(model.MD_PIXEL_SIZE_COR, (1, 1))
            if (not abs(rot_deg) < 10 or  # Rotation < 10°
                    not 0.9 < opt_scale < 1.1 or  # Optical mag < 10%
                    not abs(shear) < 0.3 or  # Shear < 30%
                    any(not 0.9 < v < 1.1
                        for v in scaling_xy)  # SEM ratio diff < 10%
                ):
                # Special warning in case of wrong magnification
                if not 0.9 < opt_scale < 1.1 and model.hasVA(
                        main_data.lens, "magnification"):
                    lens_mag = main_data.lens.magnification.value
                    measured_mag = lens_mag / opt_scale
                    logging.warning(
                        "The measured optical magnification is %fx, instead of expected %fx. "
                        "Check that the lens magnification and the SEM magnification are correctly set.",
                        measured_mag, lens_mag)
                else:  # Generic warning
                    logging.warning(
                        u"The fine alignment values are very large, try on a different place on the sample. "
                        u"mag correction: %f, rotation: %f°, shear: %f, X/Y scale: %f",
                        opt_scale, rot_deg, shear, scaling_xy)

                self._tab_panel.lbl_fine_align.Label = "Probably incorrect"
            else:
                self._tab_panel.lbl_fine_align.Label = "Successful"

            # Rotation is compensated in software on the FM image, but the user
            # can also change the SEM scan rotation, and re-run the alignment,
            # so show it clearly, for the user to take action.
            # The worse the rotation, the longer it's displayed.
            timeout = max(2, min(abs(rot_deg), 10))
            popup.show_message(
                self._tab_panel,
                u"Rotation applied: %s\nShear applied: %s\nX/Y Scaling applied: %s"
                % (units.readable_str(
                    rot_deg, unit=u"°", sig=3), units.readable_str(
                        shear, sig=3), units.readable_str(scaling_xy, sig=3)),
                timeout=timeout)
            logging.info(
                u"Fine alignment computed mag correction of %f, rotation of %f°, "
                u"shear needed of %s, and X/Y scaling needed of %s.",
                opt_scale, rot, shear, scaling_xy)

        # As the CCD image might have different pixel size, force to fit
        self._tab_panel.vp_align_ccd.canvas.fit_view_to_next_image = True

        main_data.is_acquiring.value = False
        self._tab_panel.btn_fine_align.Bind(wx.EVT_BUTTON, self._on_fine_align)
        self._tab_panel.btn_fine_align.Label = self._fa_btn_label
        self._resume()

        self._tab_panel.lbl_fine_align.Show()
        self._tab_panel.gauge_fine_align.Hide()
        self._sizer.Layout()
Exemplo n.º 56
0
    def _on_fa_done(self, future):
        logging.debug("End of overlay procedure")
        main_data = self._main_data_model
        self._acq_future = None  # To avoid holding the ref in memory
        self._faf_connector = None

        try:
            # DEBUG
            try:
                trans_val, cor_md = future.result()
            except Exception:
                cor_md = {}, {}
            opt_md, sem_md = cor_md

            # Save the optical correction metadata straight into the CCD
            main_data.ccd.updateMetadata(opt_md)

            # The SEM correction metadata goes to the ebeam
            main_data.ebeam.updateMetadata(sem_md)
        except CancelledError:
            self._tab_panel.lbl_fine_align.Label = "Cancelled"
        except Exception as ex:
            logging.warning("Failure during overlay: %s", ex)
            self._tab_panel.lbl_fine_align.Label = "Failed"
        else:
            self._main_frame.menu_item_reset_finealign.Enable(True)

            # Check whether the values make sense. If not, we still accept them,
            # but hopefully make it clear enough to the user that the calibration
            # should not be trusted.
            rot = opt_md.get(model.MD_ROTATION_COR, 0)
            rot0 = (rot + math.pi) % (2 * math.pi) - math.pi  # between -pi and pi
            rot_deg = math.degrees(rot0)
            opt_scale = opt_md.get(model.MD_PIXEL_SIZE_COR, (1, 1))[0]
            shear = sem_md.get(model.MD_SHEAR_COR, 0)
            scaling_xy = sem_md.get(model.MD_PIXEL_SIZE_COR, (1, 1))
            if (not abs(rot_deg) < 10 or  # Rotation < 10°
                not 0.9 < opt_scale < 1.1 or  # Optical mag < 10%
                not abs(shear) < 0.3 or  # Shear < 30%
                any(not 0.9 < v < 1.1 for v in scaling_xy) # SEM ratio diff < 10%
               ):
                # Special warning in case of wrong magnification
                if not 0.9 < opt_scale < 1.1 and model.hasVA(main_data.lens, "magnification"):
                    lens_mag = main_data.lens.magnification.value
                    measured_mag = lens_mag / opt_scale
                    logging.warning("The measured optical magnification is %fx, instead of expected %fx. "
                                    "Check that the lens magnification and the SEM magnification are correctly set.",
                                    measured_mag, lens_mag)
                else:  # Generic warning
                    logging.warning(u"The fine alignment values are very large, try on a different place on the sample. "
                                    u"mag correction: %f, rotation: %f°, shear: %f, X/Y scale: %f",
                                    opt_scale, rot_deg, shear, scaling_xy)

                self._tab_panel.lbl_fine_align.Label = "Probably incorrect"
            else:
                self._tab_panel.lbl_fine_align.Label = "Successful"

            # Rotation is compensated in software on the FM image, but the user
            # can also change the SEM scan rotation, and re-run the alignment,
            # so show it clearly, for the user to take action.
            # The worse the rotation, the longer it's displayed.
            timeout = max(2, min(abs(rot_deg), 10))
            popup.show_message(
                self._tab_panel,
                u"Rotation applied: %s\nShear applied: %s\nX/Y Scaling applied: %s"
                % (units.readable_str(rot_deg, unit=u"°", sig=3),
                   units.readable_str(shear, sig=3),
                   units.readable_str(scaling_xy, sig=3)),
                timeout=timeout
            )
            logging.info(u"Fine alignment computed mag correction of %f, rotation of %f°, "
                         u"shear needed of %s, and X/Y scaling needed of %s.",
                         opt_scale, rot, shear, scaling_xy)

        # As the CCD image might have different pixel size, force to fit
        self._tab_panel.vp_align_ccd.canvas.fit_view_to_next_image = True

        main_data.is_acquiring.value = False
        self._tab_panel.btn_fine_align.Bind(wx.EVT_BUTTON, self._on_fine_align)
        self._tab_panel.btn_fine_align.Label = self._fa_btn_label
        self._resume()

        self._tab_panel.lbl_fine_align.Show()
        self._tab_panel.gauge_fine_align.Hide()
        self._sizer.Layout()
Exemplo n.º 57
0
 def _display_pretty(self):
     if self._number_value is None:
         str_val = u""
     else:
         str_val = units.readable_str(self._number_value, sig=self.accuracy)
     wx.TextCtrl.ChangeValue(self, str_val)
Exemplo n.º 58
0
 def _display_pretty(self):
     if self._number_value is None:
         str_val = u""
     else:
         str_val = units.readable_str(self._number_value, sig=self.accuracy)
     wx.TextCtrl.ChangeValue(self, str_val)