Esempio n. 1
0
 def _reference_data_changed(self, *args):
     # This signal can get emitted if just the choices but not the actual
     # reference data change, so we check here that the reference data has
     # actually changed
     if self.reference_data is not getattr(self, '_last_reference_data',
                                           None):
         self._last_reference_data = self.reference_data
         # Note that we deliberately use nested delay_callback here, because
         # we want to make sure that x_att_world and y_att_world both get
         # updated first, then x_att and y_att can be changed, before
         # subsequent events are fired.
         with delay_callback(self, 'x_att', 'y_att'):
             with delay_callback(self, 'x_att_world', 'y_att_world',
                                 'slices'):
                 if self._display_world:
                     self.xw_att_helper.pixel_coord = False
                     self.yw_att_helper.pixel_coord = False
                     self.xw_att_helper.world_coord = True
                     self.yw_att_helper.world_coord = True
                 else:
                     self.xw_att_helper.pixel_coord = True
                     self.yw_att_helper.pixel_coord = True
                     self.xw_att_helper.world_coord = False
                     self.yw_att_helper.world_coord = False
                 self._update_combo_att()
                 self._set_default_slices()
                 # We need to make sure that we update x_att and y_att
                 # at the same time before any other callbacks get called,
                 # so we do this here manually.
                 self._on_xatt_world_change()
                 self._on_yatt_world_change()
Esempio n. 2
0
def test_delay_callback_nested():
    test = MagicMock()
    stub = Stub()

    add_callback(stub, 'prop1', test)
    with delay_callback(stub, 'prop1'):
        with delay_callback(stub, 'prop1'):
            stub.prop1 = 100
            stub.prop1 = 200
            stub.prop1 = 300
            assert test.call_count == 0
        assert test.call_count == 0
    test.assert_called_once_with(300)
Esempio n. 3
0
def test_delay_callback_nested():
    test = MagicMock()
    stub = Stub()

    add_callback(stub, 'prop1', test)
    with delay_callback(stub, 'prop1'):
        with delay_callback(stub, 'prop1'):
            stub.prop1 = 100
            stub.prop1 = 200
            stub.prop1 = 300
            assert test.call_count == 0
        assert test.call_count == 0
    test.assert_called_once_with(300)
Esempio n. 4
0
def test_delay_only_calls_if_changed():
    stub = Stub()
    test = MagicMock()

    add_callback(stub, 'prop1', test)

    with delay_callback(stub, 'prop1'):
        pass
    assert test.call_count == 0

    val = stub.prop1
    with delay_callback(stub, 'prop1'):
        stub.prop1 = val
    assert test.call_count == 0
Esempio n. 5
0
def test_delay_only_calls_if_changed():
    stub = Stub()
    test = MagicMock()

    add_callback(stub, 'prop1', test)

    with delay_callback(stub, 'prop1'):
        pass
    assert test.call_count == 0

    val = stub.prop1
    with delay_callback(stub, 'prop1'):
        stub.prop1 = val
    assert test.call_count == 0
Esempio n. 6
0
 def update_limits(self, update_profile=True):
     with delay_callback(self, 'v_min', 'v_max'):
         if update_profile:
             self.update_profile(update_limits=False)
         if self._profile_cache is not None and len(self._profile_cache[1]) > 0:
             self.v_min = np.nanmin(self._profile_cache[1])
             self.v_max = np.nanmax(self._profile_cache[1])
Esempio n. 7
0
    def press(self, event):

        if not self.active or not event.inaxes:
            return

        self.pressed = True

        x_min, x_max = self._axes.get_xlim()
        x_range = abs(x_max - x_min)

        if self.state.x_min is None or self.state.x_max is None:
            self.mode = 'move-x-max'
            with delay_callback(self.state, 'x_min', 'x_max'):
                self.state.x_min = event.xdata
                self.state.x_max = event.xdata
        elif abs(event.xdata - self.state.x_min) / x_range < PICK_THRESH:
            self.mode = 'move-x-min'
        elif abs(event.xdata - self.state.x_max) / x_range < PICK_THRESH:
            self.mode = 'move-x-max'
        elif (event.xdata > self.state.x_min) is (event.xdata <
                                                  self.state.x_max):
            self.mode = 'move'
            self.move_params = (event.xdata, self.state.x_min,
                                self.state.x_max)
        else:
            self.mode = 'move-x-max'
            self.state.x_min = event.xdata
Esempio n. 8
0
    def _reset_x_limits(self, *event):

        # NOTE: we don't use AttributeLimitsHelper because we need to avoid
        # trying to get the minimum of *all* the world coordinates in the
        # dataset. Instead, we use the same approach as in the layer state below
        # and in the case of world coordinates we use online the spine of the
        # data.

        if self.reference_data is None or self.x_att_pixel is None:
            return

        data = self.reference_data

        if self.x_att in data.pixel_component_ids:
            x_min, x_max = -0.5, data.shape[self.x_att.axis] - 0.5
        else:
            axis = data.world_component_ids.index(self.x_att)
            axis_view = [0] * data.ndim
            axis_view[axis] = slice(None)
            axis_values = data[self.x_att, tuple(axis_view)]
            x_min, x_max = np.nanmin(axis_values), np.nanmax(axis_values)

        with delay_callback(self, 'x_min', 'x_max'):
            self.x_min = x_min
            self.x_max = x_max
Esempio n. 9
0
def test_delay_callback_not_called_if_unmodified():
    test = MagicMock()
    stub = Stub()
    add_callback(stub, 'prop1', test)
    with delay_callback(stub, 'prop1'):
        pass
    assert test.call_count == 0
Esempio n. 10
0
def test_delay_callback_not_called_if_unmodified():
    test = MagicMock()
    stub = Stub()
    add_callback(stub, 'prop1', test)
    with delay_callback(stub, 'prop1'):
        pass
    assert test.call_count == 0
Esempio n. 11
0
    def zoom_level(self, val):
        if ((not isinstance(val, (int, float)) and val != 'fit')
                or (isinstance(val, (int, float)) and val <= 0)):
            raise ValueError(f'Unsupported zoom level: {val}')

        viewer = self.app.get_viewer("viewer-1")
        image = viewer.state.reference_data
        if (image is None or viewer.shape is None or viewer.state.x_att is None
                or viewer.state.y_att is None):  # pragma: no cover
            return

        # Zoom on X and Y will auto-adjust.
        if val == 'fit':
            # Similar to ImageViewerState.reset_limits() in Glue.
            new_x_min = 0
            new_x_max = image.shape[viewer.state.x_att.axis]
        else:
            cur_xcen = (viewer.state.x_min + viewer.state.x_max) * 0.5
            new_dx = viewer.shape[1] * 0.5 / val
            new_x_min = cur_xcen - new_dx
            new_x_max = cur_xcen + new_dx

        with delay_callback(viewer.state, 'x_min', 'x_max'):
            viewer.state.x_min = new_x_min - 0.5
            viewer.state.x_max = new_x_max - 0.5

        # We need to adjust the limits in here to avoid triggering all
        # the update events then changing the limits again.
        viewer.state._adjust_limits_aspect()
Esempio n. 12
0
    def _reference_data_changed(self, before=None, after=None):

        # A callback event for reference_data is triggered if the choices change
        # but the actual selection doesn't - so we avoid resetting the WCS in
        # this case.
        if before is after:
            return

        for layer in self.layers:
            layer.reset_cache()

        # This signal can get emitted if just the choices but not the actual
        # reference data change, so we check here that the reference data has
        # actually changed
        if self.reference_data is not getattr(self, '_last_reference_data', None):
            self._last_reference_data = self.reference_data

            with delay_callback(self, 'x_att'):

                if self.reference_data is None:
                    self.x_att_helper.set_multiple_data([])
                else:
                    self.x_att_helper.set_multiple_data([self.reference_data])
                    if self._display_world:
                        self.x_att_helper.world_coord = True
                        self.x_att = self.reference_data.world_component_ids[0]
                    else:
                        self.x_att_helper.world_coord = False
                        self.x_att = self.reference_data.pixel_component_ids[0]

                self._update_att()

        self.reset_limits()
Esempio n. 13
0
    def _handle_image_zoom(self, msg):
        mos_data = self.app.data_collection['MOS Table']

        # trigger zooming the image, if there is an image
        if mos_data.find_component_id("Images") is not None:
            if msg.shared_image:
                center, height = self._zoom_to_object_params(msg)
            else:
                try:
                    center, height = self._zoom_to_slit_params(msg)
                except IndexError:
                    # If there's nothing in the spectrum2d viewer, we can't get slit info
                    return

            if center is None or height is None:
                # Can't zoom if we couldn't figure out where to zoom (e.g. if RA/Dec not in table)
                return

            imview = self.app.get_viewer("image-viewer")

            image_axis_ratio = (
                (imview.axis_x.scale.max - imview.axis_x.scale.min) /
                (imview.axis_y.scale.max - imview.axis_y.scale.min))

            with delay_callback(imview.state, 'x_min', 'x_max', 'y_min',
                                'y_max'):
                imview.state.x_min = center[0] - image_axis_ratio * height
                imview.state.y_min = center[1] - height
                imview.state.x_max = center[0] + image_axis_ratio * height
                imview.state.y_max = center[1] + height
Esempio n. 14
0
    def _reference_data_changed(self, *args):

        for layer in self.layers:
            layer.reset_cache()

        # This signal can get emitted if just the choices but not the actual
        # reference data change, so we check here that the reference data has
        # actually changed
        if self.reference_data is not getattr(self, '_last_reference_data', None):
            self._last_reference_data = self.reference_data

            with delay_callback(self, 'x_att'):

                if self.reference_data is None:
                    self.x_att_helper.set_multiple_data([])
                else:
                    self.x_att_helper.set_multiple_data([self.reference_data])
                    if self._display_world:
                        self.x_att_helper.world_coord = True
                        self.x_att = self.reference_data.world_component_ids[0]
                    else:
                        self.x_att_helper.world_coord = False
                        self.x_att = self.reference_data.pixel_component_ids[0]

                self._update_att()
Esempio n. 15
0
 def update_view_to_bins(self, *args):
     """
     Update the view to match the histogram interval
     """
     with delay_callback(self, 'x_min', 'x_max'):
         self.x_min = self.hist_x_min
         self.x_max = self.hist_x_max
Esempio n. 16
0
    def _on_xy_change(self, *event):

        if self.viewer_state.x_att is None or self.viewer_state.y_att is None:
            return

        if isinstance(self.layer, BaseData):
            layer = self.layer
        else:
            layer = self.layer.data

        try:
            x_datetime = layer.get_kind(self.viewer_state.x_att) == 'datetime'
        except IncompatibleAttribute:
            x_datetime = False

        try:
            y_datetime = layer.get_kind(self.viewer_state.y_att) == 'datetime'
        except IncompatibleAttribute:
            y_datetime = False

        with delay_callback(self, 'xerr_visible', 'yerr_visible',
                            'vector_visible'):
            if x_datetime:
                self.xerr_visible = False
            if y_datetime:
                self.yerr_visible = False
            if x_datetime or y_datetime:
                self.vector_visible = False
Esempio n. 17
0
    def _on_layer_change(self, layer=None):

        with delay_callback(self, 'cmap_vmin', 'cmap_vmax', 'size_vmin',
                            'size_vmax', 'density_map'):

            self._update_density_map_mode()

            if self.layer is None:
                self.cmap_att_helper.set_multiple_data([])
                self.size_att_helper.set_multiple_data([])
            else:
                self.cmap_att_helper.set_multiple_data([self.layer])
                self.size_att_helper.set_multiple_data([self.layer])

            if self.layer is None:
                self.xerr_att_helper.set_multiple_data([])
                self.yerr_att_helper.set_multiple_data([])
            else:
                self.xerr_att_helper.set_multiple_data([self.layer])
                self.yerr_att_helper.set_multiple_data([self.layer])

            if self.layer is None:
                self.vx_att_helper.set_multiple_data([])
                self.vy_att_helper.set_multiple_data([])
            else:
                self.vx_att_helper.set_multiple_data([self.layer])
                self.vy_att_helper.set_multiple_data([self.layer])
    def camera_mouse_move(self, event=None):

        if 1 in event.buttons and keys.SHIFT in event.mouse_event.modifiers:

            camera = self._vispy_widget.view.camera

            norm = np.mean(camera._viewbox.size)

            p1 = event.mouse_event.press_event.pos
            p2 = event.mouse_event.pos

            dist = (p1 - p2) / norm * camera._scale_factor
            dist[1] *= -1
            dx, dy, dz = camera._dist_to_trans(dist)

            with delay_callback(self.state, 'x_min', 'x_max', 'y_min', 'y_max',
                                'z_min', 'z_max'):

                self.state.x_min = self._initial_position[
                    0] + self._width[0] * dx
                self.state.x_max = self._initial_position[
                    1] + self._width[0] * dx
                self.state.y_min = self._initial_position[
                    2] + self._width[1] * dy
                self.state.y_max = self._initial_position[
                    3] + self._width[1] * dy
                self.state.z_min = self._initial_position[
                    4] + self._width[2] * dz
                self.state.z_max = self._initial_position[
                    5] + self._width[2] * dz

            event.handled = True
    def camera_mouse_wheel(self, event=None):

        scale = (1.1**-event.delta[1])

        with delay_callback(self.state, 'x_min', 'x_max', 'y_min', 'y_max',
                            'z_min', 'z_max'):

            xmid = 0.5 * (self.state.x_min + self.state.x_max)
            dx = (self.state.x_max - xmid) * scale
            self.state.x_min = xmid - dx
            self.state.x_max = xmid + dx

            ymid = 0.5 * (self.state.y_min + self.state.y_max)
            dy = (self.state.y_max - ymid) * scale
            self.state.y_min = ymid - dy
            self.state.y_max = ymid + dy

            zmid = 0.5 * (self.state.z_min + self.state.z_max)
            dz = (self.state.z_max - zmid) * scale
            self.state.z_min = zmid - dz
            self.state.z_max = zmid + dz

        self._update_clip()

        event.handled = True
Esempio n. 20
0
    def _on_data_change(self, *args):

        links = self.visible_links
        with delay_callback(self, 'current_link'):
            LinkEditorState.current_link.set_choices(self, links)
            if len(links) > 0:
                self.current_link = links[0]
Esempio n. 21
0
 def on_limits_change(self, *args):
     for viewer in self.viewer.session.application.viewers:
         if viewer is not self.viewer:
             with delay_callback(viewer.state, 'x_min', 'x_max', 'y_min', 'y_max'):
                 viewer.state.x_min = self.viewer.state.x_min
                 viewer.state.x_max = self.viewer.state.x_max
                 viewer.state.y_min = self.viewer.state.y_min
                 viewer.state.y_max = self.viewer.state.y_max
Esempio n. 22
0
 def _reset_x_limits(self, *args):
     if self.x_att is None:
         return
     with delay_callback(self, 'hist_x_min', 'hist_x_max', 'x_min', 'x_max',
                         'x_log'):
         self.x_lim_helper.percentile = 100
         self.x_lim_helper.update_values(force=True)
         self.update_bins_to_view()
Esempio n. 23
0
    def _on_layer_change(self, layer=None):

        with delay_callback(self, 'vmin', 'vmin'):

            if self.layer is None:
                self.att_helper.set_multiple_data([])
            else:
                self.att_helper.set_multiple_data([self.layer])
Esempio n. 24
0
 def flip_data(self, *args):
     # FIXME: since the links will be the same in the list of current links,
     # we can make sure we reselect the same one as before - it would be
     # better if this didn't change in the first place though.
     _original_current_link = self.current_link
     with delay_callback(self, 'data1', 'data2'):
         self.data1, self.data2 = self.data2, self.data1
     self.current_link = _original_current_link
Esempio n. 25
0
 def _update_combo_att(self):
     with delay_callback(self, 'x_att_world', 'y_att_world'):
         if self.reference_data is None:
             self.xw_att_helper.set_multiple_data([])
             self.yw_att_helper.set_multiple_data([])
         else:
             self.xw_att_helper.set_multiple_data([self.reference_data])
             self.yw_att_helper.set_multiple_data([self.reference_data])
Esempio n. 26
0
    def on_mouse_or_key_event(self, data):
        from jdaviz.configs.imviz.helper import get_top_layer_index

        event = data['event']

        # Note that we throttle this to 200ms here as changing the contrast
        # and bias it expensive since it forces the whole image to be redrawn
        if event == 'dragmove':
            if (time.time() - self._time_last) <= 0.2:
                return

            event_x = data['pixel']['x']
            event_y = data['pixel']['y']
            max_x = self.viewer.shape[1]
            max_y = self.viewer.shape[0]

            if ((event_x < 0) or (event_x >= max_x) or
                    (event_y < 0) or (event_y >= max_y)):
                return

            # Normalize w.r.t. viewer display from 0 to 1
            x = event_x / (max_x - 1)
            y = event_y / (max_y - 1)

            # When blinked, first layer might not be top layer
            i_top = get_top_layer_index(self.viewer)
            state = self.viewer.layers[i_top].state

            # bias range 0..1
            # contrast range 0..4
            with delay_callback(state, 'bias', 'contrast'):
                state.bias = x
                state.contrast = y * 4

            self._time_last = time.time()

        elif event == 'dblclick':
            # When blinked, first layer might not be top layer
            i_top = get_top_layer_index(self.viewer)
            state = self.viewer.layers[i_top].state

            # Restore defaults that are applied on load
            with delay_callback(state, 'bias', 'contrast'):
                state.bias = 0.5
                state.contrast = 1
Esempio n. 27
0
 def set_limits(self, x_min, x_max, y_min, y_max, z_min, z_max):
     with delay_callback(self, 'x_min', 'x_max', 'y_min', 'y_max', 'z_min',
                         'z_max'):
         self.x_min = x_min
         self.x_max = x_max
         self.y_min = y_min
         self.y_max = y_max
         self.z_min = z_min
         self.z_max = z_max
Esempio n. 28
0
 def remove_data(self, data):
     with delay_callback(self.state, 'layers'):
         for layer_state in self.state.layers[::-1]:
             if isinstance(layer_state.layer, BaseData):
                 if layer_state.layer is data:
                     self.state.layers.remove(layer_state)
             else:
                 if layer_state.layer.data is data:
                     self.state.layers.remove(layer_state)
Esempio n. 29
0
 def _on_layer_change(self, layer=None):
     with delay_callback(self, 'cmap_vmin', 'cmap_vmax', 'size_vmin', 'size_vmax'):
         if self.layer is None:
             self.cmap_att_helper.set_multiple_data([])
             self.size_att_helper.set_multiple_data([])
             self.img_data_att_helper.set_multiple_data([])
         else:
             self.cmap_att_helper.set_multiple_data([self.layer])
             self.size_att_helper.set_multiple_data([self.layer])
             self.img_data_att_helper.set_multiple_data([self.layer])
Esempio n. 30
0
    def center_on(self, point):
        """Centers the view on a particular point.

        Parameters
        ----------
        point : tuple or `~astropy.coordinates.SkyCoord`
            If tuple of ``(X, Y)`` is given, it is assumed
            to be in data coordinates and 0-indexed.

        Raises
        ------
        AttributeError
            Sky coordinates are given but image does not have a valid WCS.

        """
        viewer = self.app.get_viewer("viewer-1")
        i_top = get_top_layer_index(viewer)
        image = viewer.layers[i_top].layer

        if isinstance(point, SkyCoord):
            if data_has_valid_wcs(image):
                try:
                    pix = image.coords.world_to_pixel(point)  # 0-indexed X, Y
                except NoConvergence as e:  # pragma: no cover
                    self.app.hub.broadcast(
                        SnackbarMessage(
                            f'{point} is likely out of bounds: {repr(e)}',
                            color="warning",
                            sender=self.app))
                    return
            else:
                raise AttributeError(
                    f'{getattr(image, "label", None)} does not have a valid WCS'
                )
        else:
            pix = point

        # Disallow centering outside of display; image.shape is (Y, X)
        eps = sys.float_info.epsilon
        if (not np.all(np.isfinite(pix)) or pix[0] < -eps or pix[0] >=
            (image.shape[1] + eps) or pix[1] < -eps or pix[1] >=
            (image.shape[0] + eps)):
            self.app.hub.broadcast(
                SnackbarMessage(f'{pix} is out of bounds',
                                color="warning",
                                sender=self.app))
            return

        with delay_callback(viewer.state, 'x_min', 'x_max', 'y_min', 'y_max'):
            width = viewer.state.x_max - viewer.state.x_min
            height = viewer.state.y_max - viewer.state.y_min
            viewer.state.x_min = pix[0] - (width * 0.5)
            viewer.state.y_min = pix[1] - (height * 0.5)
            viewer.state.x_max = viewer.state.x_min + width
            viewer.state.y_max = viewer.state.y_min + height
Esempio n. 31
0
 def update_bins_to_view(self, *args):
     """
     Update the bins to match the current view.
     """
     with delay_callback(self, 'hist_x_min', 'hist_x_max'):
         if self.x_max > self.x_min:
             self.hist_x_min = self.x_min
             self.hist_x_max = self.x_max
         else:
             self.hist_x_min = self.x_max
             self.hist_x_max = self.x_min
Esempio n. 32
0
 def _on_density_map_change(self, *args):
     # If the density map mode is used, we should disable the lines/errors/vectors
     if self.density_map:
         with delay_callback(self, 'line_visible', 'xerr_visible',
                             'yerr_visible', 'vector_visible'):
             if self.line_visible:
                 self.line_visible = False
             if self.xerr_visible:
                 self.xerr_visible = False
             if self.yerr_visible:
                 self.yerr_visible = False
             if self.vector_visible:
                 self.vector_visible = False
Esempio n. 33
0
    def move(self, event):

        if not self.active or not self.pressed or not event.inaxes:
            return

        if self.mode == 'move-x-min':
            self.state.x_min = event.xdata
        elif self.mode == 'move-x-max':
            self.state.x_max = event.xdata
        elif self.mode == 'move':
            orig_click, orig_x_min, orig_x_max = self.move_params
            with delay_callback(self.state, 'x_min', 'x_max'):
                self.state.x_min = orig_x_min + (event.xdata - orig_click)
                self.state.x_max = orig_x_max + (event.xdata - orig_click)
Esempio n. 34
0
def test_delay_global_callback():

    # Regression test to make sure that delay_callback works for global
    # callbacks too.

    state = State()

    test1 = MagicMock()
    state.add_callback('a', test1)

    test2 = MagicMock()
    state.add_global_callback(test2)

    with delay_callback(state, 'a'):
        state.a = 100
        assert test1.call_count == 0
        assert test2.call_count == 0

    test1.assert_called_once_with(100)
    test2.assert_called_once_with(a=100)

    test2.reset_mock()

    with delay_callback(state, 'a'):
        state.b = 200
        assert test2.call_count == 1

    test2.assert_called_once_with(b=200)

    test2.reset_mock()

    with delay_callback(state, 'a', 'b'):
        state.a = 300
        state.b = 400
        assert test2.call_count == 0

    test2.assert_called_once_with(a=300, b=400)
Esempio n. 35
0
def test_delay_global_callback_stub():

    # Make sure that adding the global callback delay functionality doesn't
    # break things when we are dealing with a plain class without HasCallbackProperties

    stub = Stub()

    test1 = MagicMock()
    add_callback(stub, 'prop1', test1)

    with delay_callback(stub, 'prop1'):
        stub.prop1 = 100
        assert test1.call_count == 0

    test1.assert_called_once_with(100)
Esempio n. 36
0
def test_delay_in_delayed_callback():

    # Regression test for a bug that occurred if a delayed callback included
    # a delay itself.

    state = State()

    def callback(*args, **kwargs):
        with delay_callback(state, 'a'):
            state.a = 2

    state.add_callback('a', callback)

    with delay_callback(state, 'a', 'b'):
        state.a = 100
Esempio n. 37
0
def test_delay_multiple():
    stub = Stub()
    test = MagicMock()
    test2 = MagicMock()

    add_callback(stub, 'prop1', test)
    add_callback(stub, 'prop2', test2)

    with delay_callback(stub, 'prop1', 'prop2'):
        stub.prop1 = 50
        stub.prop1 = 100
        stub.prop2 = 200
        assert test.call_count == 0
        assert test2.call_count == 0

    test.assert_called_once_with(100)
    test2.assert_called_once_with(200)
Esempio n. 38
0
 def callback(*args, **kwargs):
     with delay_callback(state, 'a'):
         state.a = 2