Beispiel #1
0
    def test_linking_and_enabling(self):

        # Regression test for a bug that caused layers not not be correctly
        # enabled/disabled.

        self.viewer.add_data(self.image1)
        self.viewer.add_data(self.catalog)
        self.catalog.add_component([4, 5, 6], 'e')

        self.data_collection.new_subset_group(subset_state=self.catalog.id['e'] > 4)

        process_events()

        assert self.viewer.layers[0].enabled  # image
        assert not self.viewer.layers[1].enabled  # scatter
        assert not self.viewer.layers[2].enabled  # image subset
        assert not self.viewer.layers[3].enabled  # scatter subset

        link1 = LinkSame(self.catalog.id['c'], self.image1.pixel_component_ids[0])
        link2 = LinkSame(self.catalog.id['d'], self.image1.pixel_component_ids[1])
        self.data_collection.add_link(link1)
        self.data_collection.add_link(link2)

        process_events()

        assert self.viewer.layers[0].enabled  # image
        assert self.viewer.layers[1].enabled  # scatter
        assert not self.viewer.layers[2].enabled  # image subset
        assert self.viewer.layers[3].enabled  # scatter subset
Beispiel #2
0
    def test_linking_and_enabling(self):

        # Regression test for a bug that caused layers not not be correctly
        # enabled/disabled.

        self.viewer.add_data(self.image1)
        self.viewer.add_data(self.catalog)
        self.catalog.add_component([4, 5, 6], 'e')

        self.data_collection.new_subset_group(subset_state=self.catalog.id['e'] > 4)

        process_events()

        assert self.viewer.layers[0].enabled  # image
        assert not self.viewer.layers[1].enabled  # scatter
        assert not self.viewer.layers[2].enabled  # image subset
        assert not self.viewer.layers[3].enabled  # scatter subset

        link1 = LinkSame(self.catalog.id['c'], self.image1.world_component_ids[0])
        link2 = LinkSame(self.catalog.id['d'], self.image1.world_component_ids[1])
        self.data_collection.add_link(link1)
        self.data_collection.add_link(link2)

        process_events()

        assert self.viewer.layers[0].enabled  # image
        assert self.viewer.layers[1].enabled  # scatter
        assert not self.viewer.layers[2].enabled  # image subset
        assert self.viewer.layers[3].enabled  # scatter subset
Beispiel #3
0
def test_graceful_close_after_invalid(capsys):

    # Regression test for a bug that caused an error if an invalid dataset
    # was added to the viewer after the user had acknowledged the error.

    d = Data(a=[[1, 2], [3, 4]], label='test')

    dc = DataCollection([d])

    gapp = GlueApplication(dc)

    viewer = gapp.new_data_viewer(TableViewer)
    gapp.show()

    process_events()

    with pytest.raises(ValueError,
                       match='Can only use Table widget for 1D data'):
        viewer.add_data(d)

    viewer.close()

    process_events()

    #  We use capsys here because the # error is otherwise only apparent in stderr.
    out, err = capsys.readouterr()
    assert out.strip() == ""
    assert err.strip() == ""
Beispiel #4
0
    def test_log_labels(self):

        # Regression test to make sure the labels are correctly changed to log
        # when the x-axis is in log space.

        viewer_state = self.viewer.state
        data = Data(x=np.logspace(-5, 5, 10000))
        self.data_collection.append(data)

        self.viewer.add_data(data)
        viewer_state.x_log = True

        process_events()

        labels = [
            x.get_text() for x in self.viewer.axes.xaxis.get_ticklabels()
        ]

        # Different Matplotlib versions return slightly different
        # labels, but the ones below should be present regardless
        # of Matplotlib version.
        expected_present = [
            '$\\mathdefault{10^{-5}}$', '$\\mathdefault{10^{-3}}$',
            '$\\mathdefault{10^{-1}}$', '$\\mathdefault{10^{1}}$',
            '$\\mathdefault{10^{3}}$', '$\\mathdefault{10^{5}}$'
        ]

        for label in expected_present:
            assert label in labels
Beispiel #5
0
    def test_select_previously_incompatible_layer(self):

        # Regression test for a bug that caused a selection in a previously disabled
        # layer to enable the layer without updating the subset view

        self.viewer.add_data(self.image1)
        self.viewer.add_data(self.catalog)
        self.catalog.add_component([4, 5, 6], 'e')

        link1 = LinkSame(self.catalog.id['c'], self.image1.world_component_ids[0])
        link2 = LinkSame(self.catalog.id['d'], self.image1.world_component_ids[1])
        self.data_collection.add_link(link1)
        self.data_collection.add_link(link2)

        self.data_collection.new_subset_group(subset_state=self.catalog.id['e'] > 4)

        process_events()

        assert self.viewer.layers[0].enabled  # image
        assert self.viewer.layers[1].enabled  # scatter
        assert not self.viewer.layers[2].enabled  # image subset
        assert self.viewer.layers[3].enabled  # scatter subset

        assert not self.viewer.layers[2].image_artist.get_visible()

        self.data_collection.subset_groups[0].subset_state = self.catalog.id['c'] > -1

        process_events()

        assert self.viewer.layers[0].enabled  # image
        assert self.viewer.layers[1].enabled  # scatter
        assert self.viewer.layers[2].enabled  # image subset
        assert self.viewer.layers[3].enabled  # scatter subset

        assert self.viewer.layers[2].image_artist.get_visible()
Beispiel #6
0
    def test_navigate_sync_image(self):

        self.viewer.add_data(self.data)
        image_viewer = self.app.new_data_viewer(ImageViewer)
        image_viewer.add_data(self.data)
        assert image_viewer.state.slices == (0, 0, 0)

        self.viewer.state.x_att = self.data.pixel_component_ids[0]

        # Force events to be processed to make sure that the callback functions
        # for the computation thread are executed (since they rely on signals)
        self.viewer.layers[0].wait()
        process_events()

        x, y = self.viewer.axes.transData.transform([[1, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        self.viewer.axes.figure.canvas.button_release_event(x, y, 1)
        assert image_viewer.state.slices == (1, 0, 0)

        self.viewer.state.x_att = self.data.world_component_ids[0]

        x, y = self.viewer.axes.transData.transform([[10, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        self.viewer.axes.figure.canvas.button_release_event(x, y, 1)
        assert image_viewer.state.slices == (5, 0, 0)
Beispiel #7
0
    def test_density_map_incompatible_subset(self, capsys):

        # Regression test for a bug that caused the scatter viewer to crash
        # if subset for density map was incompatible.

        data2 = Data(label='d1',
                     x=[3.4, 2.3, -1.1, 0.3],
                     y=[3.2, 3.3, 3.4, 3.5],
                     z=['a', 'b', 'c', 'a'])

        self.data_collection.append(data2)

        self.viewer.add_data(self.data)
        self.viewer.add_data(data2)

        self.data_collection.new_subset_group('test', self.data.id['x'] > 1)

        for layer in self.viewer.state.layers:
            layer.density_map = True

        self.viewer.figure.canvas.draw()
        process_events()

        assert self.viewer.layers[0].enabled
        assert not self.viewer.layers[1].enabled
        assert self.viewer.layers[2].enabled
        assert not self.viewer.layers[3].enabled
Beispiel #8
0
    def test_datetime64_disabled(self, capsys):

        # Make sure that datetime components aren't options for the vector and
        # error markers.

        data = Data(label='test')
        data.add_component(np.array([100, 200, 300, 400], dtype='M8[D]'), 't1')
        data.add_component(np.array([200, 300, 400, 500], dtype='M8[D]'), 't2')
        data.add_component(np.array([200., 300., 400., 500.]), 'x')
        data.add_component(np.array([200., 300., 400., 500.]), 'y')
        self.data_collection.append(data)

        self.viewer.add_data(data)
        self.viewer.state.x_att = data.id['x']
        self.viewer.state.y_att = data.id['y']
        self.viewer.state.layers[0].cmap_mode = 'Linear'
        self.viewer.state.layers[0].cmap_att = data.id['x']
        self.viewer.state.layers[0].size_mode = 'Linear'
        self.viewer.state.layers[0].size_att = data.id['y']
        self.viewer.state.layers[0].vector_visible = True
        self.viewer.state.layers[0].xerr_visible = True
        self.viewer.state.layers[0].yerr_visible = True

        process_events()

        self.viewer.state.x_att = data.id['t1']
        self.viewer.state.y_att = data.id['t2']

        process_events()

        #  We use capsys here because the # error is otherwise only apparent in stderr.
        out, err = capsys.readouterr()
        assert out.strip() == ""
        assert err.strip() == ""
Beispiel #9
0
    def test_collapse_reverse(self, capsys):

        # Regression test for a bug that caused collapsing to fail if selecting
        # the range in the reverse direction.

        self.viewer.add_data(self.data)

        image_viewer = self.app.new_data_viewer(ImageViewer)
        image_viewer.add_data(self.data)

        self.profile_tools.ui.tabs.setCurrentIndex(2)

        self.viewer.state.x_att = self.data.pixel_component_ids[0]

        # Force events to be processed to make sure that the callback functions
        # for the computation thread are executed (since they rely on signals)
        self.viewer.layers[0].wait()
        process_events()

        x, y = self.viewer.axes.transData.transform([[15.1, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        x, y = self.viewer.axes.transData.transform([[0.9, 4]])[0]
        self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1)

        self.profile_tools.ui.button_collapse.click()
        process_events()

        #  We use capsys here because the # error is otherwise only apparent in stderr.
        out, err = capsys.readouterr()
        assert out.strip() == ""
        assert err.strip() == ""
Beispiel #10
0
    def test_navigate_sync_image(self):

        self.viewer.add_data(self.data)
        image_viewer = self.app.new_data_viewer(ImageViewer)
        image_viewer.add_data(self.data)
        assert image_viewer.state.slices == (0, 0, 0)

        self.viewer.state.x_att = self.data.pixel_component_ids[0]

        # Force events to be processed to make sure that the callback functions
        # for the computation thread are executed (since they rely on signals)
        self.viewer.layers[0].wait()
        process_events()

        x, y = self.viewer.axes.transData.transform([[1, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        self.viewer.axes.figure.canvas.button_release_event(x, y, 1)
        assert image_viewer.state.slices == (1, 0, 0)

        self.viewer.state.x_att = self.data.world_component_ids[0]

        x, y = self.viewer.axes.transData.transform([[10, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        self.viewer.axes.figure.canvas.button_release_event(x, y, 1)
        assert image_viewer.state.slices == (5, 0, 0)
Beispiel #11
0
 def wait(self):
     # Wait 0.5 seconds to make sure that the computation has properly started
     time.sleep(0.5)
     while self._worker.running:
         time.sleep(1 / 25)
     from glue.utils.qt import process_events
     process_events()
Beispiel #12
0
    def test_select_previously_incompatible_layer(self):

        # Regression test for a bug that caused a selection in a previously disabled
        # layer to enable the layer without updating the subset view

        self.viewer.add_data(self.image1)
        self.viewer.add_data(self.catalog)
        self.catalog.add_component([4, 5, 6], 'e')

        link1 = LinkSame(self.catalog.id['c'], self.image1.pixel_component_ids[0])
        link2 = LinkSame(self.catalog.id['d'], self.image1.pixel_component_ids[1])
        self.data_collection.add_link(link1)
        self.data_collection.add_link(link2)

        self.data_collection.new_subset_group(subset_state=self.catalog.id['e'] > 4)

        process_events()

        assert self.viewer.layers[0].enabled  # image
        assert self.viewer.layers[1].enabled  # scatter
        assert not self.viewer.layers[2].enabled  # image subset
        assert self.viewer.layers[3].enabled  # scatter subset

        assert not self.viewer.layers[2].image_artist.get_visible()

        self.data_collection.subset_groups[0].subset_state = self.catalog.id['c'] > -1

        process_events()

        assert self.viewer.layers[0].enabled  # image
        assert self.viewer.layers[1].enabled  # scatter
        assert self.viewer.layers[2].enabled  # image subset
        assert self.viewer.layers[3].enabled  # scatter subset

        assert self.viewer.layers[2].image_artist.get_visible()
Beispiel #13
0
    def test_log_labels(self):

        # Regression test to make sure the labels are correctly changed to log
        # when the x-axis is in log space.

        viewer_state = self.viewer.state
        data = Data(x=np.logspace(-5, 5, 10000))
        self.data_collection.append(data)

        self.viewer.add_data(data)
        viewer_state.x_log = True

        process_events()

        labels = [x.get_text() for x in self.viewer.axes.xaxis.get_ticklabels()]

        # Different Matplotlib versions return slightly different
        # labels, but the ones below should be present regardless
        # of Matplotlib version.
        expected_present = ['$\\mathdefault{10^{-5}}$',
                            '$\\mathdefault{10^{-3}}$',
                            '$\\mathdefault{10^{-1}}$',
                            '$\\mathdefault{10^{1}}$',
                            '$\\mathdefault{10^{3}}$',
                            '$\\mathdefault{10^{5}}$']

        for label in expected_present:
            assert label in labels
Beispiel #14
0
 def __init__(self, figure):
     self.figure = figure
     # For recent versions of Matplotlib it seems that we need
     # to process events at least twice to really flush out any
     # unprocessed events
     process_events()
     process_events()
     self.start = self.figure.canvas._draw_count
Beispiel #15
0
 def wait(self):
     if QT_INSTALLED:
         # Wait 0.5 seconds to make sure that the computation has properly started
         time.sleep(0.5)
         while self._worker.running:
             time.sleep(1 / 25)
         from glue.utils.qt import process_events
         process_events()
Beispiel #16
0
def test_logger_close():

    # Regression test to make sure that when closing an application, sys.stderr
    # no longer points to GlueLogger.

    app = GlueApplication()
    app.close()

    process_events()

    assert not isinstance(sys.stderr, GlueLogger)
Beispiel #17
0
def test_logger_close():

    # Regression test to make sure that when closing an application, sys.stderr
    # no longer points to GlueLogger.

    app = GlueApplication()
    app.close()

    process_events()

    assert not isinstance(sys.stderr, GlueLogger)
Beispiel #18
0
    def test_collapse(self):

        self.viewer.add_data(self.data)

        image_viewer = self.app.new_data_viewer(ImageViewer)
        image_viewer.add_data(self.data)

        self.profile_tools.ui.tabs.setCurrentIndex(2)

        # First try in pixel coordinates

        self.viewer.state.x_att = self.data.pixel_component_ids[0]

        # Force events to be processed to make sure that the callback functions
        # for the computation thread are executed (since they rely on signals)
        self.viewer.layers[0].wait()
        process_events()

        x, y = self.viewer.axes.transData.transform([[0.9, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        x, y = self.viewer.axes.transData.transform([[15.1, 4]])[0]
        self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1)

        self.profile_tools.ui.button_collapse.click()

        assert isinstance(image_viewer.state.slices[0], AggregateSlice)
        assert image_viewer.state.slices[0].slice.start == 1
        assert image_viewer.state.slices[0].slice.stop == 15
        assert image_viewer.state.slices[0].center == 0
        assert image_viewer.state.slices[0].function is nanmean

        # Next, try in world coordinates

        self.viewer.state.x_att = self.data.world_component_ids[0]

        # Force events to be processed to make sure that the callback functions
        # for the computation thread are executed (since they rely on signals)
        self.viewer.layers[0].wait()
        process_events()

        x, y = self.viewer.axes.transData.transform([[1.9, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        x, y = self.viewer.axes.transData.transform([[30.1, 4]])[0]
        self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1)

        self.profile_tools.ui.button_collapse.click()

        assert isinstance(image_viewer.state.slices[0], AggregateSlice)
        assert image_viewer.state.slices[0].slice.start == 1
        assert image_viewer.state.slices[0].slice.stop == 15
        assert image_viewer.state.slices[0].center == 0
        assert image_viewer.state.slices[0].function is nanmean
Beispiel #19
0
    def test_collapse(self):

        self.viewer.add_data(self.data)

        image_viewer = self.app.new_data_viewer(ImageViewer)
        image_viewer.add_data(self.data)

        self.profile_tools.ui.tabs.setCurrentIndex(2)

        # First try in pixel coordinates

        self.viewer.state.x_att = self.data.pixel_component_ids[0]

        # Force events to be processed to make sure that the callback functions
        # for the computation thread are executed (since they rely on signals)
        self.viewer.layers[0].wait()
        process_events()

        x, y = self.viewer.axes.transData.transform([[0.9, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        x, y = self.viewer.axes.transData.transform([[15.1, 4]])[0]
        self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1)

        self.profile_tools.ui.button_collapse.click()

        assert isinstance(image_viewer.state.slices[0], AggregateSlice)
        assert image_viewer.state.slices[0].slice.start == 1
        assert image_viewer.state.slices[0].slice.stop == 15
        assert image_viewer.state.slices[0].center == 0
        assert image_viewer.state.slices[0].function is nanmean

        # Next, try in world coordinates

        self.viewer.state.x_att = self.data.world_component_ids[0]

        # Force events to be processed to make sure that the callback functions
        # for the computation thread are executed (since they rely on signals)
        self.viewer.layers[0].wait()
        process_events()

        x, y = self.viewer.axes.transData.transform([[1.9, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        x, y = self.viewer.axes.transData.transform([[30.1, 4]])[0]
        self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1)

        self.profile_tools.ui.button_collapse.click()

        assert isinstance(image_viewer.state.slices[0], AggregateSlice)
        assert image_viewer.state.slices[0].slice.start == 1
        assert image_viewer.state.slices[0].slice.stop == 15
        assert image_viewer.state.slices[0].center == 0
        assert image_viewer.state.slices[0].function is nanmean
Beispiel #20
0
    def test_invisible_subset(self):

        # Regression test for a bug that caused a subset layer that started
        # off as invisible to have issues when made visible. We emulate the
        # initial invisible (but enabled) state by invalidating the cache.

        self.viewer.add_data(self.image1)
        self.data_collection.new_subset_group(subset_state=self.image1.id['x'] > 1, label='A')
        self.viewer.layers[1].visible = False
        self.viewer.layers[1].image_artist.invalidate_cache()
        self.viewer.layers[1].redraw()
        process_events()
        assert not np.any(self.viewer.layers[1].image_artist._A.mask)
        self.viewer.layers[1].visible = True
        assert not np.any(self.viewer.layers[1].image_artist._A.mask)
Beispiel #21
0
    def test_invisible_subset(self):

        # Regression test for a bug that caused a subset layer that started
        # off as invisible to have issues when made visible. We emulate the
        # initial invisible (but enabled) state by invalidating the cache.

        self.viewer.add_data(self.image1)
        self.data_collection.new_subset_group(subset_state=self.image1.id['x'] > 1, label='A')
        self.viewer.layers[1].visible = False
        self.viewer.layers[1].image_artist.invalidate_cache()
        self.viewer.layers[1].redraw()
        process_events()
        assert not np.any(self.viewer.layers[1].image_artist._A.mask)
        self.viewer.layers[1].visible = True
        assert not np.any(self.viewer.layers[1].image_artist._A.mask)
Beispiel #22
0
    def test_navigate_sync_image(self):

        self.viewer.add_data(self.data)
        self.viewer.toolbar.active_tool = 'solar:pixel_extraction'

        self.viewer.axes.figure.canvas.draw()
        process_events()

        x, y = self.viewer.axes.transData.transform([[1, 2]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        self.viewer.axes.figure.canvas.button_release_event(x, y, 1)
        assert len(self.data_collection) == 2

        derived1 = self.data_collection[1]
        assert derived1.label == "d1[:,2,1]"
        assert derived1.shape == (3, )
        assert_allclose(derived1['x'], self.data['x'][:, 2, 1])

        x, y = self.viewer.axes.transData.transform([[1, 1]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        self.viewer.axes.figure.canvas.button_release_event(x, y, 1)
        assert len(self.data_collection) == 2

        derived2 = self.data_collection[1]
        assert derived2 is derived1
        assert derived2.label == "d1[:,1,1]"
        assert derived2.shape == (3, )
        assert_allclose(derived2['x'], self.data['x'][:, 1, 1])

        self.viewer.state.x_att = self.data.pixel_component_ids[0]

        self.viewer.axes.figure.canvas.draw()
        process_events()

        x, y = self.viewer.axes.transData.transform([[1, 0]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        self.viewer.axes.figure.canvas.button_release_event(x, y, 1)
        assert len(self.data_collection) == 2

        derived3 = self.data_collection[1]
        assert derived3 is not derived1
        assert derived3.label == "d1[1,0,:]"
        assert derived3.shape == (2, )
        assert_allclose(derived3['x'], self.data['x'][1, 0, :])
Beispiel #23
0
    def test_add_helper(self):

        dialog = LinkEditor(self.data_collection)
        dialog.show()
        link_widget = dialog.link_widget

        link_widget.state.data1 = self.data1
        link_widget.state.data2 = self.data2

        add_coordinate_link = get_action(link_widget, 'ICRS <-> Galactic')

        # Add a coordinate link
        add_coordinate_link.trigger()

        # Ensure that all events get processed
        process_events()

        assert link_widget.listsel_current_link.count() == 1
        assert link_widget.link_details.text(
        ) == 'Link ICRS and Galactic coordinates'
        assert non_empty_rows_count(link_widget.combos1) == 2
        assert non_empty_rows_count(link_widget.combos2) == 2
        assert link_widget.combos1.itemAtPosition(
            0, 1).widget().currentText() == 'x'
        assert link_widget.combos1.itemAtPosition(
            1, 1).widget().currentText() == 'y'
        assert link_widget.combos2.itemAtPosition(
            0, 1).widget().currentText() == 'a'
        assert link_widget.combos2.itemAtPosition(
            1, 1).widget().currentText() == 'b'

        dialog.accept()

        links = self.data_collection.external_links

        assert len(links) == 1

        assert isinstance(links[0], ICRS_to_Galactic)
        assert links[0].cids1[0] is self.data1.id['x']
        assert links[0].cids1[1] is self.data1.id['y']
        assert links[0].cids2[0] is self.data2.id['a']
        assert links[0].cids2[1] is self.data2.id['b']
Beispiel #24
0
    def test_session_categorical(self, tmpdir):
        def visible_xaxis_labels(ax):
            # Due to a bug in Matplotlib the labels returned outside the field
            # of view may be incorrect: https://github.com/matplotlib/matplotlib/issues/9397
            pos = ax.xaxis.get_ticklocs()
            labels = [tick.get_text() for tick in ax.xaxis.get_ticklabels()]
            xmin, xmax = ax.get_xlim()
            return [
                labels[i] for i in range(len(pos))
                if pos[i] >= xmin and pos[i] <= xmax
            ]

        # Regression test for a bug that caused a restored scatter viewer
        # with a categorical component to not show the categorical labels
        # as tick labels.

        filename = tmpdir.join('test_session_categorical.glu').strpath

        self.viewer.add_data(self.data)
        self.viewer.state.x_att = self.data.id['z']

        process_events()

        assert visible_xaxis_labels(self.viewer.axes) == ['a', 'b', 'c']

        self.session.application.save_session(filename)

        with open(filename, 'r') as f:
            session = f.read()

        state = GlueUnSerializer.loads(session)

        ga = state.object('__main__')

        dc = ga.session.data_collection

        viewer = ga.viewers[0][0]
        assert viewer.state.x_att is dc[0].id['z']
        assert visible_xaxis_labels(self.viewer.axes) == ['a', 'b', 'c']

        ga.close()
Beispiel #25
0
    def test_basic(self):

        self.viewer.toolbar.active_tool = 'slice'

        self.viewer.axes.figure.canvas.draw()
        process_events()

        x, y = self.viewer.axes.transData.transform([[0.9, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        x, y = self.viewer.axes.transData.transform([[7.2, 6.6]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)

        process_events()

        assert len(self.application.tab().subWindowList()) == 1

        self.viewer.axes.figure.canvas.key_press_event('enter')

        process_events()

        assert len(self.application.tab().subWindowList()) == 2

        pv_widget = self.application.tab().subWindowList()[1].widget()
        assert pv_widget._x.shape == (6, )
        assert pv_widget._y.shape == (6, )
Beispiel #26
0
def test_indexed_data(capsys):

    # Make sure that the image viewer works properly with IndexedData objects

    data_4d = Data(label='hypercube_wcs',
                   x=np.random.random((3, 5, 4, 3)),
                   coords=WCS(naxis=4))

    data_2d = IndexedData(data_4d, (2, None, 3, None))

    application = GlueApplication()

    session = application.session

    hub = session.hub

    data_collection = session.data_collection
    data_collection.append(data_4d)
    data_collection.append(data_2d)

    viewer = application.new_data_viewer(ImageViewer)
    viewer.add_data(data_2d)

    assert viewer.state.x_att is data_2d.pixel_component_ids[1]
    assert viewer.state.y_att is data_2d.pixel_component_ids[0]
    assert viewer.state.x_att_world is data_2d.world_component_ids[1]
    assert viewer.state.y_att_world is data_2d.world_component_ids[0]

    process_events()

    application.close()

    # Some exceptions used to happen during callbacks, and these show up
    # in stderr but don't interrupt the code, so we make sure here that
    # nothing was printed to stdout nor stderr.

    out, err = capsys.readouterr()

    assert out.strip() == ""
    assert err.strip() == ""
Beispiel #27
0
    def test_session_categorical(self, tmpdir):

        def visible_xaxis_labels(ax):
            # Due to a bug in Matplotlib the labels returned outside the field
            # of view may be incorrect: https://github.com/matplotlib/matplotlib/issues/9397
            pos = ax.xaxis.get_ticklocs()
            labels = [tick.get_text() for tick in ax.xaxis.get_ticklabels()]
            xmin, xmax = ax.get_xlim()
            return [labels[i] for i in range(len(pos)) if pos[i] >= xmin and pos[i] <= xmax]

        # Regression test for a bug that caused a restored scatter viewer
        # with a categorical component to not show the categorical labels
        # as tick labels.

        filename = tmpdir.join('test_session_categorical.glu').strpath

        self.viewer.add_data(self.data)
        self.viewer.state.x_att = self.data.id['z']

        process_events()

        assert visible_xaxis_labels(self.viewer.axes) == ['a', 'b', 'c']

        self.session.application.save_session(filename)

        with open(filename, 'r') as f:
            session = f.read()

        state = GlueUnSerializer.loads(session)

        ga = state.object('__main__')

        dc = ga.session.data_collection

        viewer = ga.viewers[0][0]
        assert viewer.state.x_att is dc[0].id['z']
        assert visible_xaxis_labels(self.viewer.axes) == ['a', 'b', 'c']

        ga.close()
Beispiel #28
0
    def test_fit_polynomial(self):

        # TODO: need to deterministically set to polynomial fitter

        self.viewer.add_data(self.data)
        self.profile_tools.ui.tabs.setCurrentIndex(1)

        # First try in pixel coordinates

        self.viewer.state.x_att = self.data.pixel_component_ids[0]

        # Force events to be processed to make sure that the callback functions
        # for the computation thread are executed (since they rely on signals)
        self.viewer.layers[0].wait()
        process_events()

        x, y = self.viewer.axes.transData.transform([[0.9, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        x, y = self.viewer.axes.transData.transform([[15.1, 4]])[0]
        self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1)

        assert_allclose(self.profile_tools.rng_mode.state.x_range, (0.9, 15.1))

        self.profile_tools.ui.button_fit.click()
        self.profile_tools.wait_for_fit()

        # Force events to be processed to make sure that the callback functions
        # for the computation thread are executed (since they rely on signals)
        process_events()

        pixel_log = self.profile_tools.text_log.toPlainText().splitlines()
        assert pixel_log[0] == 'd1'
        assert pixel_log[1] == 'Coefficients:'
        assert pixel_log[-2] == '8.000000e+00'
        assert pixel_log[-1] == '3.500000e+00'

        self.profile_tools.ui.button_clear.click()
        assert self.profile_tools.text_log.toPlainText() == ''

        # Next, try in world coordinates

        self.viewer.state.x_att = self.data.world_component_ids[0]

        x, y = self.viewer.axes.transData.transform([[1.9, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        x, y = self.viewer.axes.transData.transform([[30.1, 4]])[0]
        self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1)

        assert_allclose(self.profile_tools.rng_mode.state.x_range, (1.9, 30.1))

        self.profile_tools.ui.button_fit.click()
        self.profile_tools.wait_for_fit()
        process_events()

        world_log = self.profile_tools.text_log.toPlainText().splitlines()
        assert world_log[0] == 'd1'
        assert world_log[1] == 'Coefficients:'
        assert world_log[-2] == '4.000000e+00'
        assert world_log[-1] == '3.500000e+00'
Beispiel #29
0
    def test_fit_polynomial(self):

        # TODO: need to deterministically set to polynomial fitter

        self.viewer.add_data(self.data)
        self.profile_tools.ui.tabs.setCurrentIndex(1)

        # First try in pixel coordinates

        self.viewer.state.x_att = self.data.pixel_component_ids[0]

        # Force events to be processed to make sure that the callback functions
        # for the computation thread are executed (since they rely on signals)
        self.viewer.layers[0].wait()
        process_events()

        x, y = self.viewer.axes.transData.transform([[0.9, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        x, y = self.viewer.axes.transData.transform([[15.1, 4]])[0]
        self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1)

        assert_allclose(self.profile_tools.rng_mode.state.x_range, (0.9, 15.1))

        self.profile_tools.ui.button_fit.click()
        self.profile_tools.wait_for_fit()

        # Force events to be processed to make sure that the callback functions
        # for the computation thread are executed (since they rely on signals)
        process_events()

        pixel_log = self.profile_tools.text_log.toPlainText().splitlines()
        assert pixel_log[0] == 'd1'
        assert pixel_log[1] == 'Coefficients:'
        assert pixel_log[-2] == '8.000000e+00'
        assert pixel_log[-1] == '3.500000e+00'

        self.profile_tools.ui.button_clear.click()
        assert self.profile_tools.text_log.toPlainText() == ''

        # Next, try in world coordinates

        self.viewer.state.x_att = self.data.world_component_ids[0]

        x, y = self.viewer.axes.transData.transform([[1.9, 4]])[0]
        self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
        x, y = self.viewer.axes.transData.transform([[30.1, 4]])[0]
        self.viewer.axes.figure.canvas.motion_notify_event(x, y, 1)

        assert_allclose(self.profile_tools.rng_mode.state.x_range, (1.9, 30.1))

        self.profile_tools.ui.button_fit.click()
        self.profile_tools.wait_for_fit()
        process_events()

        world_log = self.profile_tools.text_log.toPlainText().splitlines()
        assert world_log[0] == 'd1'
        assert world_log[1] == 'Coefficients:'
        assert world_log[-2] == '4.000000e+00'
        assert world_log[-1] == '3.500000e+00'
Beispiel #30
0
    def test_add_helper(self):

        dialog = LinkEditor(self.data_collection)
        dialog.show()
        link_widget = dialog.link_widget

        link_widget.state.data1 = self.data1
        link_widget.state.data2 = self.data2

        add_coordinate_link = get_action(link_widget, 'ICRS <-> Galactic')

        # Add a coordinate link
        add_coordinate_link.trigger()

        # Ensure that all events get processed
        process_events()

        assert link_widget.listsel_current_link.count() == 1
        assert link_widget.link_details.text() == 'Link ICRS and Galactic coordinates'
        assert non_empty_rows_count(get_link_io(link_widget)) == 7
        assert get_link_io(link_widget).itemAtPosition(1, 1).widget().currentText() == 'x'
        assert get_link_io(link_widget).itemAtPosition(2, 1).widget().currentText() == 'y'
        assert get_link_io(link_widget).itemAtPosition(5, 1).widget().currentText() == 'a'
        assert get_link_io(link_widget).itemAtPosition(6, 1).widget().currentText() == 'b'

        dialog.accept()

        links = self.data_collection.external_links

        assert len(links) == 1

        assert isinstance(links[0], ICRS_to_Galactic)
        assert links[0].cids1[0] is self.data1.id['x']
        assert links[0].cids1[1] is self.data1.id['y']
        assert links[0].cids2[0] is self.data2.id['a']
        assert links[0].cids2[1] is self.data2.id['b']
Beispiel #31
0
def test_incompatible_subset():

    # Regression test for a bug that caused the table to be refreshed in an
    # infinite loop if incompatible subsets were present.

    data1 = Data(a=[1, 2, 3, 4, 5], label='test1')
    data2 = Data(a=[1, 2, 3, 4, 5], label='test2')
    dc = DataCollection([data1, data2])

    gapp = GlueApplication(dc)

    viewer = gapp.new_data_viewer(TableViewer)
    viewer.add_data(data1)

    dc.new_subset_group('test subset', data2.id['a'] > 2)
    gapp.show()
    process_events(0.5)

    with patch.object(viewer.layers[0], '_refresh') as refresh1:
        with patch.object(viewer.layers[1], '_refresh') as refresh2:
            process_events(0.5)

    assert refresh1.call_count == 0
    assert refresh2.call_count == 0
Beispiel #32
0
    def test_close_on_last_layer_remove(self):

        # regression test for 391

        d1 = Data(x=np.random.random((2,) * self.ndim))
        d2 = Data(y=np.random.random((2,) * self.ndim))
        dc = DataCollection([d1, d2])
        app = GlueApplication(dc)
        w = app.new_data_viewer(self.widget_cls, data=d1)
        w.add_data(d2)
        process_events()
        assert len(app.viewers[0]) == 1
        dc.remove(d1)
        process_events()
        assert len(app.viewers[0]) == 1
        dc.remove(d2)
        process_events()
        assert len(app.viewers[0]) == 0
        app.close()
Beispiel #33
0
    def test_close_on_last_layer_remove(self):

        # regression test for 391

        d1 = Data(x=np.random.random((2, ) * self.ndim))
        d2 = Data(y=np.random.random((2, ) * self.ndim))
        dc = DataCollection([d1, d2])
        app = GlueApplication(dc)
        w = app.new_data_viewer(self.widget_cls, data=d1)
        w.add_data(d2)
        process_events()
        assert len(app.viewers[0]) == 1
        dc.remove(d1)
        process_events()
        assert len(app.viewers[0]) == 1
        dc.remove(d2)
        process_events()
        assert len(app.viewers[0]) == 0
        app.close()
Beispiel #34
0
    def test_ui_behavior(self):

        # This is a bit more detailed test that checks that things update
        # correctly as we change various settings

        dialog = LinkEditor(self.data_collection)
        dialog.show()
        link_widget = dialog.link_widget

        link_widget.state.data1 = self.data1
        link_widget.state.data2 = self.data2

        add_identity_link = get_action(link_widget, 'identity')
        add_lengths_volume_link = get_action(link_widget, 'lengths_to_volume')

        # At this point, there should be no links in the main list widget
        # and nothing on the right.
        assert link_widget.listsel_current_link.count() == 0
        assert link_widget.link_details.text() == ''
        assert non_empty_rows_count(link_widget.combos1) == 0
        assert non_empty_rows_count(link_widget.combos2) == 0

        # Let's add an identity link
        add_identity_link.trigger()

        # Ensure that all events get processed
        process_events()

        # Now there should be one link in the main list and content in the
        # right hand panel.
        assert link_widget.listsel_current_link.count() == 1
        assert link_widget.link_details.text(
        ) == 'Link conceptually identical components'
        assert non_empty_rows_count(link_widget.combos1) == 1
        assert non_empty_rows_count(link_widget.combos2) == 1
        assert link_widget.combos1.itemAtPosition(
            0, 1).widget().currentText() == 'x'
        assert link_widget.combos2.itemAtPosition(
            0, 1).widget().currentText() == 'a'

        # Let's change the current components for the link
        link_widget.state.current_link.x = self.data1.id['y']
        link_widget.state.current_link.y = self.data2.id['b']

        # and make sure the UI gets updated
        assert link_widget.combos1.itemAtPosition(
            0, 1).widget().currentText() == 'y'
        assert link_widget.combos2.itemAtPosition(
            0, 1).widget().currentText() == 'b'

        # We now add another link of a different type
        add_lengths_volume_link.trigger()

        # Ensure that all events get processed
        process_events()

        # and make sure the UI has updated
        assert link_widget.listsel_current_link.count() == 2
        assert link_widget.link_details.text(
        ) == 'Convert between linear measurements and volume'
        assert non_empty_rows_count(link_widget.combos1) == 3
        assert non_empty_rows_count(link_widget.combos2) == 1
        assert link_widget.combos1.itemAtPosition(
            0, 1).widget().currentText() == 'x'
        assert link_widget.combos1.itemAtPosition(
            1, 1).widget().currentText() == 'y'
        assert link_widget.combos1.itemAtPosition(
            2, 1).widget().currentText() == 'z'
        assert link_widget.combos2.itemAtPosition(
            0, 1).widget().currentText() == 'a'

        # Try swapping the order of the data, the current link should stay the same
        link_widget.state.flip_data()
        assert link_widget.link_details.text(
        ) == 'Convert between linear measurements and volume'

        # And flip it back
        link_widget.state.flip_data()
        assert link_widget.link_details.text(
        ) == 'Convert between linear measurements and volume'

        # Now switch back to the first link
        link_widget.state.current_link = type(
            link_widget.state).current_link.get_choices(link_widget.state)[0]

        # and make sure the UI updates and has preserved the correct settings
        assert link_widget.listsel_current_link.count() == 2
        assert link_widget.link_details.text(
        ) == 'Link conceptually identical components'
        assert non_empty_rows_count(link_widget.combos1) == 1
        assert non_empty_rows_count(link_widget.combos2) == 1
        assert link_widget.combos1.itemAtPosition(
            0, 1).widget().currentText() == 'y'
        assert link_widget.combos2.itemAtPosition(
            0, 1).widget().currentText() == 'b'

        # Next up, we try changing the data

        link_widget.state.data1 = self.data3

        # At this point there should be no links in the list

        assert link_widget.listsel_current_link.count() == 0
        assert link_widget.link_details.text() == ''
        assert non_empty_rows_count(link_widget.combos1) == 0
        assert non_empty_rows_count(link_widget.combos2) == 0

        # Add another identity link
        add_identity_link.trigger()

        # Ensure that all events get processed
        process_events()

        # Now there should be one link in the main list
        assert link_widget.listsel_current_link.count() == 1
        assert link_widget.link_details.text(
        ) == 'Link conceptually identical components'
        assert non_empty_rows_count(link_widget.combos1) == 1
        assert non_empty_rows_count(link_widget.combos2) == 1
        assert link_widget.combos1.itemAtPosition(
            0, 1).widget().currentText() == 'i'
        assert link_widget.combos2.itemAtPosition(
            0, 1).widget().currentText() == 'a'

        # Switch back to the original data
        link_widget.state.data1 = self.data1

        # And check the output is as before
        assert link_widget.listsel_current_link.count() == 2
        assert link_widget.link_details.text(
        ) == 'Link conceptually identical components'
        assert non_empty_rows_count(link_widget.combos1) == 1
        assert non_empty_rows_count(link_widget.combos2) == 1
        assert link_widget.combos1.itemAtPosition(
            0, 1).widget().currentText() == 'y'
        assert link_widget.combos2.itemAtPosition(
            0, 1).widget().currentText() == 'b'

        # Let's now remove this link
        link_widget.button_remove_link.click()

        # Ensure that all events get processed
        process_events()

        # We should now see the lengths/volume link
        assert link_widget.listsel_current_link.count() == 1
        assert link_widget.link_details.text(
        ) == 'Convert between linear measurements and volume'
        assert non_empty_rows_count(link_widget.combos1) == 3
        assert non_empty_rows_count(link_widget.combos2) == 1
        assert link_widget.combos1.itemAtPosition(
            0, 1).widget().currentText() == 'x'
        assert link_widget.combos1.itemAtPosition(
            1, 1).widget().currentText() == 'y'
        assert link_widget.combos1.itemAtPosition(
            2, 1).widget().currentText() == 'z'
        assert link_widget.combos2.itemAtPosition(
            0, 1).widget().currentText() == 'a'

        dialog.accept()

        links = self.data_collection.external_links

        assert len(links) == 2

        assert isinstance(links[0], ComponentLink)
        assert links[0].get_from_ids()[0] is self.data1.id['x']
        assert links[0].get_from_ids()[1] is self.data1.id['y']
        assert links[0].get_from_ids()[2] is self.data1.id['z']
        assert links[0].get_to_id() is self.data2.id['a']

        assert isinstance(links[1], ComponentLink)
        assert links[1].get_from_ids()[0] is self.data3.id['i']
        assert links[1].get_to_id() is self.data2.id['a']
Beispiel #35
0
 def set_status(self, text, color):
     self.ui.text_status.setText(text)
     self.ui.text_status.setStyleSheet("color: {0}".format(color))
     process_events()
Beispiel #36
0
 def __init__(self, figure):
     self.figure = figure
     process_events()
     self.start = self.figure.canvas._draw_count
Beispiel #37
0
 def draw_count(self):
     process_events()
     process_events()
     return self.figure.canvas._draw_count - self.start
Beispiel #38
0
    def test_aspect_resize(self):

        # Make sure that the limits are adjusted appropriately when resizing
        # depending on the aspect ratio mode. Note that we don't add any data
        # here since it isn't needed for this test.

        # This test works with Matplotlib 2.0 and 2.2 but not 2.1, hence we
        # skip it with Matplotlib 2.1 above.

        # Note that we need to explicitly call draw() below because otherwise
        # draw_idle is used, which has no guarantee of being effective.

        # Set initial limits to deterministic values
        self.viewer.state.aspect = 'auto'
        self.viewer.state.x_min = 0.
        self.viewer.state.x_max = 1.
        self.viewer.state.y_min = 0.
        self.viewer.state.y_max = 1.

        self.viewer.state.aspect = 'equal'

        # Resize events only work if widget is visible
        self.viewer.show()
        self.viewer.figure.canvas.draw()
        process_events(wait=0.1)

        def limits(viewer):
            return (viewer.state.x_min, viewer.state.x_max,
                    viewer.state.y_min, viewer.state.y_max)

        # Set viewer to an initial size and save limits
        self.viewer.viewer_size = (800, 400)
        self.viewer.figure.canvas.draw()
        process_events(wait=0.1)
        initial_limits = limits(self.viewer)

        # Change the viewer size, and make sure the limits are adjusted
        self.viewer.viewer_size = (400, 400)
        self.viewer.figure.canvas.draw()
        process_events(wait=0.1)
        with pytest.raises(AssertionError):
            assert_allclose(limits(self.viewer), initial_limits)

        # Now change the viewer size a number of times and make sure if we
        # return to the original size, the limits match the initial ones.
        self.viewer.viewer_size = (350, 800)
        self.viewer.figure.canvas.draw()
        process_events(wait=0.1)
        self.viewer.viewer_size = (900, 300)
        self.viewer.figure.canvas.draw()
        process_events(wait=0.1)
        self.viewer.viewer_size = (600, 600)
        self.viewer.figure.canvas.draw()
        process_events(wait=0.1)
        self.viewer.viewer_size = (800, 400)
        self.viewer.figure.canvas.draw()
        process_events(wait=0.1)
        assert_allclose(limits(self.viewer), initial_limits)

        # Now check that the limits don't change in 'auto' mode
        self.viewer.state.aspect = 'auto'
        self.viewer.viewer_size = (900, 300)
        self.viewer.figure.canvas.draw()
        process_events(wait=0.1)
        assert_allclose(limits(self.viewer), initial_limits)
Beispiel #39
0
 def viewer_count(self):
     process_events()
     obj = objgraph.by_type(self.viewer_cls.__name__)
     return len(obj)
Beispiel #40
0
    def test_disable_incompatible(self):

        # Test to make sure that image and image subset layers are disabled if
        # their pixel coordinates are not compatible with the ones of the
        # reference data.

        self.viewer.add_data(self.image1)
        self.viewer.add_data(self.image2)

        assert self.viewer.state.reference_data is self.image1

        self.data_collection.new_subset_group()

        process_events()

        assert len(self.viewer.layers) == 4

        # Only the two layers associated with the reference data should be enabled
        for layer_artist in self.viewer.layers:
            if layer_artist.layer in (self.image1, self.image1.subsets[0]):
                assert layer_artist.enabled
            else:
                assert not layer_artist.enabled

        py1, px1 = self.image1.pixel_component_ids
        py2, px2 = self.image2.pixel_component_ids

        link1 = LinkSame(px1, px2)
        self.data_collection.add_link(link1)

        process_events()

        # One link isn't enough, second dataset layers are still not enabled

        for layer_artist in self.viewer.layers:
            if layer_artist.layer in (self.image1, self.image1.subsets[0]):
                assert layer_artist.enabled
            else:
                assert not layer_artist.enabled

        link2 = LinkSame(py1, py2)
        self.data_collection.add_link(link2)

        process_events()

        # All layers should now be enabled

        for layer_artist in self.viewer.layers:
            assert layer_artist.enabled

        self.data_collection.remove_link(link2)

        process_events()

        # We should now be back to the original situation

        for layer_artist in self.viewer.layers:
            if layer_artist.layer in (self.image1, self.image1.subsets[0]):
                assert layer_artist.enabled
            else:
                assert not layer_artist.enabled
Beispiel #41
0
def test_table_widget(tmpdir):

    # Start off by creating a glue application instance with a table viewer and
    # some data pre-loaded.

    app = get_qapp()

    d = Data(a=[1, 2, 3, 4, 5],
             b=[3.2, 1.2, 4.5, 3.3, 2.2],
             c=['e', 'b', 'c', 'a', 'f'])

    dc = DataCollection([d])

    gapp = GlueApplication(dc)

    widget = gapp.new_data_viewer(TableViewer)
    widget.add_data(d)

    subset_mode = gapp._session.edit_subset_mode

    # Create two subsets

    sg1 = dc.new_subset_group('D <= 3', d.id['a'] <= 3)
    sg1.style.color = '#aa0000'
    sg2 = dc.new_subset_group('1 < D < 4', (d.id['a'] > 1) & (d.id['a'] < 4))
    sg2.style.color = '#0000cc'

    model = widget.ui.table.model()

    # We now check what the data and colors of the table are, and try various
    # sorting methods to make sure that things are still correct.

    data = {'a': [1, 2, 3, 4, 5],
            'b': [3.2, 1.2, 4.5, 3.3, 2.2],
            'c': ['e', 'b', 'c', 'a', 'f']}

    colors = ['#aa0000', '#380088', '#380088', None, None]

    check_values_and_color(model, data, colors)

    model.sort(1, Qt.AscendingOrder)

    data = {'a': [2, 5, 1, 4, 3],
            'b': [1.2, 2.2, 3.2, 3.3, 4.5],
            'c': ['b', 'f', 'e', 'a', 'c']}

    colors = ['#380088', None, '#aa0000', None, '#380088']

    check_values_and_color(model, data, colors)

    model.sort(2, Qt.AscendingOrder)

    data = {'a': [4, 2, 3, 1, 5],
            'b': [3.3, 1.2, 4.5, 3.2, 2.2],
            'c': ['a', 'b', 'c', 'e', 'f']}

    colors = [None, '#380088', '#380088', '#aa0000', None]

    check_values_and_color(model, data, colors)

    model.sort(0, Qt.DescendingOrder)

    data = {'a': [5, 4, 3, 2, 1],
            'b': [2.2, 3.3, 4.5, 1.2, 3.2],
            'c': ['f', 'a', 'c', 'b', 'e']}

    colors = [None, None, '#380088', '#380088', '#aa0000']

    check_values_and_color(model, data, colors)

    model.sort(0, Qt.AscendingOrder)

    # We now modify the subsets using the table.

    selection = widget.ui.table.selectionModel()

    widget.toolbar.actions['table:rowselect'].toggle()

    def press_key(key):
        event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, key, Qt.NoModifier)
        app.postEvent(widget.ui.table, event)
        app.processEvents()

    process_events()

    # We now use key presses to navigate down to the third row

    press_key(Qt.Key_Tab)
    press_key(Qt.Key_Down)
    press_key(Qt.Key_Down)

    process_events()

    indices = selection.selectedRows()

    # We make sure that the third row is selected

    assert len(indices) == 1
    assert indices[0].row() == 2

    # At this point, the subsets haven't changed yet

    np.testing.assert_equal(d.subsets[0].to_mask(), [1, 1, 1, 0, 0])
    np.testing.assert_equal(d.subsets[1].to_mask(), [0, 1, 1, 0, 0])

    # We specify that we are editing the second subset, and use a 'not' logical
    # operation to remove the currently selected line from the second subset.

    subset_mode.edit_subset = [d.subsets[1]]
    subset_mode.mode = AndNotMode

    press_key(Qt.Key_Enter)

    np.testing.assert_equal(d.subsets[0].to_mask(), [1, 1, 1, 0, 0])
    np.testing.assert_equal(d.subsets[1].to_mask(), [0, 1, 0, 0, 0])

    # At this point, the selection should be cleared

    indices = selection.selectedRows()
    assert len(indices) == 0

    # We move to the fourth row and now do an 'or' selection with the first
    # subset.

    press_key(Qt.Key_Down)

    subset_mode.mode = OrMode

    subset_mode.edit_subset = [d.subsets[0]]

    press_key(Qt.Key_Enter)

    np.testing.assert_equal(d.subsets[0].to_mask(), [1, 1, 1, 1, 0])
    np.testing.assert_equal(d.subsets[1].to_mask(), [0, 1, 0, 0, 0])

    # Finally we move to the fifth row and deselect all subsets so that
    # pressing enter now creates a new subset.

    press_key(Qt.Key_Down)

    subset_mode.mode = ReplaceMode

    subset_mode.edit_subset = None

    press_key(Qt.Key_Enter)

    np.testing.assert_equal(d.subsets[0].to_mask(), [1, 1, 1, 1, 0])
    np.testing.assert_equal(d.subsets[1].to_mask(), [0, 1, 0, 0, 0])
    np.testing.assert_equal(d.subsets[2].to_mask(), [0, 0, 0, 0, 1])

    # Make the color for the new subset deterministic
    dc.subset_groups[2].style.color = '#bababa'

    # Now finally check saving and restoring session

    session_file = tmpdir.join('table.glu').strpath

    gapp.save_session(session_file)

    gapp2 = GlueApplication.restore_session(session_file)
    gapp2.show()

    d = gapp2.data_collection[0]

    widget2 = gapp2.viewers[0][0]

    model2 = widget2.ui.table.model()

    data = {'a': [1, 2, 3, 4, 5],
            'b': [3.2, 1.2, 4.5, 3.3, 2.2],
            'c': ['e', 'b', 'c', 'a', 'f']}

    # Need to take into account new selections above
    colors = ['#aa0000', '#380088', '#aa0000', "#aa0000", "#bababa"]

    check_values_and_color(model2, data, colors)
Beispiel #42
0
    def test_ui_behavior(self):

        # This is a bit more detailed test that checks that things update
        # correctly as we change various settings

        dialog = LinkEditor(self.data_collection)
        dialog.show()
        link_widget = dialog.link_widget

        link_widget.state.data1 = self.data1
        link_widget.state.data2 = self.data2

        add_identity_link = get_action(link_widget, 'identity')
        add_lengths_volume_link = get_action(link_widget, 'lengths_to_volume')

        # At this point, there should be no links in the main list widget
        # and nothing on the right.
        assert link_widget.listsel_current_link.count() == 0
        assert link_widget.link_details.text() == ''
        assert link_widget.link_io.itemAt(0) is None

        # Let's add an identity link
        add_identity_link.trigger()

        # Ensure that all events get processed
        process_events()

        # Now there should be one link in the main list and content in the
        # right hand panel.
        assert link_widget.listsel_current_link.count() == 1
        assert link_widget.link_details.text() == 'Link conceptually identical components'
        assert non_empty_rows_count(get_link_io(link_widget)) == 5
        assert get_link_io(link_widget).itemAtPosition(1, 1).widget().currentText() == 'x'
        assert get_link_io(link_widget).itemAtPosition(4, 1).widget().currentText() == 'a'

        # Let's change the current components for the link
        link_widget.state.current_link.x = self.data1.id['y']
        link_widget.state.current_link.y = self.data2.id['b']

        # and make sure the UI gets updated
        assert get_link_io(link_widget).itemAtPosition(1, 1).widget().currentText() == 'y'
        assert get_link_io(link_widget).itemAtPosition(4, 1).widget().currentText() == 'b'

        # We now add another link of a different type
        add_lengths_volume_link.trigger()

        # Ensure that all events get processed
        process_events()

        # and make sure the UI has updated
        assert link_widget.listsel_current_link.count() == 2
        assert link_widget.link_details.text() == 'Convert between linear measurements and volume'
        assert non_empty_rows_count(get_link_io(link_widget)) == 7
        assert get_link_io(link_widget).itemAtPosition(1, 1).widget().currentText() == 'x'
        assert get_link_io(link_widget).itemAtPosition(2, 1).widget().currentText() == 'y'
        assert get_link_io(link_widget).itemAtPosition(3, 1).widget().currentText() == 'z'
        assert get_link_io(link_widget).itemAtPosition(6, 1).widget().currentText() == 'a'

        # Now switch back to the first link
        link_widget.state.current_link = type(link_widget.state).current_link.get_choices(link_widget.state)[0]

        # and make sure the UI updates and has preserved the correct settings
        assert link_widget.listsel_current_link.count() == 2
        assert link_widget.link_details.text() == 'Link conceptually identical components'
        assert non_empty_rows_count(get_link_io(link_widget)) == 5
        assert get_link_io(link_widget).itemAtPosition(1, 1).widget().currentText() == 'y'
        assert get_link_io(link_widget).itemAtPosition(4, 1).widget().currentText() == 'b'

        # Next up, we try changing the data

        link_widget.state.data1 = self.data3

        # At this point there should be no links in the list

        assert link_widget.listsel_current_link.count() == 0
        assert link_widget.link_details.text() == ''
        assert link_widget.link_io.itemAt(0) is None

        # Add another identity link
        add_identity_link.trigger()

        # Ensure that all events get processed
        process_events()

        # Now there should be one link in the main list
        assert link_widget.listsel_current_link.count() == 1
        assert link_widget.link_details.text() == 'Link conceptually identical components'
        assert non_empty_rows_count(get_link_io(link_widget)) == 5
        assert get_link_io(link_widget).itemAtPosition(1, 1).widget().currentText() == 'i'
        assert get_link_io(link_widget).itemAtPosition(4, 1).widget().currentText() == 'a'

        # Switch back to the original data
        link_widget.state.data1 = self.data1

        # And check the output is as before
        assert link_widget.listsel_current_link.count() == 2
        assert link_widget.link_details.text() == 'Link conceptually identical components'
        assert non_empty_rows_count(get_link_io(link_widget)) == 5
        assert get_link_io(link_widget).itemAtPosition(1, 1).widget().currentText() == 'y'
        assert get_link_io(link_widget).itemAtPosition(4, 1).widget().currentText() == 'b'

        # Let's now remove this link
        link_widget.button_remove_link.click()

        # Ensure that all events get processed
        process_events()

        # We should now see the lengths/volume link
        assert link_widget.listsel_current_link.count() == 1
        assert link_widget.link_details.text() == 'Convert between linear measurements and volume'
        assert non_empty_rows_count(get_link_io(link_widget)) == 7
        assert get_link_io(link_widget).itemAtPosition(1, 1).widget().currentText() == 'x'
        assert get_link_io(link_widget).itemAtPosition(2, 1).widget().currentText() == 'y'
        assert get_link_io(link_widget).itemAtPosition(3, 1).widget().currentText() == 'z'
        assert get_link_io(link_widget).itemAtPosition(6, 1).widget().currentText() == 'a'

        dialog.accept()

        links = self.data_collection.external_links

        assert len(links) == 2

        assert isinstance(links[0], ComponentLink)
        assert links[0].get_from_ids()[0] is self.data1.id['x']
        assert links[0].get_from_ids()[1] is self.data1.id['y']
        assert links[0].get_from_ids()[2] is self.data1.id['z']
        assert links[0].get_to_id() is self.data2.id['a']

        assert isinstance(links[1], ComponentLink)
        assert links[1].get_from_ids()[0] is self.data3.id['i']
        assert links[1].get_to_id() is self.data2.id['a']
Beispiel #43
0
def test_table_widget(tmpdir):

    # Start off by creating a glue application instance with a table viewer and
    # some data pre-loaded.

    app = get_qapp()

    d = Data(a=[1, 2, 3, 4, 5],
             b=[3.2, 1.2, 4.5, 3.3, 2.2],
             c=['e', 'b', 'c', 'a', 'f'])

    dc = DataCollection([d])

    gapp = GlueApplication(dc)

    widget = gapp.new_data_viewer(TableViewer)
    widget.add_data(d)

    subset_mode = gapp._session.edit_subset_mode

    # Create two subsets

    sg1 = dc.new_subset_group('D <= 3', d.id['a'] <= 3)
    sg1.style.color = '#aa0000'
    sg2 = dc.new_subset_group('1 < D < 4', (d.id['a'] > 1) & (d.id['a'] < 4))
    sg2.style.color = '#0000cc'

    model = widget.ui.table.model()

    # We now check what the data and colors of the table are, and try various
    # sorting methods to make sure that things are still correct.

    data = {
        'a': [1, 2, 3, 4, 5],
        'b': [3.2, 1.2, 4.5, 3.3, 2.2],
        'c': ['e', 'b', 'c', 'a', 'f']
    }

    colors = ['#aa0000', '#380088', '#380088', None, None]

    check_values_and_color(model, data, colors)

    model.sort(1, Qt.AscendingOrder)

    data = {
        'a': [2, 5, 1, 4, 3],
        'b': [1.2, 2.2, 3.2, 3.3, 4.5],
        'c': ['b', 'f', 'e', 'a', 'c']
    }

    colors = ['#380088', None, '#aa0000', None, '#380088']

    check_values_and_color(model, data, colors)

    model.sort(2, Qt.AscendingOrder)

    data = {
        'a': [4, 2, 3, 1, 5],
        'b': [3.3, 1.2, 4.5, 3.2, 2.2],
        'c': ['a', 'b', 'c', 'e', 'f']
    }

    colors = [None, '#380088', '#380088', '#aa0000', None]

    check_values_and_color(model, data, colors)

    model.sort(0, Qt.DescendingOrder)

    data = {
        'a': [5, 4, 3, 2, 1],
        'b': [2.2, 3.3, 4.5, 1.2, 3.2],
        'c': ['f', 'a', 'c', 'b', 'e']
    }

    colors = [None, None, '#380088', '#380088', '#aa0000']

    check_values_and_color(model, data, colors)

    model.sort(0, Qt.AscendingOrder)

    # We now modify the subsets using the table.

    selection = widget.ui.table.selectionModel()

    widget.toolbar.actions['table:rowselect'].toggle()

    def press_key(key):
        event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, key, Qt.NoModifier)
        app.postEvent(widget.ui.table, event)
        app.processEvents()

    process_events()

    # We now use key presses to navigate down to the third row

    press_key(Qt.Key_Tab)
    press_key(Qt.Key_Down)
    press_key(Qt.Key_Down)

    process_events()

    indices = selection.selectedRows()

    # We make sure that the third row is selected

    assert len(indices) == 1
    assert indices[0].row() == 2

    # At this point, the subsets haven't changed yet

    np.testing.assert_equal(d.subsets[0].to_mask(), [1, 1, 1, 0, 0])
    np.testing.assert_equal(d.subsets[1].to_mask(), [0, 1, 1, 0, 0])

    # We specify that we are editing the second subset, and use a 'not' logical
    # operation to remove the currently selected line from the second subset.

    subset_mode.edit_subset = [d.subsets[1]]
    subset_mode.mode = AndNotMode

    press_key(Qt.Key_Enter)

    np.testing.assert_equal(d.subsets[0].to_mask(), [1, 1, 1, 0, 0])
    np.testing.assert_equal(d.subsets[1].to_mask(), [0, 1, 0, 0, 0])

    # At this point, the selection should be cleared

    indices = selection.selectedRows()
    assert len(indices) == 0

    # We move to the fourth row and now do an 'or' selection with the first
    # subset.

    press_key(Qt.Key_Down)

    subset_mode.mode = OrMode

    subset_mode.edit_subset = [d.subsets[0]]

    press_key(Qt.Key_Enter)

    np.testing.assert_equal(d.subsets[0].to_mask(), [1, 1, 1, 1, 0])
    np.testing.assert_equal(d.subsets[1].to_mask(), [0, 1, 0, 0, 0])

    # Finally we move to the fifth row and deselect all subsets so that
    # pressing enter now creates a new subset.

    press_key(Qt.Key_Down)

    subset_mode.mode = ReplaceMode

    subset_mode.edit_subset = None

    press_key(Qt.Key_Enter)

    np.testing.assert_equal(d.subsets[0].to_mask(), [1, 1, 1, 1, 0])
    np.testing.assert_equal(d.subsets[1].to_mask(), [0, 1, 0, 0, 0])
    np.testing.assert_equal(d.subsets[2].to_mask(), [0, 0, 0, 0, 1])

    # Make the color for the new subset deterministic
    dc.subset_groups[2].style.color = '#bababa'

    # Now finally check saving and restoring session

    session_file = tmpdir.join('table.glu').strpath

    gapp.save_session(session_file)

    gapp2 = GlueApplication.restore_session(session_file)
    gapp2.show()

    d = gapp2.data_collection[0]

    widget2 = gapp2.viewers[0][0]

    model2 = widget2.ui.table.model()

    data = {
        'a': [1, 2, 3, 4, 5],
        'b': [3.2, 1.2, 4.5, 3.3, 2.2],
        'c': ['e', 'b', 'c', 'a', 'f']
    }

    # Need to take into account new selections above
    colors = ['#aa0000', '#380088', '#aa0000', "#aa0000", "#bababa"]

    check_values_and_color(model2, data, colors)
Beispiel #44
0
    def test_disable_incompatible(self):

        # Test to make sure that image and image subset layers are disabled if
        # their pixel coordinates are not compatible with the ones of the
        # reference data.

        self.viewer.add_data(self.image1)
        self.viewer.add_data(self.image2)

        assert self.viewer.state.reference_data is self.image1

        self.data_collection.new_subset_group()

        process_events()

        assert len(self.viewer.layers) == 4

        # Only the two layers associated with the reference data should be enabled
        for layer_artist in self.viewer.layers:
            if layer_artist.layer in (self.image1, self.image1.subsets[0]):
                assert layer_artist.enabled
            else:
                assert not layer_artist.enabled

        py1, px1 = self.image1.pixel_component_ids
        py2, px2 = self.image2.pixel_component_ids

        link1 = LinkSame(px1, px2)
        self.data_collection.add_link(link1)

        process_events()

        # One link isn't enough, second dataset layers are still not enabled

        for layer_artist in self.viewer.layers:
            if layer_artist.layer in (self.image1, self.image1.subsets[0]):
                assert layer_artist.enabled
            else:
                assert not layer_artist.enabled

        link2 = LinkSame(py1, py2)
        self.data_collection.add_link(link2)

        process_events()

        # All layers should now be enabled

        for layer_artist in self.viewer.layers:
            assert layer_artist.enabled

        self.data_collection.remove_link(link2)

        process_events()

        # We should now be back to the original situation

        for layer_artist in self.viewer.layers:
            if layer_artist.layer in (self.image1, self.image1.subsets[0]):
                assert layer_artist.enabled
            else:
                assert not layer_artist.enabled