class TestOpenSpaceViewer:
    def setup_method(self, method):

        self.data1 = 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.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.app = GlueApplication()

        self.data_collection = self.app.session.data_collection
        self.data_collection.append(self.data1)
        self.data_collection.append(self.data2)

        self.viewer = self.app.new_data_viewer(OpenSpaceDataViewer)

    def test_add_single_data(self, websocket_server):
        self.viewer.connect_to_openspace()
        assert len(websocket_server.messages) == 0
        self.viewer.add_data(self.data1)
        assert len(websocket_server.messages) == 1
Пример #2
0
def test_table_widget_session_no_subset(tmpdir):

    # Regression test for a bug that caused table viewers with no subsets to
    # not be restored correctly and instead raise an exception.

    app = get_qapp()  # noqa

    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'], label='test')

    dc = DataCollection([d])

    gapp = GlueApplication(dc)

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

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

    gapp.save_session(session_file)

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

    gapp2.data_collection[0]
    gapp2.viewers[0][0]
Пример #3
0
class TestScatter3D:

    def setup_method(self, method):
        self.data = Data(x=[1, 2, 3], y=[4, 5, 6], z=[7, 8, 9], label='d1')
        self.app = GlueApplication()
        self.app.session.data_collection.append(self.data)
        self.viewer = self.app.new_data_viewer(VispyScatterViewer)
        self.viewer.add_data(self.data)
        for subtool in self.viewer.toolbar.tools['save'].subtools:
            if subtool.tool_id == 'save:plotly3d':
                self.tool = subtool
                break
        else:
            raise Exception("Could not find save:plotly2d tool in viewer")

    def teardown_method(self, method):
        self.viewer.close(warn=False)
        self.viewer = None
        self.app.close()
        self.app = None

    def test_default(self, tmpdir):
        output_file = tmpdir.join('test.html').strpath
        with patch('qtpy.compat.getsavefilename') as fd:
            fd.return_value = output_file, 'html'
            with patch.object(SaveHoverDialog, 'exec_', auto_accept()):
                self.tool.activate()
        assert os.path.exists(output_file)
Пример #4
0
def test_table_widget_session_no_subset(tmpdir):

    # Regression test for a bug that caused table viewers with no subsets to
    # not be restored correctly and instead raise an exception.

    app = get_qapp()  # noqa

    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'],
             label='test')

    dc = DataCollection([d])

    gapp = GlueApplication(dc)

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

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

    gapp.save_session(session_file)

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

    gapp2.data_collection[0]
    gapp2.viewers[0][0]
Пример #5
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() == ""
Пример #6
0
    def test_single_draw_call_on_create(self):
        d = Data(x=np.random.random((2,) * self.ndim))
        dc = DataCollection([d])
        app = GlueApplication(dc)

        try:
            from glue.viewers.common.qt.mpl_widget import MplCanvas
            draw = MplCanvas.draw
            MplCanvas.draw = MagicMock()

            app.new_data_viewer(self.widget_cls, data=d)

            # each Canvas instance gives at most 1 draw call
            selfs = [c[0][0] for c in MplCanvas.draw.call_arg_list]
            assert len(set(selfs)) == len(selfs)
        finally:
            MplCanvas.draw = draw
Пример #7
0
def test_state_save_with_data_layers():
    app = GlueApplication()
    dc = app.data_collection
    d = Data(x=[1, 2, 3], label='test')
    dc.append(d)
    w = app.new_data_viewer(viewer._widget_cls)
    w.add_data(d)
    check_clone_app(app)
Пример #8
0
    def test_single_draw_call_on_create(self):
        d = Data(x=np.random.random((2,) * self.ndim))
        dc = DataCollection([d])
        app = GlueApplication(dc)

        try:
            from glue.viewers.matplotlib.qt.widget import MplCanvas
            draw = MplCanvas.draw
            MplCanvas.draw = MagicMock()

            app.new_data_viewer(self.widget_cls, data=d)

            # each Canvas instance gives at most 1 draw call
            selfs = [c[0][0] for c in MplCanvas.draw.call_arg_list]
            assert len(set(selfs)) == len(selfs)
        finally:
            MplCanvas.draw = draw
Пример #9
0
def test_state_save_with_data_layers():
    app = GlueApplication()
    dc = app.data_collection
    d = Data(x=[1, 2, 3], label='test')
    dc.append(d)
    w = app.new_data_viewer(viewer._widget_cls)
    w.add_data(d)
    check_clone_app(app)
Пример #10
0
 def test_save_load(self):
     app = GlueApplication(session=self.session)
     w = app.new_data_viewer(self.viewer._viewer_cls)
     v = w._coordinator
     roi = None
     s = CustomSubsetState(v, roi)
     app.data_collection.new_subset_group(subset_state=s, label='test')
     app2 = clone(app)
     s2 = app2.data_collection[0].subsets[0].subset_state
     assert_array_equal(s2.to_mask(self.data), [False, True, True])
Пример #11
0
 def test_save_load(self):
     app = GlueApplication(session=self.session)
     w = app.new_data_viewer(self.viewer._viewer_cls)
     v = w._coordinator
     roi = None
     s = CustomSubsetState(v, roi)
     app.data_collection.new_subset_group(subset_state=s, label='test')
     app2 = clone(app)
     s2 = app2.data_collection[0].subsets[0].subset_state
     assert_array_equal(s2.to_mask(self.data), [False, True, True])
Пример #12
0
    def test_cube(self):
        d = core.Data(label='cube', x=np.zeros((2, 2, 2)))
        dc = core.DataCollection([d])
        app = GlueApplication(dc)
        w = app.new_data_viewer(ImageWidget, d)
        w.slice = ('x', 'y', 1)
        assert w.slice == ('x', 'y', 1)

        c = self.check_clone(app)
        w2 = c.viewers[0][0]
        assert w2.ui.slice.slice == w.slice
Пример #13
0
    def test_cube(self):
        d = core.Data(label='cube',
                      x=np.zeros((2, 2, 2)))
        dc = core.DataCollection([d])
        app = GlueApplication(dc)
        w = app.new_data_viewer(ImageWidget, d)
        w.slice = ('x', 'y', 1)
        assert w.slice == ('x', 'y', 1)

        c = self.check_clone(app)
        w2 = c.viewers[0][0]
        assert w2.ui.slice.slice == w.slice
Пример #14
0
def test_two_custom_viewer_classes():
    class MyWidget1(CustomViewer):

        text_box1_Widget1 = '_Hello'

        def setup(self, text_box1_Widget1):
            pass

    class MyWidget2(CustomViewer):

        text_box1_Widget2 = '_Hello'
        text_box2_Widget2 = '_world'

        def setup(self, text_box1_Widget2, text_box2_Widget2):
            pass

    app = GlueApplication()
    dc = app.data_collection
    d = Data(x=[1, 2, 3], label='test')
    dc.append(d)
    app.new_data_viewer(MyWidget1._widget_cls)
    app.new_data_viewer(MyWidget2._widget_cls)
Пример #15
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)
        with patch.object(self.widget_cls, 'close') as close:
            w = app.new_data_viewer(self.widget_cls, data=d1)
            w.add_data(d2)
            dc.remove(d1)
            dc.remove(d2)
        assert close.call_count >= 1
Пример #16
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)
        with patch.object(self.widget_cls, 'close') as close:
            w = app.new_data_viewer(self.widget_cls, data=d1)
            w.add_data(d2)
            dc.remove(d1)
            dc.remove(d2)
        assert close.call_count >= 1
Пример #17
0
    def setup_class(self):

        data = Data(x=[1, 2, 3], y=[2, 3, 4], label='data')
        dc = DataCollection([data])
        app = GlueApplication(dc)

        data.style.color = '#000000'
        v = app.new_data_viewer(HistogramWidget, data=data)
        v.component = data.id['y']
        v.xmin = 0
        v.xmax = 10
        v.bins = 20

        self.args, self.kwargs = build_plotly_call(app)
Пример #18
0
def test_two_custom_viewer_classes():

    class MyWidget1(CustomViewer):

        text_box1_Widget1 = '_Hello'

        def setup(self, text_box1_Widget1):
            pass

    class MyWidget2(CustomViewer):

        text_box1_Widget2 = '_Hello'
        text_box2_Widget2 = '_world'

        def setup(self, text_box1_Widget2, text_box2_Widget2):
            pass

    app = GlueApplication()
    dc = app.data_collection
    d = Data(x=[1, 2, 3], label='test')
    dc.append(d)
    app.new_data_viewer(MyWidget1._widget_cls)
    app.new_data_viewer(MyWidget2._widget_cls)
Пример #19
0
class TestDataTableModel():

    def setup_method(self, method):
        self.gapp = GlueApplication()
        self.viewer = self.gapp.new_data_viewer(TableViewer)
        self.data = Data(x=[1, 2, 3, 4], y=[2, 3, 4, 5])
        self.gapp.data_collection.append(self.data)
        self.viewer.add_data(self.data)
        self.model = DataTableModel(self.viewer)

    def teardown_method(self, method):
        self.gapp.close()
        self.gapp = None

    def test_column_count(self):
        assert self.model.columnCount() == 2

    def test_column_count_hidden(self):
        self.model.show_coords = True
        assert self.model.columnCount() == 3

    def test_header_data(self):
        for i, c in enumerate(self.data.main_components):
            result = self.model.headerData(i, Qt.Horizontal, Qt.DisplayRole)
            assert result == c.label

        for i in range(self.data.size):
            result = self.model.headerData(i, Qt.Vertical, Qt.DisplayRole)
            assert result == str(i)

    def test_row_count(self):
        assert self.model.rowCount() == 4

    def test_data(self):
        for i, c in enumerate(self.data.main_components):
            for j in range(self.data.size):
                idx = self.model.index(j, i)
                result = self.model.data(idx, Qt.DisplayRole)
                assert float(result) == self.data[c, j]

    @pytest.mark.xfail
    def test_data_2d(self):
        self.data = Data(x=[[1, 2], [3, 4]], y=[[2, 3], [4, 5]])
        self.model = DataTableModel(self.data)
        for i, c in enumerate(self.data.main_components):
            for j in range(self.data.size):
                idx = self.model.index(j, i)
                result = self.model.data(idx, Qt.DisplayRole)
                assert float(result) == self.data[c].ravel()[j]
Пример #20
0
class TestDataTableModel():

    def setup_method(self, method):
        self.gapp = GlueApplication()
        self.viewer = self.gapp.new_data_viewer(TableViewer)
        self.data = Data(x=[1, 2, 3, 4], y=[2, 3, 4, 5])
        self.gapp.data_collection.append(self.data)
        self.viewer.add_data(self.data)
        self.model = DataTableModel(self.viewer)

    def teardown_method(self, method):
        self.gapp.close()
        self.gapp = None

    def test_column_count(self):
        assert self.model.columnCount() == 2

    def test_column_count_hidden(self):
        self.model.show_coords = True
        assert self.model.columnCount() == 4

    def test_header_data(self):
        for i, c in enumerate(self.data.main_components):
            result = self.model.headerData(i, Qt.Horizontal, Qt.DisplayRole)
            assert result == c.label

        for i in range(self.data.size):
            result = self.model.headerData(i, Qt.Vertical, Qt.DisplayRole)
            assert result == str(i)

    def test_row_count(self):
        assert self.model.rowCount() == 4

    def test_data(self):
        for i, c in enumerate(self.data.main_components):
            for j in range(self.data.size):
                idx = self.model.index(j, i)
                result = self.model.data(idx, Qt.DisplayRole)
                assert float(result) == self.data[c, j]

    @pytest.mark.xfail
    def test_data_2d(self):
        self.data = Data(x=[[1, 2], [3, 4]], y=[[2, 3], [4, 5]])
        self.model = DataTableModel(self.data)
        for i, c in enumerate(self.data.main_components):
            for j in range(self.data.size):
                idx = self.model.index(j, i)
                result = self.model.data(idx, Qt.DisplayRole)
                assert float(result) == self.data[c].ravel()[j]
Пример #21
0
def test_rotate(capsys):

    app = GlueApplication()
    viewer = app.new_data_viewer(VispyScatterViewer)

    viewer.toolbar.actions['vispy:rotate'].toggle()
    assert viewer.toolbar.active_tool.tool_id == 'vispy:rotate'

    viewer.toolbar.actions['vispy:rotate'].toggle()
    assert viewer.toolbar.active_tool is None

    out, err = capsys.readouterr()
    assert out.strip() == ""
    assert err.strip() == ""

    app.close()
    def test_options_widget(self):
        d1 = Data(x=np.random.random((2, ) * self.ndim))
        d2 = Data(x=np.random.random((2, ) * self.ndim))
        dc = DataCollection([d1, d2])
        app = GlueApplication(dc)
        w = app.new_data_viewer(self.widget_cls, data=d1)

        w.state.x_stretch = 0.5
        w.state.y_stretch = 1.0
        w.state.z_stretch = 2.0

        w.state.x_min = -0.1
        w.state.x_max = 10.1
        w.state.y_min = 0.1
        w.state.y_max = 10.9
        w.state.z_min = 0.2
        w.state.z_max = 10.8

        w.state.visible_axes = False
Пример #23
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()
Пример #24
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()
Пример #25
0
    def test_options_widget(self):
        d1 = Data(x=np.random.random((2,) * self.ndim))
        d2 = Data(x=np.random.random((2,) * self.ndim))
        dc = DataCollection([d1, d2])
        app = GlueApplication(dc)
        w = app.new_data_viewer(self.widget_cls, data=d1)

        w.state.x_stretch = 0.5
        w.state.y_stretch = 1.0
        w.state.z_stretch = 2.0

        w.state.x_min = -0.1
        w.state.x_max = 10.1
        w.state.y_min = 0.1
        w.state.y_max = 10.9
        w.state.z_min = 0.2
        w.state.z_max = 10.8

        w.state.visible_axes = False
Пример #26
0
def test_add_subset():

    # Regression test for a bug that occurred when adding a subset
    # directly to the table viewer.

    data1 = Data(a=[1, 2, 3, 4, 5], label='test1')
    data2 = Data(a=[1, 2, 3, 4, 5], label='test2')
    dc = DataCollection([data1, data2])
    dc.new_subset_group('test subset 1', data1.id['a'] > 2)

    gapp = GlueApplication(dc)

    viewer = gapp.new_data_viewer(TableViewer)
    viewer.add_subset(data1.subsets[0])

    assert len(viewer.state.layers) == 2
    assert not viewer.state.layers[0].visible
    assert viewer.state.layers[1].visible

    dc.new_subset_group('test subset 2', data1.id['a'] <= 2)

    assert len(viewer.state.layers) == 3
    assert not viewer.state.layers[0].visible
    assert viewer.state.layers[1].visible
    assert viewer.state.layers[2].visible

    viewer.remove_subset(data1.subsets[1])

    assert len(viewer.state.layers) == 2
    assert not viewer.state.layers[0].visible
    assert viewer.state.layers[1].visible

    viewer.add_subset(data1.subsets[1])

    assert len(viewer.state.layers) == 3
    assert not viewer.state.layers[0].visible
    assert viewer.state.layers[1].visible
    assert viewer.state.layers[2].visible

    with pytest.raises(ValueError) as exc:
        viewer.add_subset(data2.subsets[1])
    assert exc.value.args[
        0] == 'subset parent data does not match existing table data'
Пример #27
0
def test_change_components():

    # Regression test for a bug that caused table viewers to not update when
    # adding/removing components.

    app = get_qapp()  # noqa

    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'],
             label='test')

    dc = DataCollection([d])

    gapp = GlueApplication(dc)

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

    data_changed = MagicMock()
    viewer.model.dataChanged.connect(data_changed)

    # layoutChanged needs to be emitted for the new/removed columns to be
    # registered (dataChanged is not enough)
    layout_changed = MagicMock()
    viewer.model.layoutChanged.connect(layout_changed)

    assert data_changed.call_count == 0
    assert layout_changed.call_count == 0
    viewer.model.columnCount() == 2

    d.add_component([3, 4, 5, 6, 2], 'z')

    assert data_changed.call_count == 1
    assert layout_changed.call_count == 1
    viewer.model.columnCount() == 3

    d.remove_component(d.id['z'])

    assert data_changed.call_count == 2
    assert layout_changed.call_count == 2
    viewer.model.columnCount() == 2
Пример #28
0
    def test_add_viewer(self, tmpdir):

        d1 = Data(x=np.random.random((2,) * self.ndim))
        d2 = Data(x=np.random.random((2,) * self.ndim))
        dc = DataCollection([d1, d2])
        app = GlueApplication(dc)
        w = app.new_data_viewer(self.widget_cls, data=d1)
        w.viewer_size = (300, 400)

        filename = tmpdir.join('session.glu').strpath
        app.save_session(filename, include_data=True)

        app2 = GlueApplication.restore_session(filename)

        # test session is restored correctly
        for viewer in app2.viewers:
            assert viewer[0].viewer_size == (300, 400)

        app.close()
        app2.close()
    def test_add_viewer(self, tmpdir):

        d1 = Data(x=np.random.random((2, ) * self.ndim))
        d2 = Data(x=np.random.random((2, ) * self.ndim))
        dc = DataCollection([d1, d2])
        app = GlueApplication(dc)
        w = app.new_data_viewer(self.widget_cls, data=d1)
        w.viewer_size = (300, 400)

        filename = tmpdir.join('session.glu').strpath
        app.save_session(filename, include_data=True)

        app2 = GlueApplication.restore_session(filename)

        # test session is restored correctly
        for viewer in app2.viewers:
            assert viewer[0].viewer_size == (300, 400)

        app.close()
        app2.close()
Пример #30
0
    def setup_method(self, method):
        LinkSame = core.link_helpers.LinkSame

        d = core.Data(label='im', x=[[1, 2], [2, 3]], y=[[2, 3], [4, 5]])
        d2 = core.Data(label='cat',
                       x=[0, 1, 0, 1],
                       y=[0, 0, 1, 1],
                       z=[1, 2, 3, 4])

        dc = core.DataCollection([d, d2])
        dc.add_link(LinkSame(d.get_pixel_component_id(0), d2.id['x']))
        dc.add_link(LinkSame(d.get_pixel_component_id(1), d2.id['y']))

        app = GlueApplication(dc)
        w = app.new_data_viewer(ImageWidget, data=d)
        self.d = d
        self.app = app
        self.w = w
        self.d2 = d2
        self.dc = dc
Пример #31
0
    def setup_method(self, method):
        LinkSame = core.link_helpers.LinkSame

        d = core.Data(label='im', x=[[1, 2], [2, 3]], y=[[2, 3], [4, 5]])
        d2 = core.Data(label='cat',
                       x=[0, 1, 0, 1],
                       y=[0, 0, 1, 1],
                       z=[1, 2, 3, 4])

        dc = core.DataCollection([d, d2])
        dc.add_link(LinkSame(d.get_pixel_component_id(0), d2.id['x']))
        dc.add_link(LinkSame(d.get_pixel_component_id(1), d2.id['y']))

        app = GlueApplication(dc)
        w = app.new_data_viewer(ImageWidget, data=d)
        self.d = d
        self.app = app
        self.w = w
        self.d2 = d2
        self.dc = dc
Пример #32
0
def test_change_components():

    # Regression test for a bug that caused table viewers to not update when
    # adding/removing components. For now, this does not work with Qt 5.7.

    app = get_qapp()  # noqa

    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'], label='test')

    dc = DataCollection([d])

    gapp = GlueApplication(dc)

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

    data_changed = MagicMock()
    viewer.model.dataChanged.connect(data_changed)

    # layoutChanged needs to be emitted for the new/removed columns to be
    # registered (dataChanged is not enough)
    layout_changed = MagicMock()
    viewer.model.layoutChanged.connect(layout_changed)

    assert data_changed.call_count == 0
    assert layout_changed.call_count == 0
    viewer.model.columnCount() == 2

    d.add_component([3, 4, 5, 6, 2], 'z')

    assert data_changed.call_count == 1
    assert layout_changed.call_count == 1
    viewer.model.columnCount() == 3

    d.remove_component(d.id['z'])

    assert data_changed.call_count == 2
    assert layout_changed.call_count == 2
    viewer.model.columnCount() == 2
Пример #33
0
class TestPVSliceTool(object):
    def setup_method(self, method):
        self.cube = Data(label='cube', x=np.arange(1000).reshape((5, 10, 20)))
        self.application = GlueApplication()
        self.application.data_collection.append(self.cube)
        self.viewer = self.application.new_data_viewer(ImageViewer)
        self.viewer.add_data(self.cube)

    def teardown_method(self, method):
        self.viewer.close()
        self.viewer = None
        self.application.close()
        self.application = None

    @requires_astropy
    @requires_scipy
    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, )
Пример #34
0
def test_table_with_dask_column():

    da = pytest.importorskip('dask.array')

    app = get_qapp()

    d = Data(d=da.asarray([1, 2, 3, 4, 5]), e=np.arange(5) + 1)

    dc = DataCollection([d])

    gapp = GlueApplication(dc)

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

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

    assert widget.state.layers[0].visible
    assert widget.state.layers[1].visible
    assert widget.state.layers[2].visible

    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 = {'d': [1, 2, 3, 4, 5], 'e': [1, 2, 3, 4, 5]}

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

    check_values_and_color(model, data, colors)

    widget.state.layers[2].visible = False

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

    check_values_and_color(model, data, colors)
Пример #35
0
def test_reset(tmpdir, capsys):

    app = GlueApplication()
    viewer = app.new_data_viewer(VispyScatterViewer)
    data = Data(x=[1, 2, 3], label='Data')
    app.data_collection.append(data)
    app.show()
    viewer.add_data(data)

    assert viewer.state.x_min == 1.
    assert viewer.state.y_min == 1.
    assert viewer.state.z_min == 1.

    assert viewer.state.x_max == 3.
    assert viewer.state.y_max == 3.
    assert viewer.state.z_max == 3.

    viewer.state.x_min = 2
    viewer.state.y_min = 3
    viewer.state.z_min = 5

    viewer.state.x_max = 6
    viewer.state.y_max = 7
    viewer.state.z_max = 8

    viewer.toolbar.actions['vispy:reset'].trigger()

    assert viewer.state.x_min == 1.
    assert viewer.state.y_min == 1.
    assert viewer.state.z_min == 1.

    assert viewer.state.x_max == 3.
    assert viewer.state.y_max == 3.
    assert viewer.state.z_max == 3.

    out, err = capsys.readouterr()
    assert out.strip() == ""
    assert err.strip() == ""

    app.close()
Пример #36
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() == ""
Пример #37
0
def test_table_title():

    app = get_qapp()  # noqa

    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)

    assert viewer.windowTitle() == 'Table'

    viewer.add_data(data1)

    assert viewer.windowTitle() == 'Table: test1'

    viewer.add_data(data2)

    assert viewer.windowTitle() == 'Table: test2'
Пример #38
0
def test_table_title():

    app = get_qapp()  # noqa

    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)

    assert viewer.windowTitle() == 'Table'

    viewer.add_data(data1)

    assert viewer.windowTitle() == 'Table: test1'

    viewer.add_data(data2)

    assert viewer.windowTitle() == 'Table: test2'
Пример #39
0
    def test_viewer_size(self, tmpdir):

        # regression test for #781
        # viewers were not restored with the right size

        d1 = Data(x=np.random.random((2,) * self.ndim))
        d2 = Data(x=np.random.random((2,) * self.ndim))
        dc = DataCollection([d1, d2])
        app = GlueApplication(dc)
        w = app.new_data_viewer(self.widget_cls, data=d1)
        w.viewer_size = (300, 400)

        filename = tmpdir.join('session.glu').strpath
        app.save_session(filename, include_data=True)

        app2 = GlueApplication.restore_session(filename)

        for viewer in app2.viewers:
            assert viewer[0].viewer_size == (300, 400)

        app.close()
        app2.close()
Пример #40
0
    def test_close_on_last_layer_remove(self):

        # regression test for 391

        # Note: processEvents is needed for things to work correctly with PySide2
        qtapp = get_qapp()

        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)
        qtapp.processEvents()
        assert len(app.viewers[0]) == 1
        dc.remove(d1)
        qtapp.processEvents()
        assert len(app.viewers[0]) == 1
        dc.remove(d2)
        qtapp.processEvents()
        assert len(app.viewers[0]) == 0
        app.close()
Пример #41
0
    def test_viewer_size(self, tmpdir):

        # regression test for #781
        # viewers were not restored with the right size

        d1 = Data(x=np.random.random((2,) * self.ndim))
        d2 = Data(x=np.random.random((2,) * self.ndim))
        dc = DataCollection([d1, d2])
        app = GlueApplication(dc)
        w = app.new_data_viewer(self.widget_cls, data=d1)
        w.viewer_size = (300, 400)

        filename = tmpdir.join('session.glu').strpath
        app.save_session(filename, include_data=True)

        app2 = GlueApplication.restore_session(filename)

        for viewer in app2.viewers:
            assert viewer[0].viewer_size == (300, 400)

        app.close()
        app2.close()
Пример #42
0
def test_save(tmpdir, capsys):

    app = GlueApplication()
    viewer = app.new_data_viewer(VispyScatterViewer)
    data = Data(x=[1, 2, 3], label='Data')
    app.data_collection.append(data)
    app.show()
    viewer.add_data(data)

    filename = tmpdir.join('test.png').strpath

    with patch('qtpy.compat.getsavefilename') as fd:
        fd.return_value = filename, 'png'
        viewer.toolbar.tools['save'].subtools[0].activate()

    assert os.path.exists(filename)

    out, err = capsys.readouterr()
    assert out.strip() == ""
    assert err.strip() == ""

    app.close()
Пример #43
0
    def test_close_on_last_layer_remove(self):

        # regression test for 391

        # Note: processEvents is needed for things to work correctly with PySide2
        qtapp = get_qapp()

        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)
        qtapp.processEvents()
        assert len(app.viewers[0]) == 1
        dc.remove(d1)
        qtapp.processEvents()
        assert len(app.viewers[0]) == 1
        dc.remove(d2)
        qtapp.processEvents()
        assert len(app.viewers[0]) == 0
        app.close()
Пример #44
0
def test_record(tmpdir, capsys):

    app = GlueApplication()
    viewer = app.new_data_viewer(VispyScatterViewer)

    filename = tmpdir.join('test.gif').strpath

    with patch('qtpy.compat.getsavefilename') as fd:
        fd.return_value = filename, 'gif'
        viewer.toolbar.actions['vispy:record'].toggle()

    assert viewer.toolbar.active_tool.tool_id == 'vispy:record'

    viewer.toolbar.actions['vispy:record'].toggle()
    assert viewer.toolbar.active_tool is None

    assert os.path.exists(filename)

    out, err = capsys.readouterr()
    assert out.strip() == ""
    assert err.strip() == ""

    app.close()
Пример #45
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
Пример #46
0
class TestPlotly(object):

    def setup_method(self, method):
        d = Data(x=[1, 2, 3], y=[2, 3, 4], z=['a', 'b', 'c'], label='data')
        dc = DataCollection([d])
        self.app = GlueApplication(dc)
        self.data = d

    def teardown_method(self, method):
        self.app.close()
        self.app = None

    def test_scatter(self):

        d = self.data
        d.style.markersize = 6
        d.style.color = '#ff0000'
        d.style.alpha = .4

        viewer = self.app.new_data_viewer(ScatterViewer, data=d)
        viewer.state.x_att = d.id['y']
        viewer.state.y_att = d.id['x']

        args, kwargs = build_plotly_call(self.app)
        data = args[0]['data'][0]

        expected = dict(type='scatter', mode='markers', name=d.label,
                        marker=dict(size=6, color='rgba(255, 0, 0, 0.4)',
                                    symbol='circle'))
        for k, v in expected.items():
            assert data[k] == v

        np.testing.assert_array_equal(data['x'], d['y'])
        np.testing.assert_array_equal(data['y'], d['x'])

        layout = args[0]['layout']
        assert layout['showlegend']

        viewer.close()

    def test_scatter_subset(self):

        d = self.data
        s = d.new_subset(label='subset')
        s.subset_state = d.id['x'] > 1
        s.style.marker = 's'

        viewer = self.app.new_data_viewer(ScatterViewer, data=d)
        viewer.state.x_att = d.id['x']
        viewer.state.y_att = d.id['x']

        args, kwargs = build_plotly_call(self.app)
        data = args[0]['data']

        # check that subset is on Top
        assert len(data) == 2
        assert data[0]['name'] == 'data'
        assert data[1]['name'] == 'subset'

        viewer.close()

    def test_axes(self):

        viewer = self.app.new_data_viewer(ScatterViewer, data=self.data)

        viewer.state.x_log = True
        viewer.state.x_min = 10
        viewer.state.x_max = 100
        viewer.state.x_att = self.data.id['x']

        viewer.state.y_log = False
        viewer.state.y_min = 2
        viewer.state.y_max = 4
        viewer.state.y_att = self.data.id['y']

        args, kwargs = build_plotly_call(self.app)

        xaxis = dict(type='log', rangemode='normal',
                     range=[1, 2], title='x', zeroline=False)
        yaxis = dict(type='linear', rangemode='normal',
                     range=[2, 4], title='y', zeroline=False)
        layout = args[0]['layout']
        for k, v in layout['xaxis'].items():
            assert xaxis.get(k, v) == v
        for k, v in layout['yaxis'].items():
            assert yaxis.get(k, v) == v

        viewer.close()

    def test_histogram(self):

        d = self.data
        d.style.color = '#000000'

        viewer = self.app.new_data_viewer(HistogramViewer, data=d)
        viewer.state.x_att = d.id['y']
        viewer.state.hist_x_min = 0
        viewer.state.hist_x_max = 10
        viewer.state.hist_n_bin = 20

        args, kwargs = build_plotly_call(self.app)

        expected = dict(
            name='data',
            type='bar',
            marker=dict(
                color='rgba(0, 0, 0, {0:0.1f})'.format(float(settings.DATA_ALPHA))
            ),
        )
        data = args[0]['data']
        for k in expected:
            assert expected[k] == data[0][k]
        assert args[0]['layout']['barmode'] == 'overlay'

        viewer.close()

    def test_scatter_categorical(self):

        viewer = self.app.new_data_viewer(ScatterViewer, data=self.data)

        viewer.state.x_att = self.data.id['x']
        viewer.state.y_att = self.data.id['z']

        args, kwargs = build_plotly_call(self.app)

        xaxis = dict(type='linear', rangemode='normal',
                     range=[0.92, 3.08], title='x', zeroline=False)
        yaxis = dict(type='linear', rangemode='normal',
                     range=[-0.62, 2.62], title='z', zeroline=False)
        layout = args[0]['layout']
        for k, v in layout['xaxis'].items():
            assert xaxis.get(k, v) == v
        for k, v in layout['yaxis'].items():
            assert yaxis.get(k, v) == v

        viewer.close()

    def test_histogram_categorical(self):

        viewer = self.app.new_data_viewer(HistogramViewer, data=self.data)

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

        args, kwargs = build_plotly_call(self.app)

        xaxis = dict(type='linear', rangemode='normal',
                     range=[-0.5, 2.5], title='z', zeroline=False)
        yaxis = dict(type='linear', rangemode='normal',
                     range=[0, 1.05], title='', zeroline=False)
        layout = args[0]['layout']
        for k, v in layout['xaxis'].items():
            assert xaxis.get(k, v) == v
        for k, v in layout['yaxis'].items():
            assert yaxis.get(k, v) == v

        viewer.close()
Пример #47
0
class TestDendrogramViewer():

    def setup_method(self, method):

        self.data = Data(label='d1', parent=[-1, 0, 1, 1], height=[1.3, 2.2, 3.2, 4.4])

        self.app = GlueApplication()
        self.session = self.app.session
        self.hub = self.session.hub

        self.data_collection = self.session.data_collection
        self.data_collection.append(self.data)

        self.viewer = self.app.new_data_viewer(DendrogramViewer)

        self.data_collection.register_to_hub(self.hub)
        self.viewer.register_to_hub(self.hub)

    def teardown_method(self, method):
        self.viewer.close()
        self.viewer = None
        self.app.close()
        self.app = None

    def test_point_select(self):

        self.viewer.add_data(self.data)

        # By default selecting a structure selects all substructures
        roi = PointROI(0.5, 1.5)
        self.viewer.apply_roi(roi)
        assert len(self.data.subsets) == 1
        mask1 = self.data.subsets[0].subset_state.to_mask(self.data)
        assert_equal(mask1, [0, 1, 1, 1])

        # But this option can be turned off
        self.viewer.state.select_substruct = False
        self.viewer.apply_roi(roi)
        assert len(self.data.subsets) == 1
        mask1 = self.data.subsets[0].subset_state.to_mask(self.data)
        assert_equal(mask1, [0, 1, 0, 0])
        self.viewer.state.select_substruct = True

        # Try selecting a leaf
        roi = PointROI(0.2, 2.8)
        self.viewer.apply_roi(roi)
        assert len(self.data.subsets) == 1
        mask1 = self.data.subsets[0].subset_state.to_mask(self.data)
        assert_equal(mask1, [0, 0, 1, 0])

        # Try selecting another leaf
        roi = PointROI(0.7, 2.8)
        self.viewer.apply_roi(roi)
        assert len(self.data.subsets) == 1
        mask1 = self.data.subsets[0].subset_state.to_mask(self.data)
        assert_equal(mask1, [0, 0, 0, 1])

    def test_attribute_change_triggers_relayout(self):

        self.data.add_component([4, 5, 6, 7], 'flux')
        self.viewer.add_data(self.data)

        l = self.viewer.state._layout
        self.viewer.state.height_att = self.data.id['flux']
        assert self.viewer.state._layout is not l
Пример #48
0
class TestScatterViewer(object):

    def setup_method(self, method):

        self.data = 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_2d = Data(label='d2', a=[[1, 2], [3, 4]], b=[[5, 6], [7, 8]],
                            x=[[3, 5], [5.4, 1]], y=[[1.2, 4], [7, 8]])

        self.app = GlueApplication()
        self.session = self.app.session
        self.hub = self.session.hub

        self.data_collection = self.session.data_collection
        self.data_collection.append(self.data)
        self.data_collection.append(self.data_2d)

        self.viewer = self.app.new_data_viewer(ScatterViewer)

    def teardown_method(self, method):
        self.viewer.close()
        self.viewer = None
        self.app.close()
        self.app = None

    def test_basic(self):

        viewer_state = self.viewer.state

        # Check defaults when we add data
        self.viewer.add_data(self.data)

        assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]:World 0'
        assert combo_as_string(self.viewer.options_widget().ui.combosel_y_att) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]:World 0'

        assert viewer_state.x_att is self.data.id['x']
        assert_allclose(viewer_state.x_min, -1.1 - 0.18)
        assert_allclose(viewer_state.x_max, 3.4 + 0.18)

        assert viewer_state.y_att is self.data.id['y']
        assert_allclose(viewer_state.y_min, 3.2 - 0.012)
        assert_allclose(viewer_state.y_max, 3.5 + 0.012)

        assert not viewer_state.x_log
        assert not viewer_state.y_log

        assert len(viewer_state.layers) == 1

        # Change to categorical component and check new values

        viewer_state.y_att = self.data.id['z']

        assert viewer_state.x_att is self.data.id['x']
        assert_allclose(viewer_state.x_min, -1.1 - 0.18)
        assert_allclose(viewer_state.x_max, 3.4 + 0.18)

        assert viewer_state.y_att is self.data.id['z']
        assert_allclose(viewer_state.y_min, -0.5 - 0.12)
        assert_allclose(viewer_state.y_max, 2.5 + 0.12)

        assert not viewer_state.x_log
        assert not viewer_state.y_log

    def test_flip(self):

        viewer_state = self.viewer.state

        self.viewer.add_data(self.data)

        assert_allclose(viewer_state.x_min, -1.1 - 0.18)
        assert_allclose(viewer_state.x_max, 3.4 + 0.18)

        self.viewer.options_widget().button_flip_x.click()

        assert_allclose(viewer_state.x_max, -1.1 - 0.18)
        assert_allclose(viewer_state.x_min, 3.4 + 0.18)

        assert_allclose(viewer_state.y_min, 3.2 - 0.012)
        assert_allclose(viewer_state.y_max, 3.5 + 0.012)

        self.viewer.options_widget().button_flip_y.click()

        assert_allclose(viewer_state.y_max, 3.2 - 0.012)
        assert_allclose(viewer_state.y_min, 3.5 + 0.012)

    def test_remove_data(self):
        self.viewer.add_data(self.data)
        assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]:World 0'
        assert combo_as_string(self.viewer.options_widget().ui.combosel_y_att) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]:World 0'
        self.data_collection.remove(self.data)
        assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == ''
        assert combo_as_string(self.viewer.options_widget().ui.combosel_y_att) == ''

    def test_update_component_updates_title(self):
        self.viewer.add_data(self.data)
        assert self.viewer.windowTitle() == '2D Scatter'
        self.viewer.state.x_att = self.data.id['y']
        assert self.viewer.windowTitle() == '2D Scatter'

    def test_combo_updates_with_component_add(self):
        self.viewer.add_data(self.data)
        self.data.add_component([3, 4, 1, 2], 'a')
        assert self.viewer.state.x_att is self.data.id['x']
        assert self.viewer.state.y_att is self.data.id['y']
        assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:z:a:Coordinate components:Pixel Axis 0 [x]:World 0'
        assert combo_as_string(self.viewer.options_widget().ui.combosel_y_att) == 'Main components:x:y:z:a:Coordinate components:Pixel Axis 0 [x]:World 0'

    def test_nonnumeric_first_component(self):
        # regression test for #208. Shouldn't complain if
        # first component is non-numerical
        data = core.Data()
        data.add_component(['a', 'b', 'c'], label='c1')
        data.add_component([1, 2, 3], label='c2')
        self.data_collection.append(data)
        self.viewer.add_data(data)

    def test_apply_roi(self):

        self.viewer.add_data(self.data)

        roi = RectangularROI(0, 3, 3.25, 3.45)

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

        self.viewer.apply_roi(roi)

        assert len(self.viewer.layers) == 2
        assert len(self.data.subsets) == 1

        assert_allclose(self.data.subsets[0].to_mask(), [0, 1, 0, 0])

        state = self.data.subsets[0].subset_state
        assert isinstance(state, RoiSubsetState)

    def test_apply_roi_categorical(self):

        viewer_state = self.viewer.state

        self.viewer.add_data(self.data)

        viewer_state.y_att = self.data.id['z']

        roi = RectangularROI(0, 3, -0.4, 0.3)

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

        self.viewer.apply_roi(roi)

        assert len(self.viewer.layers) == 2
        assert len(self.data.subsets) == 1

        assert_allclose(self.data.subsets[0].to_mask(), [0, 0, 0, 1])

        state = self.data.subsets[0].subset_state
        assert isinstance(state, AndState)

    def test_apply_roi_empty(self):
        # Make sure that doing an ROI selection on an empty viewer doesn't
        # produce error messsages
        roi = XRangeROI(-0.2, 0.1)
        self.viewer.apply_roi(roi)

    def test_axes_labels(self):

        viewer_state = self.viewer.state

        self.viewer.add_data(self.data)

        assert self.viewer.axes.get_xlabel() == 'x'
        assert self.viewer.axes.get_ylabel() == 'y'

        viewer_state.x_log = True

        assert self.viewer.axes.get_xlabel() == 'Log x'
        assert self.viewer.axes.get_ylabel() == 'y'

        viewer_state.x_att = self.data.id['y']

        assert self.viewer.axes.get_xlabel() == 'y'
        assert self.viewer.axes.get_ylabel() == 'y'

        viewer_state.y_log = True

        assert self.viewer.axes.get_xlabel() == 'y'
        assert self.viewer.axes.get_ylabel() == 'Log y'

    def test_component_replaced(self):

        # regression test for 508 - if a component ID is replaced, we should
        # make sure that the component ID is selected if the old component ID
        # was selected

        self.viewer.add_data(self.data)
        self.viewer.state.x_att = self.data.id['x']
        test = ComponentID('test')
        self.data.update_id(self.viewer.state.x_att, test)
        assert self.viewer.state.x_att is test
        assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:test:y:z:Coordinate components:Pixel Axis 0 [x]:World 0'

    def test_nan_component(self):
        # regression test for case when all values are NaN in a component
        data = core.Data()
        data.add_component([np.nan, np.nan, np.nan], label='c1')
        self.data_collection.append(data)
        self.viewer.add_data(data)

    def test_density_map(self):

        kwargs = dict(range=[(-5, 5), (-5, 5)], bins=(2, 2))

        self.viewer.add_data(self.data)
        self.viewer.state.layers[0].points_mode = 'auto'
        assert self.viewer.layers[0].state.compute_density_map(**kwargs).sum() == 0
        self.viewer.state.layers[0].points_mode = 'density'
        assert self.viewer.layers[0].state.compute_density_map(**kwargs).sum() == 4
        self.viewer.state.layers[0].points_mode = 'markers'
        assert self.viewer.layers[0].state.compute_density_map(**kwargs).sum() == 0

    def test_density_map_color(self):

        # Regression test to make sure things don't crash when changing
        # back to markers if the color mode is cmap

        self.viewer.add_data(self.data)
        self.viewer.state.layers[0].points_mode = 'density'
        self.viewer.state.layers[0].cmap_mode = 'Linear'
        self.viewer.state.layers[0].size_mode = 'Linear'
        self.viewer.state.layers[0].points_mode = 'markers'
        self.viewer.state.layers[0].points_mode = 'density'

    @pytest.mark.parametrize('protocol', [0, 1])
    def test_session_back_compat(self, protocol):

        filename = os.path.join(DATA, 'scatter_v{0}.glu'.format(protocol))

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

        state = GlueUnSerializer.loads(session)

        ga = state.object('__main__')

        dc = ga.session.data_collection

        assert len(dc) == 1

        assert dc[0].label == 'basic'

        viewer1 = ga.viewers[0][0]
        assert len(viewer1.state.layers) == 3
        assert viewer1.state.x_att is dc[0].id['a']
        assert viewer1.state.y_att is dc[0].id['b']
        assert_allclose(viewer1.state.x_min, -1.04)
        assert_allclose(viewer1.state.x_max, 1.04)
        assert_allclose(viewer1.state.y_min, 1.98)
        assert_allclose(viewer1.state.y_max, 3.02)
        assert not viewer1.state.x_log
        assert not viewer1.state.y_log
        assert viewer1.state.layers[0].visible
        assert viewer1.state.layers[1].visible
        assert viewer1.state.layers[2].visible

        viewer2 = ga.viewers[0][1]
        assert len(viewer2.state.layers) == 3
        assert viewer2.state.x_att is dc[0].id['a']
        assert viewer2.state.y_att is dc[0].id['c']
        assert_allclose(viewer2.state.x_min, 9.5e-6)
        assert_allclose(viewer2.state.x_max, 1.05)
        assert_allclose(viewer2.state.y_min, 0.38)
        assert_allclose(viewer2.state.y_max, 5.25)
        assert viewer2.state.x_log
        assert viewer2.state.y_log
        assert viewer2.state.layers[0].visible
        assert not viewer2.state.layers[1].visible
        assert viewer2.state.layers[2].visible

        viewer3 = ga.viewers[0][2]
        assert len(viewer3.state.layers) == 3
        assert viewer3.state.x_att is dc[0].id['b']
        assert viewer3.state.y_att is dc[0].id['a']
        assert_allclose(viewer3.state.x_min, 0)
        assert_allclose(viewer3.state.x_max, 5)
        assert_allclose(viewer3.state.y_min, -5)
        assert_allclose(viewer3.state.y_max, 5)
        assert not viewer3.state.x_log
        assert not viewer3.state.y_log
        assert viewer3.state.layers[0].visible
        assert viewer3.state.layers[1].visible
        assert not viewer3.state.layers[2].visible

        ga.close()

    def test_session_line_back_compat(self):

        # Backward-compatibility for v0.11 files in which the line and scatter
        # plots were defined as separate styles.

        filename = os.path.join(DATA, 'scatter_and_line_v1.glu')

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

        state = GlueUnSerializer.loads(session)

        ga = state.object('__main__')

        dc = ga.session.data_collection

        assert len(dc) == 1

        assert dc[0].label == 'table'

        viewer1 = ga.viewers[0][0]
        assert len(viewer1.state.layers) == 1
        assert viewer1.state.x_att is dc[0].id['a']
        assert viewer1.state.y_att is dc[0].id['b']
        assert viewer1.state.layers[0].markers_visible
        assert not viewer1.state.layers[0].line_visible

        viewer1 = ga.viewers[0][1]
        assert len(viewer1.state.layers) == 1
        assert viewer1.state.x_att is dc[0].id['a']
        assert viewer1.state.y_att is dc[0].id['b']
        assert not viewer1.state.layers[0].markers_visible
        assert viewer1.state.layers[0].line_visible

        ga.close()

    def test_save_svg(self, tmpdir):
        # Regression test for a bug in AxesCache that caused SVG saving to
        # fail (because renderer.buffer_rgba did not exist)
        self.viewer.add_data(self.data)
        filename = tmpdir.join('test.svg').strpath
        self.viewer.axes.figure.savefig(filename)

    def test_2d(self):

        viewer_state = self.viewer.state

        self.viewer.add_data(self.data_2d)

        assert viewer_state.x_att is self.data_2d.id['a']
        assert_allclose(viewer_state.x_min, 1 - 0.12)
        assert_allclose(viewer_state.x_max, 4 + 0.12)

        assert viewer_state.y_att is self.data_2d.id['b']
        assert_allclose(viewer_state.y_min, 5 - 0.12)
        assert_allclose(viewer_state.y_max, 8 + 0.12)

        assert self.viewer.layers[0].plot_artist.get_xdata().shape == (4,)

    def test_apply_roi_single(self):

        # Regression test for a bug that caused mode.update to be called
        # multiple times and resulted in all other viewers receiving many
        # messages regarding subset updates (this occurred when multiple)
        # datasets were present.

        layer_tree = LayerTreeWidget(session=self.session)
        layer_tree.set_checkable(False)
        layer_tree.setup(self.data_collection)
        layer_tree.bind_selection_to_edit_subset()

        class Client(HubListener):

            def __init__(self, *args, **kwargs):
                super(Client, self).__init__(*args, **kwargs)
                self.count = Counter()

            def ping(self, message):
                self.count[message.sender] += 1

            def register_to_hub(self, hub):
                hub.subscribe(self, SubsetUpdateMessage, handler=self.ping)

        d1 = Data(a=[1, 2, 3], label='d3')
        d2 = Data(b=[1, 2, 3], label='d4')
        d3 = Data(c=[1, 2, 3], label='d5')
        d4 = Data(d=[1, 2, 3], label='d6')

        self.data_collection.append(d1)
        self.data_collection.append(d2)
        self.data_collection.append(d3)
        self.data_collection.append(d4)

        client = Client()
        client.register_to_hub(self.hub)

        self.viewer.add_data(d1)
        self.viewer.add_data(d3)

        roi = XRangeROI(2.5, 3.5)
        self.viewer.apply_roi(roi)

        for subset in client.count:
            assert client.count[subset] == 1

    @pytest.mark.parametrize('ndim', [1, 2])
    def test_all_options(self, ndim):

        # This test makes sure that all the code for the different scatter modes
        # gets run, though does not check the result.

        viewer_state = self.viewer.state

        if ndim == 1:
            data = self.data
        elif ndim == 2:
            data = self.data_2d

        self.viewer.add_data(data)

        layer_state = viewer_state.layers[0]

        layer_state.style = 'Scatter'

        layer_state.size_mode = 'Linear'
        layer_state.size_att = data.id['y']
        layer_state.size_vmin = 1.2
        layer_state.size_vmax = 4.
        layer_state.size_scaling = 2

        layer_state.cmap_mode = 'Linear'
        layer_state.cmap_att = data.id['x']
        layer_state.cmap_vmin = -1
        layer_state.cmap_vmax = 2.
        layer_state.cmap = colormaps.members[3][1]

        # Check inverting works
        layer_state.cmap_vmin = 3.

        layer_state.size_mode = 'Fixed'

        layer_state.xerr_visible = True
        layer_state.xerr_att = data.id['x']
        layer_state.yerr_visible = True
        layer_state.yerr_att = data.id['y']

        layer_state.style = 'Line'
        layer_state.linewidth = 3
        layer_state.linestyle = 'dashed'

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

    def test_enable_disable_components_combo(self):

        # Regression test for a bug that caused an error when turning off pixel
        # components from combo boxes.

        self.viewer.add_data(self.data)

        self.data['a'] = self.data.id['x'] + 5

        self.viewer.state.x_att_helper.pixel_coord = True

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

        self.viewer.state.x_att_helper.pixel_coord = False

    def test_datetime64_support(self, tmpdir):

        self.data.add_component(np.array([100, 200, 300, 400], dtype='M8[D]'), 't1')
        self.data.add_component(np.array([200, 300, 400, 500], dtype='M8[D]'), 't2')
        self.viewer.add_data(self.data)
        self.viewer.state.x_att = self.data.id['t1']
        self.viewer.state.y_att = self.data.id['y']

        # Matplotlib deals with dates by converting them to the number of days
        # since 01-01-0001, so we can check that the limits are correctly
        # converted (and not 100 to 400)
        assert self.viewer.axes.get_xlim() == (719251.0, 719575.0)
        assert self.viewer.axes.get_ylim() == (3.2 - 0.012, 3.5 + 0.012)

        # Apply an ROI selection in plotting coordinates
        roi = RectangularROI(xmin=719313, xmax=719513, ymin=3, ymax=4)
        self.viewer.apply_roi(roi)

        # Check that the two middle elements are selected
        assert_equal(self.data.subsets[0].to_mask(), [0, 1, 1, 0])

        # Now do the same with the y axis
        self.viewer.state.y_att = self.data.id['t2']

        assert self.viewer.axes.get_xlim() == (719251.0, 719575.0)
        assert self.viewer.axes.get_ylim() == (719351.0, 719675.0)

        # Apply an ROI selection in plotting coordinates
        edit = self.session.edit_subset_mode
        edit.edit_subset = []
        roi = CircularROI(xc=719463, yc=719563, radius=200)
        self.viewer.apply_roi(roi)
        assert_equal(self.data.subsets[1].to_mask(), [0, 1, 1, 1])

        # Make sure that the Qt labels look ok
        self.viewer.state.y_att = self.data.id['y']
        options = self.viewer.options_widget().ui
        assert options.valuetext_x_min.text() == '1970-03-30'
        assert options.valuetext_x_max.text() == '1971-02-17'
        assert options.valuetext_y_min.text() == '3.188'
        assert options.valuetext_y_max.text() == '3.512'

        # Make sure that we can set the xmin/xmax to a string date
        assert_equal(self.viewer.state.x_min, np.datetime64('1970-03-30', 'D'))
        options.valuetext_x_min.setText('1970-04-14')
        options.valuetext_x_min.editingFinished.emit()
        assert self.viewer.axes.get_xlim() == (719266.0, 719575.0)
        assert_equal(self.viewer.state.x_min, np.datetime64('1970-04-14', 'D'))

        # Make sure that everything works fine after saving/reloading
        filename = tmpdir.join('test_datetime64.glu').strpath
        self.session.application.save_session(filename)
        with open(filename, 'r') as f:
            session = f.read()
        state = GlueUnSerializer.loads(session)
        ga = state.object('__main__')
        viewer = ga.viewers[0][0]
        options = viewer.options_widget().ui

        assert_equal(self.viewer.state.x_min, np.datetime64('1970-04-14', 'D'))

        assert options.valuetext_x_min.text() == '1970-04-14'
        assert options.valuetext_x_max.text() == '1971-02-17'
        assert options.valuetext_y_min.text() == '3.188'
        assert options.valuetext_y_max.text() == '3.512'

        ga.close()
Пример #49
0
class TestProfileTools(object):

    def setup_method(self, method):

        self.data = Data(label='d1')
        self.data.coords = SimpleCoordinates()
        self.data['x'] = np.arange(240).reshape((30, 4, 2)).astype(float)

        self.app = GlueApplication()
        self.session = self.app.session
        self.hub = self.session.hub

        self.data_collection = self.session.data_collection
        self.data_collection.append(self.data)

        self.viewer = self.app.new_data_viewer(ProfileViewer)
        self.viewer.state.function = 'mean'

        self.viewer.toolbar.active_tool = 'profile-analysis'

        self.profile_tools = self.viewer.toolbar.tools['profile-analysis']._profile_tools

    def teardown_method(self, method):
        self.viewer.close()
        self.viewer = None
        self.app.close()
        self.app = None

    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)

    @pytest.mark.skipif('PYSIDE2_INSTALLED')
    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'

    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
Пример #50
0
class TestImageViewer(object):

    def setup_method(self, method):

        self.coords = MyCoords()
        self.image1 = Data(label='image1', x=[[1, 2], [3, 4]], y=[[4, 5], [2, 3]])
        self.image2 = Data(label='image2', a=[[3, 3], [2, 2]], b=[[4, 4], [3, 2]],
                           coords=self.coords)
        self.catalog = Data(label='catalog', c=[1, 3, 2], d=[4, 3, 3])
        self.hypercube = Data(label='hypercube', x=np.arange(120).reshape((2, 3, 4, 5)))

        # Create data versions with WCS coordinates
        self.image1_wcs = Data(label='image1_wcs', x=self.image1['x'],
                               coords=WCSCoordinates(wcs=WCS(naxis=2)))
        self.hypercube_wcs = Data(label='hypercube_wcs', x=self.hypercube['x'],
                                  coords=WCSCoordinates(wcs=WCS(naxis=4)))

        self.application = GlueApplication()

        self.session = self.application.session

        self.hub = self.session.hub

        self.data_collection = self.session.data_collection
        self.data_collection.append(self.image1)
        self.data_collection.append(self.image2)
        self.data_collection.append(self.catalog)
        self.data_collection.append(self.hypercube)
        self.data_collection.append(self.image1_wcs)
        self.data_collection.append(self.hypercube_wcs)

        self.viewer = self.application.new_data_viewer(ImageViewer)

        self.data_collection.register_to_hub(self.hub)
        self.viewer.register_to_hub(self.hub)

        self.options_widget = self.viewer.options_widget()

    def teardown_method(self, method):
        self.viewer.close()
        self.viewer = None
        self.application.close()
        self.application = None

    def test_basic(self):

        # Check defaults when we add data

        self.viewer.add_data(self.image1)

        assert combo_as_string(self.options_widget.ui.combosel_x_att_world) == 'Coordinate components:World 0:World 1'
        assert combo_as_string(self.options_widget.ui.combosel_y_att_world) == 'Coordinate components:World 0:World 1'

        assert self.viewer.axes.get_xlabel() == 'World 1'
        assert self.viewer.state.x_att_world is self.image1.id['World 1']
        assert self.viewer.state.x_att is self.image1.pixel_component_ids[1]
        # TODO: make sure limits are deterministic then update this
        # assert self.viewer.state.x_min == -0.5
        # assert self.viewer.state.x_max == +1.5

        assert self.viewer.axes.get_ylabel() == 'World 0'
        assert self.viewer.state.y_att_world is self.image1.id['World 0']
        assert self.viewer.state.y_att is self.image1.pixel_component_ids[0]
        # TODO: make sure limits are deterministic then update this
        # assert self.viewer.state.y_min == -0.5
        # assert self.viewer.state.y_max == +1.5

        assert not self.viewer.state.x_log
        assert not self.viewer.state.y_log

        assert len(self.viewer.state.layers) == 1

    def test_custom_coords(self):

        # Check defaults when we add data with coordinates

        self.viewer.add_data(self.image2)

        assert combo_as_string(self.options_widget.ui.combosel_x_att_world) == 'Coordinate components:Banana:Apple'
        assert combo_as_string(self.options_widget.ui.combosel_x_att_world) == 'Coordinate components:Banana:Apple'

        assert self.viewer.axes.get_xlabel() == 'Apple'
        assert self.viewer.state.x_att_world is self.image2.id['Apple']
        assert self.viewer.state.x_att is self.image2.pixel_component_ids[1]
        assert self.viewer.axes.get_ylabel() == 'Banana'
        assert self.viewer.state.y_att_world is self.image2.id['Banana']
        assert self.viewer.state.y_att is self.image2.pixel_component_ids[0]

    def test_flip(self):

        self.viewer.add_data(self.image1)

        x_min_start = self.viewer.state.x_min
        x_max_start = self.viewer.state.x_max

        self.options_widget.button_flip_x.click()

        assert self.viewer.state.x_min == x_max_start
        assert self.viewer.state.x_max == x_min_start

        y_min_start = self.viewer.state.y_min
        y_max_start = self.viewer.state.y_max

        self.options_widget.button_flip_y.click()

        assert self.viewer.state.y_min == y_max_start
        assert self.viewer.state.y_max == y_min_start

    def test_combo_updates_with_component_add(self):
        self.viewer.add_data(self.image1)
        self.image1.add_component([[9, 9], [8, 8]], 'z')
        assert self.viewer.state.x_att_world is self.image1.id['World 1']
        assert self.viewer.state.y_att_world is self.image1.id['World 0']
        # TODO: there should be an easier way to do this
        layer_style_editor = self.viewer._view.layout_style_widgets[self.viewer.layers[0]]
        assert combo_as_string(layer_style_editor.ui.combosel_attribute) == 'x:y:z'

    def test_apply_roi(self):

        self.viewer.add_data(self.image1)

        roi = RectangularROI(0.4, 1.6, -0.6, 0.6)

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

        self.viewer.apply_roi(roi)

        assert len(self.viewer.layers) == 2
        assert len(self.image1.subsets) == 1

        assert_allclose(self.image1.subsets[0].to_mask(), [[0, 1], [0, 0]])

        state = self.image1.subsets[0].subset_state
        assert isinstance(state, RoiSubsetState)

    def test_apply_roi_empty(self):
        # Make sure that doing an ROI selection on an empty viewer doesn't
        # produce error messsages
        roi = XRangeROI(-0.2, 0.1)
        self.viewer.apply_roi(roi)

    def test_identical(self):

        # Check what happens if we set both attributes to the same coordinates

        self.viewer.add_data(self.image2)

        assert self.viewer.state.x_att_world is self.image2.id['Apple']
        assert self.viewer.state.y_att_world is self.image2.id['Banana']

        self.viewer.state.y_att_world = self.image2.id['Apple']

        assert self.viewer.state.x_att_world is self.image2.id['Banana']
        assert self.viewer.state.y_att_world is self.image2.id['Apple']

        self.viewer.state.x_att_world = self.image2.id['Apple']

        assert self.viewer.state.x_att_world is self.image2.id['Apple']
        assert self.viewer.state.y_att_world is self.image2.id['Banana']

    def test_duplicate_subsets(self):

        # Regression test: make sure that when adding a seconda layer for the
        # same dataset, we don't add the subsets all over again.

        self.viewer.add_data(self.image1)
        self.data_collection.new_subset_group(subset_state=self.image1.id['x'] > 1, label='A')

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

        self.viewer.add_data(self.image1)

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

    def test_aspect_subset(self):

        self.viewer.add_data(self.image1)

        assert self.viewer.state.aspect == 'equal'
        assert self.viewer.axes.get_aspect() == 'equal'

        self.viewer.state.aspect = 'auto'

        self.data_collection.new_subset_group('s1', self.image1.id['x'] > 0.)

        assert len(self.viewer.state.layers) == 2

        assert self.viewer.state.aspect == 'auto'
        assert self.viewer.axes.get_aspect() == 'auto'

        self.viewer.state.aspect = 'equal'

        self.data_collection.new_subset_group('s2', self.image1.id['x'] > 1.)

        assert len(self.viewer.state.layers) == 3

        assert self.viewer.state.aspect == 'equal'
        assert self.viewer.axes.get_aspect() == 'equal'

    def test_hypercube(self):

        # Check defaults when we add data

        self.viewer.add_data(self.hypercube)

        assert combo_as_string(self.options_widget.ui.combosel_x_att_world) == 'Coordinate components:World 0:World 1:World 2:World 3'
        assert combo_as_string(self.options_widget.ui.combosel_x_att_world) == 'Coordinate components:World 0:World 1:World 2:World 3'

        assert self.viewer.axes.get_xlabel() == 'World 3'
        assert self.viewer.state.x_att_world is self.hypercube.id['World 3']
        assert self.viewer.state.x_att is self.hypercube.pixel_component_ids[3]
        # TODO: make sure limits are deterministic then update this
        # assert self.viewer.state.x_min == -0.5
        # assert self.viewer.state.x_max == +1.5

        assert self.viewer.axes.get_ylabel() == 'World 2'
        assert self.viewer.state.y_att_world is self.hypercube.id['World 2']
        assert self.viewer.state.y_att is self.hypercube.pixel_component_ids[2]
        # TODO: make sure limits are deterministic then update this
        # assert self.viewer.state.y_min == -0.5
        # assert self.viewer.state.y_max == +1.5

        assert not self.viewer.state.x_log
        assert not self.viewer.state.y_log

        assert len(self.viewer.state.layers) == 1

    def test_hypercube_world(self):

        # Check defaults when we add data

        wcs = WCS(naxis=4)
        hypercube2 = Data()
        hypercube2.coords = WCSCoordinates(wcs=wcs)
        hypercube2.add_component(np.random.random((2, 3, 4, 5)), 'a')

        self.data_collection.append(hypercube2)

        self.viewer.add_data(hypercube2)

    def test_incompatible_subset(self):
        self.viewer.add_data(self.image1)
        self.data_collection.new_subset_group(subset_state=self.catalog.id['c'] > 1, label='A')

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

    def test_apply_roi_single(self):

        # Regression test for a bug that caused mode.update to be called
        # multiple times and resulted in all other viewers receiving many
        # messages regarding subset updates (this occurred when multiple)
        # datasets were present.

        layer_tree = LayerTreeWidget(session=self.session)
        layer_tree.set_checkable(False)
        layer_tree.setup(self.data_collection)
        layer_tree.bind_selection_to_edit_subset()

        class Client(HubListener):

            def __init__(self, *args, **kwargs):
                super(Client, self).__init__(*args, **kwargs)
                self.count = Counter()

            def ping(self, message):
                self.count[message.sender] += 1

            def register_to_hub(self, hub):
                hub.subscribe(self, SubsetUpdateMessage, handler=self.ping)

        d1 = Data(a=[[1, 2], [3, 4]], label='d1')
        d2 = Data(b=[[1, 2], [3, 4]], label='d2')
        d3 = Data(c=[[1, 2], [3, 4]], label='d3')
        d4 = Data(d=[[1, 2], [3, 4]], label='d4')

        self.data_collection.append(d1)
        self.data_collection.append(d2)
        self.data_collection.append(d3)
        self.data_collection.append(d4)

        client = Client()
        client.register_to_hub(self.hub)

        self.viewer.add_data(d1)
        self.viewer.add_data(d3)

        roi = XRangeROI(2.5, 3.5)
        self.viewer.apply_roi(roi)

        for subset in client.count:
            assert client.count[subset] == 1

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

        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)

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

        # All layers should now be enabled

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

        self.data_collection.remove_link(link2)

        # 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

    def test_change_reference_data(self, capsys):

        # Test to make sure everything works fine if we change the reference data.

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

        assert self.viewer.state.reference_data is self.image1
        assert self.viewer.state.x_att_world is self.image1.world_component_ids[-1]
        assert self.viewer.state.y_att_world is self.image1.world_component_ids[-2]
        assert self.viewer.state.x_att is self.image1.pixel_component_ids[-1]
        assert self.viewer.state.y_att is self.image1.pixel_component_ids[-2]

        self.viewer.state.reference_data = self.image2

        assert self.viewer.state.reference_data is self.image2
        assert self.viewer.state.x_att_world is self.image2.world_component_ids[-1]
        assert self.viewer.state.y_att_world is self.image2.world_component_ids[-2]
        assert self.viewer.state.x_att is self.image2.pixel_component_ids[-1]
        assert self.viewer.state.y_att is self.image2.pixel_component_ids[-2]

        self.viewer.state.reference_data = self.image1

        assert self.viewer.state.reference_data is self.image1
        assert self.viewer.state.x_att_world is self.image1.world_component_ids[-1]
        assert self.viewer.state.y_att_world is self.image1.world_component_ids[-2]
        assert self.viewer.state.x_att is self.image1.pixel_component_ids[-1]
        assert self.viewer.state.y_att is self.image1.pixel_component_ids[-2]

        # 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() == ""

    @pytest.mark.parametrize('wcs', [False, True])
    def test_change_reference_data_dimensionality(self, capsys, wcs):

        # Regression test for a bug that caused an exception when changing
        # the dimensionality of the reference data

        if wcs:
            first = self.image1_wcs
            second = self.hypercube_wcs
        else:
            first = self.image1
            second = self.hypercube

        self.viewer.add_data(first)
        self.viewer.add_data(second)

        assert self.viewer.state.reference_data is first
        assert self.viewer.state.x_att_world is first.world_component_ids[-1]
        assert self.viewer.state.y_att_world is first.world_component_ids[-2]
        assert self.viewer.state.x_att is first.pixel_component_ids[-1]
        assert self.viewer.state.y_att is first.pixel_component_ids[-2]

        self.viewer.state.reference_data = second

        assert self.viewer.state.reference_data is second
        assert self.viewer.state.x_att_world is second.world_component_ids[-1]
        assert self.viewer.state.y_att_world is second.world_component_ids[-2]
        assert self.viewer.state.x_att is second.pixel_component_ids[-1]
        assert self.viewer.state.y_att is second.pixel_component_ids[-2]

        self.viewer.state.reference_data = first

        assert self.viewer.state.reference_data is first
        assert self.viewer.state.x_att_world is first.world_component_ids[-1]
        assert self.viewer.state.y_att_world is first.world_component_ids[-2]
        assert self.viewer.state.x_att is first.pixel_component_ids[-1]
        assert self.viewer.state.y_att is first.pixel_component_ids[-2]

        # 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() == ""

    def test_scatter_overlay(self):
        self.viewer.add_data(self.image1)
        self.viewer.add_data(self.catalog)

    def test_removed_subset(self):

        # Regression test for a bug in v0.11.0 that meant that if a subset
        # was removed, the image viewer would then crash when changing view
        # (e.g. zooming in). The bug was caused by undeleted references to
        # ModestImage due to circular references. We therefore check in this
        # test how many ModestImage objects exist.

        def get_modest_images():
            mi = []
            gc.collect()
            for obj in gc.get_objects():
                try:
                    if isinstance(obj, ModestImage):
                        mi.append(obj)
                except ReferenceError:
                    pass
            return mi

        # The viewer starts off with one ModestImage. This is also a good test
        # that other ModestImages in other tests have been removed.
        assert len(get_modest_images()) == 1

        large_image = Data(x=np.random.random((2048, 2048)))
        self.data_collection.append(large_image)

        # The subset group can be made from any dataset
        subset_group = self.data_collection.new_subset_group(subset_state=self.image1.id['x'] > 1, label='A')

        self.viewer.add_data(large_image)

        # Since the dataset added has a subset, and each subset has its own
        # ModestImage, this increases the count.
        assert len(get_modest_images()) == 2

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

        self.data_collection.remove_subset_group(subset_group)

        # Removing the subset should bring the count back to 1 again
        assert len(get_modest_images()) == 1

    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)

        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

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

    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)

        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)

        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

    def test_save_aggregate_slice(self, tmpdir):

        # Regression test to make sure that image viewers that include
        # aggregate slice objects in the slices can be saved/restored

        self.viewer.add_data(self.hypercube)
        self.viewer.state.slices = AggregateSlice(slice(1, 3), 10, np.sum), 3, 0, 0

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

        self.application.save_session(filename)
        self.application.close()

        app2 = GlueApplication.restore_session(filename)
        viewer_state = app2.viewers[0][0].state
        slices = viewer_state.slices
        assert isinstance(slices[0], AggregateSlice)
        assert slices[0].slice == slice(1, 3)
        assert slices[0].center == 10
        assert slices[0].function is np.sum
        assert slices[1:] == (3, 0, 0)

        app2.close()

    def test_subset_cube_image(self):

        # Regression test to make sure that if an image and cube are present
        # in an image viewer and a subset is also present, we don't get an
        # error when trying to access the subset shape

        self.viewer.add_data(self.image1)
        self.data_collection.new_subset_group(label='subset',
                                              subset_state=self.image1.id['x'] > 1.5)

        self.viewer.add_data(self.hypercube)
        self.viewer.state.reference_data = self.hypercube

        assert self.viewer.layers[1].subset_array.shape == (4, 5)
        assert self.viewer.layers[3].subset_array.shape == (4, 5)

    def test_preserve_slice(self):

        # Regression test to make sure that when adding a second dataset to
        # an image viewer, the current slice in a cube does not change.

        self.viewer.add_data(self.hypercube)
        self.viewer.state.slices = (1, 2, 3, 4)
        self.viewer.add_data(self.image1)
        assert self.viewer.state.slices == (1, 2, 3, 4)

    def test_close(self):

        # Regression test for a bug that caused an error related to the toolbar
        # and _mpl_nav not being present when closing the viewer.

        self.viewer.toolbar.active_tool = self.viewer.toolbar.tools['mpl:zoom']
        self.viewer.close(warn=False)
Пример #51
0
def test_foreground_background_settings():

    d_1d = Data(x=np.random.random(100), y=np.random.random(100), label='Data 1d')
    d_2d = Data(x=np.random.random((100, 100)), y=np.random.random((100, 100)), label='Data 2d')

    dc = DataCollection([d_1d, d_2d])

    app = GlueApplication(dc)

    # Make sure that settings change existing viewers, so we create a bunch of
    # viewers here.

    scatter1 = app.new_data_viewer(ScatterWidget)
    scatter1.add_data(d_1d)

    image1 = app.new_data_viewer(ImageWidget)
    image1.add_data(d_2d)

    histogram1 = app.new_data_viewer(HistogramWidget)
    histogram1.add_data(d_1d)

    dendrogram1 = app.new_data_viewer(DendroWidget)

    example_custom = _generate_custom_viewer()

    custom1 = app.new_data_viewer(example_custom)

    RED = (1, 0, 0, 0.5)
    GREEN = (0, 1, 0, 0.6)

    app.show()

    with patch('glue.config.settings') as settings:

        settings.FOREGROUND_COLOR = 'black'
        settings.BACKGROUND_COLOR = 'white'
        settings.DATA_COLOR = '0.5'
        settings.DATA_ALPHA = 0.5

        dialog = PreferencesDialog(app)
        dialog.show()
        dialog.background = RED
        dialog.foreground = GREEN
        dialog.accept()

        assert_axes_background(scatter1.axes, RED)
        assert_axes_background(image1.axes, RED)
        assert_axes_background(histogram1.axes, RED)
        assert_axes_background(dendrogram1.axes, RED)
        assert_axes_background(custom1.axes, RED)

        assert_axes_foreground(scatter1.axes, GREEN)
        assert_axes_foreground(image1.axes, GREEN)
        assert_axes_foreground(histogram1.axes, GREEN)
        assert_axes_foreground(dendrogram1.axes, GREEN)
        assert_axes_foreground(custom1.axes, GREEN)

        # Now make sure that new viewers also inherit these settings

        scatter2 = app.new_data_viewer(ScatterWidget)
        scatter2.add_data(d_1d)

        image2 = app.new_data_viewer(ImageWidget)
        image2.add_data(d_2d)

        histogram2 = app.new_data_viewer(HistogramWidget)
        histogram2.add_data(d_1d)

        dendrogram2 = app.new_data_viewer(DendroWidget)
        custom2 = app.new_data_viewer(example_custom)

        assert_axes_background(scatter2.axes, RED)
        assert_axes_background(image2.axes, RED)
        assert_axes_background(histogram2.axes, RED)
        assert_axes_background(dendrogram2.axes, RED)
        assert_axes_background(custom2.axes, RED)

        assert_axes_foreground(scatter2.axes, GREEN)
        assert_axes_foreground(image2.axes, GREEN)
        assert_axes_foreground(histogram2.axes, GREEN)
        assert_axes_foreground(dendrogram2.axes, GREEN)
        assert_axes_foreground(custom2.axes, GREEN)
Пример #52
0
class TestProfileViewer(object):

    def setup_method(self, method):

        self.data = Data(label='d1')
        self.data.coords = SimpleCoordinates()
        self.data['x'] = np.arange(24).reshape((3, 4, 2))

        self.app = GlueApplication()
        self.session = self.app.session
        self.hub = self.session.hub

        self.data_collection = self.session.data_collection
        self.data_collection.append(self.data)

        self.viewer = self.app.new_data_viewer(ProfileViewer)

    def teardown_method(self, method):
        self.viewer.close()
        self.viewer = None
        self.app.close()
        self.app = None

    def test_functions(self):
        self.viewer.add_data(self.data)
        self.viewer.state.function = 'mean'
        assert len(self.viewer.layers) == 1
        layer_artist = self.viewer.layers[0]
        assert_allclose(layer_artist.state.profile[0], [0, 2, 4])
        assert_allclose(layer_artist.state.profile[1], [3.5, 11.5, 19.5])

    def test_incompatible(self):
        self.viewer.add_data(self.data)
        data2 = Data(y=np.random.random((3, 4, 2)))
        self.data_collection.append(data2)
        self.viewer.add_data(data2)
        assert len(self.viewer.layers) == 2
        assert self.viewer.layers[0].enabled
        assert not self.viewer.layers[1].enabled

    def test_selection(self):

        self.viewer.add_data(self.data)

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

        roi = XRangeROI(0.9, 2.1)

        self.viewer.apply_roi(roi)

        assert len(self.data.subsets) == 1
        assert_equal(self.data.subsets[0].to_mask()[:, 0, 0], [0, 1, 1])

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

        roi = XRangeROI(1.9, 3.1)

        self.viewer.apply_roi(roi)

        assert len(self.data.subsets) == 1
        assert_equal(self.data.subsets[0].to_mask()[:, 0, 0], [0, 1, 0])

    def test_enabled_layers(self):

        data2 = Data(label='d1', y=np.arange(24).reshape((3, 4, 2)))
        self.data_collection.append(data2)

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

        assert self.viewer.layers[0].enabled
        assert not self.viewer.layers[1].enabled

        self.data_collection.add_link(ComponentLink([data2.world_component_ids[1]], self.data.world_component_ids[0], using=lambda x: 2 * x))

        assert self.viewer.layers[0].enabled
        assert self.viewer.layers[1].enabled

    def test_slice_subset_state(self):

        self.viewer.add_data(self.data)

        subset = self.data.new_subset()
        subset.subset_state = SliceSubsetState(self.data, [slice(1, 2), slice(None)])

        assert self.viewer.layers[0].enabled
        assert self.viewer.layers[1].enabled

    def test_clone(self):

        # Regression test for a bug that meant that deserializing a profile
        # viewer resulted in disabled layers

        self.viewer.add_data(self.data)

        subset = self.data.new_subset()
        subset.subset_state = SliceSubsetState(self.data, [slice(1, 2), slice(None)])

        app = clone(self.app)

        assert app.viewers[0][0].layers[0].enabled
        assert app.viewers[0][0].layers[1].enabled

        app.close()

    def test_incompatible_on_add(self):

        # Regression test for a bug when adding a dataset to a profile viewer
        # with a single incompatible subset.

        subset_state = SliceSubsetState(self.data, [slice(1, 2), slice(None)])
        self.data_collection.new_subset_group(subset_state=subset_state, label='s1')

        data2 = Data(x=[[2, 3], [4, 3]], label='d2')
        self.data_collection.append(data2)
        self.viewer.add_data(data2)
Пример #53
0
from glue.core.link_helpers import LinkSame

ga = GlueApplication()
ga.resize(1230, 900)
ga.show()

ga.app.processEvents()
ga.screenshot('main_window1.png')

image = ga.load_data('w5.fits')
image.label = 'W5'

ga.app.processEvents()
ga.screenshot('data_open.png')

image_viewer = ga.new_data_viewer(ImageViewer, data=image)
image_viewer._mdi_wrapper.resize(450, 400)

image_viewer.state.layers[0].v_min = 440
image_viewer.state.layers[0].v_max = 900
image_viewer.state.layers[0].stretch = 'sqrt'
image_viewer.state.reset_limits()

ga.app.processEvents()
ga.screenshot('main_window2.png')

py, px = image.pixel_component_ids
subset_state = (px > 500) & (px < 900) & (py > 300) & (py < 800)

ga.data_collection.new_subset_group(subset_state=subset_state, label='Subset 1')
Пример #54
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)
Пример #55
0
def test_state_save():
    app = GlueApplication()
    w = app.new_data_viewer(viewer._widget_cls)
    check_clone_app(app)
Пример #56
0
class TestHistogramViewer(object):

    def setup_method(self, method):

        self.data = Data(label='d1', x=[3.4, 2.3, -1.1, 0.3], y=['a', 'b', 'c', 'a'])

        self.app = GlueApplication()
        self.session = self.app.session
        self.hub = self.session.hub

        self.data_collection = self.session.data_collection
        self.data_collection.append(self.data)

        self.viewer = self.app.new_data_viewer(HistogramViewer)

    def teardown_method(self, method):
        self.viewer.close()
        self.viewer = None
        self.app.close()
        self.app = None

    def test_basic(self):

        viewer_state = self.viewer.state

        # Check defaults when we add data
        self.viewer.add_data(self.data)

        assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:Coordinate components:Pixel Axis 0 [x]:World 0'

        assert viewer_state.x_att is self.data.id['x']
        assert viewer_state.x_min == -1.1
        assert viewer_state.x_max == 3.4
        assert viewer_state.y_min == 0.0
        assert viewer_state.y_max == 1.2

        assert viewer_state.hist_x_min == -1.1
        assert viewer_state.hist_x_max == 3.4
        assert viewer_state.hist_n_bin == 15

        assert not viewer_state.cumulative
        assert not viewer_state.normalize

        assert not viewer_state.x_log
        assert not viewer_state.y_log

        assert len(viewer_state.layers) == 1

        # Change to categorical component and check new values

        viewer_state.x_att = self.data.id['y']

        assert viewer_state.x_min == -0.5
        assert viewer_state.x_max == 2.5
        assert viewer_state.y_min == 0.0
        assert viewer_state.y_max == 2.4

        assert viewer_state.hist_x_min == -0.5
        assert viewer_state.hist_x_max == 2.5
        assert viewer_state.hist_n_bin == 3

        assert not viewer_state.cumulative
        assert not viewer_state.normalize

        assert not viewer_state.x_log
        assert not viewer_state.y_log

    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

        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

    def test_flip(self):

        viewer_state = self.viewer.state

        self.viewer.add_data(self.data)

        assert viewer_state.x_min == -1.1
        assert viewer_state.x_max == 3.4

        self.viewer.options_widget().button_flip_x.click()

        assert viewer_state.x_min == 3.4
        assert viewer_state.x_max == -1.1

    def test_remove_data(self):
        self.viewer.add_data(self.data)
        assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:Coordinate components:Pixel Axis 0 [x]:World 0'
        self.data_collection.remove(self.data)
        assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == ''

    def test_update_component_updates_title(self):
        self.viewer.add_data(self.data)
        assert self.viewer.windowTitle() == '1D Histogram'
        self.viewer.state.x_att = self.data.id['y']
        assert self.viewer.windowTitle() == '1D Histogram'

    def test_combo_updates_with_component_add(self):
        self.viewer.add_data(self.data)
        self.data.add_component([3, 4, 1, 2], 'z')
        assert self.viewer.state.x_att is self.data.id['x']
        assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]:World 0'

    def test_nonnumeric_first_component(self):
        # regression test for #208. Shouldn't complain if
        # first component is non-numerical
        data = core.Data()
        data.add_component(['a', 'b', 'c'], label='c1')
        data.add_component([1, 2, 3], label='c2')
        self.data_collection.append(data)
        self.viewer.add_data(data)

    def test_nan_component(self):
        # regression test for case when all values are NaN in a component
        data = core.Data()
        data.add_component([np.nan, np.nan, np.nan], label='c1')
        self.data_collection.append(data)
        self.viewer.add_data(data)

    def test_histogram_values(self):

        # Check the actual values of the histograms

        viewer_state = self.viewer.state

        self.viewer.add_data(self.data)

        # Numerical attribute

        viewer_state.hist_x_min = -5
        viewer_state.hist_x_max = 5
        viewer_state.hist_n_bin = 4

        assert_allclose(self.viewer.state.y_max, 2.4)

        assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 1, 2, 1])
        assert_allclose(self.viewer.layers[0].state.histogram[0], [-5, -2.5, 0, 2.5, 5])

        cid = self.data.main_components[0]
        self.data_collection.new_subset_group('subset 1', cid < 2)

        assert_allclose(self.viewer.layers[1].state.histogram[1], [0, 1, 1, 0])
        assert_allclose(self.viewer.layers[1].state.histogram[0], [-5, -2.5, 0, 2.5, 5])

        viewer_state.normalize = True

        assert_allclose(self.viewer.state.y_max, 0.24)
        assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 0.1, 0.2, 0.1])
        assert_allclose(self.viewer.layers[0].state.histogram[0], [-5, -2.5, 0, 2.5, 5])
        assert_allclose(self.viewer.layers[1].state.histogram[1], [0, 0.2, 0.2, 0])
        assert_allclose(self.viewer.layers[1].state.histogram[0], [-5, -2.5, 0, 2.5, 5])

        viewer_state.cumulative = True

        assert_allclose(self.viewer.state.y_max, 1.2)
        assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 0.25, 0.75, 1.0])
        assert_allclose(self.viewer.layers[0].state.histogram[0], [-5, -2.5, 0, 2.5, 5])
        assert_allclose(self.viewer.layers[1].state.histogram[1], [0, 0.5, 1.0, 1.0])
        assert_allclose(self.viewer.layers[1].state.histogram[0], [-5, -2.5, 0, 2.5, 5])

        viewer_state.normalize = False

        assert_allclose(self.viewer.state.y_max, 4.8)
        assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 1, 3, 4])
        assert_allclose(self.viewer.layers[0].state.histogram[0], [-5, -2.5, 0, 2.5, 5])
        assert_allclose(self.viewer.layers[1].state.histogram[1], [0, 1, 2, 2])
        assert_allclose(self.viewer.layers[1].state.histogram[0], [-5, -2.5, 0, 2.5, 5])

        viewer_state.cumulative = False

        # Categorical attribute

        viewer_state.x_att = self.data.id['y']

        formatter = self.viewer.axes.xaxis.get_major_formatter()
        xlabels = [formatter.format_data(pos) for pos in range(3)]
        assert xlabels == ['a', 'b', 'c']

        assert_allclose(self.viewer.state.y_max, 2.4)
        assert_allclose(self.viewer.layers[0].state.histogram[1], [2, 1, 1])
        assert_allclose(self.viewer.layers[0].state.histogram[0], [-0.5, 0.5, 1.5, 2.5])
        assert_allclose(self.viewer.layers[1].state.histogram[1], [1, 0, 1])
        assert_allclose(self.viewer.layers[1].state.histogram[0], [-0.5, 0.5, 1.5, 2.5])

        viewer_state.normalize = True

        assert_allclose(self.viewer.state.y_max, 0.6)
        assert_allclose(self.viewer.layers[0].state.histogram[1], [0.5, 0.25, 0.25])
        assert_allclose(self.viewer.layers[0].state.histogram[0], [-0.5, 0.5, 1.5, 2.5])
        assert_allclose(self.viewer.layers[1].state.histogram[1], [0.5, 0, 0.5])
        assert_allclose(self.viewer.layers[1].state.histogram[0], [-0.5, 0.5, 1.5, 2.5])

        viewer_state.cumulative = True

        assert_allclose(self.viewer.state.y_max, 1.2)
        assert_allclose(self.viewer.layers[0].state.histogram[1], [0.5, 0.75, 1])
        assert_allclose(self.viewer.layers[0].state.histogram[0], [-0.5, 0.5, 1.5, 2.5])
        assert_allclose(self.viewer.layers[1].state.histogram[1], [0.5, 0.5, 1])
        assert_allclose(self.viewer.layers[1].state.histogram[0], [-0.5, 0.5, 1.5, 2.5])

        viewer_state.normalize = False

        assert_allclose(self.viewer.state.y_max, 4.8)
        assert_allclose(self.viewer.layers[0].state.histogram[1], [2, 3, 4])
        assert_allclose(self.viewer.layers[0].state.histogram[0], [-0.5, 0.5, 1.5, 2.5])
        assert_allclose(self.viewer.layers[1].state.histogram[1], [1, 1, 2])
        assert_allclose(self.viewer.layers[1].state.histogram[0], [-0.5, 0.5, 1.5, 2.5])

        # TODO: add tests for log

    def test_apply_roi(self):

        # Check that when doing an ROI selection, the ROI clips to the bin edges
        # outside the selection

        viewer_state = self.viewer.state

        self.viewer.add_data(self.data)

        viewer_state.hist_x_min = -5
        viewer_state.hist_x_max = 5
        viewer_state.hist_n_bin = 4

        roi = XRangeROI(-0.2, 0.1)

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

        self.viewer.apply_roi(roi)

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

        assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 1, 2, 1])
        assert_allclose(self.viewer.layers[1].state.histogram[1], [0, 1, 2, 0])

        assert_allclose(self.data.subsets[0].to_mask(), [0, 1, 1, 1])

        state = self.data.subsets[0].subset_state
        assert isinstance(state, RangeSubsetState)

        assert state.lo == -2.5
        assert state.hi == 2.5

        # TODO: add a similar test in log space

    def test_apply_roi_categorical(self):

        # Check that when doing an ROI selection, the ROI clips to the bin edges
        # outside the selection

        viewer_state = self.viewer.state

        self.viewer.add_data(self.data)

        viewer_state.x_att = self.data.id['y']

        roi = XRangeROI(0.3, 0.9)

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

        self.viewer.apply_roi(roi)

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

        assert_allclose(self.viewer.layers[0].state.histogram[1], [2, 1, 1])
        assert_allclose(self.viewer.layers[1].state.histogram[1], [2, 1, 0])

        assert_allclose(self.data.subsets[0].to_mask(), [1, 1, 0, 1])

        state = self.data.subsets[0].subset_state
        assert isinstance(state, CategoricalROISubsetState)

        assert_equal(state.roi.categories, ['a', 'b'])

    def test_apply_roi_empty(self):
        # Make sure that doing an ROI selection on an empty viewer doesn't
        # produce error messsages
        roi = XRangeROI(-0.2, 0.1)
        self.viewer.apply_roi(roi)

    def test_axes_labels(self):

        viewer_state = self.viewer.state

        self.viewer.add_data(self.data)

        assert self.viewer.axes.get_xlabel() == 'x'
        assert self.viewer.axes.get_ylabel() == 'Number'

        viewer_state.x_log = True

        assert self.viewer.axes.get_xlabel() == 'Log x'
        assert self.viewer.axes.get_ylabel() == 'Number'

        viewer_state.x_att = self.data.id['y']

        assert self.viewer.axes.get_xlabel() == 'y'
        assert self.viewer.axes.get_ylabel() == 'Number'

        viewer_state.normalize = True

        assert self.viewer.axes.get_xlabel() == 'y'
        assert self.viewer.axes.get_ylabel() == 'Normalized number'

        viewer_state.normalize = False
        viewer_state.cumulative = True

        assert self.viewer.axes.get_xlabel() == 'y'
        assert self.viewer.axes.get_ylabel() == 'Number'

    def test_y_min_y_max(self):

        # Regression test for a bug that caused y_max to not be set correctly
        # when multiple subsets were present and after turning on normalization
        # after switching to a different attribute from that used to make the
        # selection.

        viewer_state = self.viewer.state
        self.viewer.add_data(self.data)

        self.data.add_component([3.4, 3.5, 10.2, 20.3], 'z')

        viewer_state.x_att = self.data.id['x']

        cid = self.data.main_components[0]
        self.data_collection.new_subset_group('subset 1', cid < 1)

        cid = self.data.main_components[0]
        self.data_collection.new_subset_group('subset 2', cid < 2)

        cid = self.data.main_components[0]
        self.data_collection.new_subset_group('subset 3', cid < 3)

        assert_allclose(self.viewer.state.y_min, 0)
        assert_allclose(self.viewer.state.y_max, 1.2)

        viewer_state.x_att = self.data.id['z']

        assert_allclose(self.viewer.state.y_min, 0)
        assert_allclose(self.viewer.state.y_max, 2.4)

        viewer_state.normalize = True

        assert_allclose(self.viewer.state.y_min, 0)
        assert_allclose(self.viewer.state.y_max, 0.5325443786982249)

    def test_update_when_limits_unchanged(self):

        # Regression test for glue-viz/glue#1010 - this bug caused histograms
        # to not be recomputed if the attribute changed but the limits and
        # number of bins did not.

        viewer_state = self.viewer.state

        self.viewer.add_data(self.data)

        viewer_state.x_att = self.data.id['y']
        viewer_state.hist_x_min = -10.1
        viewer_state.hist_x_max = +10
        viewer_state.hist_n_bin = 5

        assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 0, 3, 1, 0])

        viewer_state.x_att = self.data.id['x']
        viewer_state.hist_x_min = -10.1
        viewer_state.hist_x_max = +10
        viewer_state.hist_n_bin = 5

        assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 0, 2, 2, 0])

        viewer_state.x_att = self.data.id['y']

        assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 0, 3, 1, 0])

        viewer_state.x_att = self.data.id['x']

        assert_allclose(self.viewer.layers[0].state.histogram[1], [0, 0, 2, 2, 0])

    def test_component_replaced(self):

        # regression test for 508 - if a component ID is replaced, we should
        # make sure that the component ID is selected if the old component ID
        # was selected

        self.viewer.add_data(self.data)
        self.viewer.state.x_att = self.data.id['x']
        test = ComponentID('test')
        self.data.update_id(self.viewer.state.x_att, test)
        assert self.viewer.state.x_att is test
        assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:test:y:Coordinate components:Pixel Axis 0 [x]:World 0'

    def test_nbin_override_persists_over_numerical_attribute_change(self):

        # regression test for #398

        self.data.add_component([3, 4, 1, 2], 'z')

        self.viewer.add_data(self.data)
        self.viewer.state.x_att = self.data.id['x']
        self.viewer.state.hist_n_bin = 7
        self.viewer.state.x_att = self.data.id['z']
        assert self.viewer.state.hist_n_bin == 7

    @pytest.mark.parametrize('protocol', [0, 1])
    def test_session_back_compat(self, protocol):

        filename = os.path.join(DATA, 'histogram_v{0}.glu'.format(protocol))

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

        state = GlueUnSerializer.loads(session)

        ga = state.object('__main__')

        dc = ga.session.data_collection

        assert len(dc) == 1

        assert dc[0].label == 'data'

        viewer1 = ga.viewers[0][0]
        assert len(viewer1.state.layers) == 2
        assert viewer1.state.x_att is dc[0].id['a']
        assert_allclose(viewer1.state.x_min, 0)
        assert_allclose(viewer1.state.x_max, 9)
        assert_allclose(viewer1.state.y_min, 0)
        assert_allclose(viewer1.state.y_max, 2.4)
        assert_allclose(viewer1.state.hist_x_min, 0)
        assert_allclose(viewer1.state.hist_x_max, 9)
        assert_allclose(viewer1.state.hist_n_bin, 6)
        assert not viewer1.state.x_log
        assert not viewer1.state.y_log
        assert viewer1.state.layers[0].visible
        assert not viewer1.state.layers[1].visible
        assert not viewer1.state.cumulative
        assert not viewer1.state.normalize

        viewer2 = ga.viewers[0][1]
        assert viewer2.state.x_att is dc[0].id['b']
        assert_allclose(viewer2.state.x_min, 2)
        assert_allclose(viewer2.state.x_max, 16)
        assert_allclose(viewer2.state.y_min, 0)
        assert_allclose(viewer2.state.y_max, 1.2)
        assert_allclose(viewer2.state.hist_x_min, 2)
        assert_allclose(viewer2.state.hist_x_max, 16)
        assert_allclose(viewer2.state.hist_n_bin, 8)
        assert not viewer2.state.x_log
        assert not viewer2.state.y_log
        assert viewer2.state.layers[0].visible
        assert viewer2.state.layers[1].visible
        assert not viewer2.state.cumulative
        assert not viewer2.state.normalize

        viewer3 = ga.viewers[0][2]
        assert viewer3.state.x_att is dc[0].id['a']
        assert_allclose(viewer3.state.x_min, 0)
        assert_allclose(viewer3.state.x_max, 9)
        assert_allclose(viewer3.state.y_min, 0.01111111111111111)
        assert_allclose(viewer3.state.y_max, 0.7407407407407407)
        assert_allclose(viewer3.state.hist_x_min, 0)
        assert_allclose(viewer3.state.hist_x_max, 9)
        assert_allclose(viewer3.state.hist_n_bin, 10)
        assert not viewer3.state.x_log
        assert viewer3.state.y_log
        assert viewer3.state.layers[0].visible
        assert viewer3.state.layers[1].visible
        assert not viewer3.state.cumulative
        assert viewer3.state.normalize

        viewer4 = ga.viewers[0][3]
        assert viewer4.state.x_att is dc[0].id['a']
        assert_allclose(viewer4.state.x_min, -1)
        assert_allclose(viewer4.state.x_max, 10)
        assert_allclose(viewer4.state.y_min, 0)
        assert_allclose(viewer4.state.y_max, 12)
        assert_allclose(viewer4.state.hist_x_min, -1)
        assert_allclose(viewer4.state.hist_x_max, 10)
        assert_allclose(viewer4.state.hist_n_bin, 4)
        assert not viewer4.state.x_log
        assert not viewer4.state.y_log
        assert viewer4.state.layers[0].visible
        assert viewer4.state.layers[1].visible
        assert viewer4.state.cumulative
        assert not viewer4.state.normalize

    def test_apply_roi_single(self):

        # Regression test for a bug that caused mode.update to be called
        # multiple times and resulted in all other viewers receiving many
        # messages regarding subset updates (this occurred when multiple)
        # datasets were present.

        layer_tree = LayerTreeWidget(session=self.session)
        layer_tree.set_checkable(False)
        layer_tree.setup(self.data_collection)
        layer_tree.bind_selection_to_edit_subset()

        class Client(HubListener):

            def __init__(self, *args, **kwargs):
                super(Client, self).__init__(*args, **kwargs)
                self.count = Counter()

            def ping(self, message):
                self.count[message.sender] += 1

            def register_to_hub(self, hub):
                hub.subscribe(self, SubsetUpdateMessage, handler=self.ping)

        d1 = Data(a=[1, 2, 3], label='d1')
        d2 = Data(b=[1, 2, 3], label='d2')
        d3 = Data(c=[1, 2, 3], label='d3')
        d4 = Data(d=[1, 2, 3], label='d4')

        self.data_collection.append(d1)
        self.data_collection.append(d2)
        self.data_collection.append(d3)
        self.data_collection.append(d4)

        client = Client()
        client.register_to_hub(self.hub)

        self.viewer.add_data(d1)
        self.viewer.add_data(d3)

        roi = XRangeROI(2.5, 3.5)
        self.viewer.apply_roi(roi)

        for subset in client.count:
            assert client.count[subset] == 1

    def test_datetime64_support(self, tmpdir):

        self.data.add_component(np.array([100, 200, 300, 400], dtype='M8[D]'), 't1')
        self.viewer.add_data(self.data)
        self.viewer.state.x_att = self.data.id['t1']

        # Matplotlib deals with dates by converting them to the number of days
        # since 01-01-0001, so we can check that the limits are correctly
        # converted (and not 100 to 400)
        assert self.viewer.axes.get_xlim() == (719263.0, 719563.0)

        # Apply an ROI selection in plotting coordinates
        roi = XRangeROI(719313, 719513)
        self.viewer.apply_roi(roi)

        # Check that the two middle elements are selected
        assert_equal(self.data.subsets[0].to_mask(), [0, 1, 1, 0])

        # Make sure that the Qt labels look ok
        options = self.viewer.options_widget().ui
        assert options.valuetext_x_min.text() == '1970-04-11'
        assert options.valuetext_x_max.text() == '1971-02-05'

        # Make sure that we can set the xmin/xmax to a string date
        assert_equal(self.viewer.state.x_min, np.datetime64('1970-04-11', 'D'))
        options.valuetext_x_min.setText('1970-04-14')
        options.valuetext_x_min.editingFinished.emit()
        assert self.viewer.axes.get_xlim() == (719266.0, 719563.0)
        assert_equal(self.viewer.state.x_min, np.datetime64('1970-04-14', 'D'))

        # Make sure that everything works fine after saving/reloading
        filename = tmpdir.join('test_datetime64.glu').strpath
        self.session.application.save_session(filename)
        with open(filename, 'r') as f:
            session = f.read()
        state = GlueUnSerializer.loads(session)
        ga = state.object('__main__')
        viewer = ga.viewers[0][0]
        options = viewer.options_widget().ui

        assert_equal(self.viewer.state.x_min, np.datetime64('1970-04-14', 'D'))

        assert options.valuetext_x_min.text() == '1970-04-14'
        assert options.valuetext_x_max.text() == '1971-02-05'
Пример #57
0
class TestQtPlotlyExporter():

    def setup_class(self):

        data = Data(x=[1, 2, 3], y=[2, 3, 4], label='data')
        dc = DataCollection([data])
        self.app = GlueApplication(dc)

        data.style.color = '#000000'
        v = self.app.new_data_viewer(HistogramViewer, data=data)
        v.component = data.id['y']
        v.xmin = 0
        v.xmax = 10
        v.bins = 20

        self.args, self.kwargs = build_plotly_call(self.app)

    def teardown_class(self):
        self.app.close()
        self.app = None

    def get_exporter(self):
        return QtPlotlyExporter(plotly_args=self.args, plotly_kwargs=self.kwargs)

    def test_default_no_credentials(self, tmpdir):

        credentials_file = tmpdir.join('.credentials').strpath

        make_credentials_file(credentials_file)

        with patch('plotly.tools.CREDENTIALS_FILE', credentials_file):

            exporter = self.get_exporter()

            assert not exporter.radio_account_config.isChecked()
            assert exporter.radio_account_manual.isChecked()

            assert exporter.radio_sharing_secret.isChecked()

    def test_default_with_credentials(self, tmpdir):

        credentials_file = tmpdir.join('.credentials').strpath

        make_credentials_file(credentials_file, username='******', api_key='batmobile')

        with patch('plotly.tools.CREDENTIALS_FILE', credentials_file):

            exporter = self.get_exporter()

            assert exporter.radio_account_config.isChecked()
            assert 'username: batman' in exporter.radio_account_config.text()
            assert exporter.radio_sharing_secret.isChecked()

    def test_edit_username_toggle_custom(self, tmpdir):

        credentials_file = tmpdir.join('.credentials').strpath

        make_credentials_file(credentials_file, username='******', api_key='batmobile')

        with patch('plotly.tools.CREDENTIALS_FILE', credentials_file):

            exporter = self.get_exporter()

            assert exporter.radio_account_config.isChecked()
            exporter.username = '******'
            assert exporter.radio_account_manual.isChecked()

            exporter.radio_account_config.setChecked(True)
            assert exporter.radio_account_config.isChecked()
            exporter.api_key = 'a'
            assert exporter.radio_account_manual.isChecked()

    def test_accept_default(self, tmpdir):

        credentials_file = tmpdir.join('.credentials').strpath

        make_credentials_file(credentials_file, username='******', api_key='batmobile')

        with patch('plotly.tools.CREDENTIALS_FILE', credentials_file):
            with patch('plotly.plotly.plot', mock.MagicMock()):
                with patch('plotly.plotly.sign_in', mock.MagicMock()):
                    with patch('webbrowser.open_new_tab') as open_new_tab:
                        exporter = self.get_exporter()
                        exporter.accept()
                        assert exporter.text_status.text() == 'Exporting succeeded'

    ERRORS = [
        (PlotlyError(SIGN_IN_ERROR), 'Authentication failed'),
        (PlotlyError(MAX_PRIVATE_ERROR), 'Maximum number of private plots reached'),
        (PlotlyError('Oh noes!'), 'An unexpected error occurred'),
        (TypeError('A banana is not an apple'), 'An unexpected error occurred')
    ]

    @pytest.mark.parametrize(('error', 'status'), ERRORS)
    def test_accept_errors(self, tmpdir, error, status):

        credentials_file = tmpdir.join('.credentials').strpath

        make_credentials_file(credentials_file, username='******', api_key='batmobile')

        plot = mock.MagicMock(side_effect=error)

        sign_in = mock.MagicMock()

        with patch('plotly.tools.CREDENTIALS_FILE', credentials_file):
            with patch('plotly.plotly.sign_in', sign_in):
                with patch('plotly.plotly.plot', plot):
                    with patch('webbrowser.open_new_tab'):
                        exporter = self.get_exporter()
                        exporter.accept()
                        assert exporter.text_status.text() == status

    def test_fix_url(self, tmpdir):

        credentials_file = tmpdir.join('.credentials').strpath

        make_credentials_file(credentials_file, username='******', api_key='batmobile')

        plot = mock.MagicMock(return_value='https://plot.ly/~batman/6?share_key=rbkWvJQn6cyj3HMMGROiqI')

        sign_in = mock.MagicMock()

        with patch('plotly.tools.CREDENTIALS_FILE', credentials_file):
            with patch('plotly.plotly.sign_in', sign_in):
                with patch('plotly.plotly.plot', plot):
                    with patch('webbrowser.open_new_tab') as open_new_tab:
                        exporter = self.get_exporter()
                        exporter.accept()
                        assert open_new_tab.called_once_with('https://plot.ly/~batman/6/?share_key=rbkWvJQn6cyj3HMMGROiqI')
Пример #58
0
class TestImageViewer(object):

    def setup_method(self, method):

        self.coords = MyCoords()
        self.image1 = Data(label='image1', x=[[1, 2], [3, 4]], y=[[4, 5], [2, 3]])
        self.image2 = Data(label='image2', a=[[3, 3], [2, 2]], b=[[4, 4], [3, 2]],
                           coords=self.coords)
        self.catalog = Data(label='catalog', c=[1, 3, 2], d=[4, 3, 3])
        self.hypercube = Data(label='hypercube', x=np.arange(120).reshape((2, 3, 4, 5)))

        # Create data versions with WCS coordinates
        self.image1_wcs = Data(label='image1_wcs', x=self.image1['x'],
                               coords=WCSCoordinates(wcs=WCS(naxis=2)))
        self.hypercube_wcs = Data(label='hypercube_wcs', x=self.hypercube['x'],
                                  coords=WCSCoordinates(wcs=WCS(naxis=4)))

        self.application = GlueApplication()

        self.session = self.application.session

        self.hub = self.session.hub

        self.data_collection = self.session.data_collection
        self.data_collection.append(self.image1)
        self.data_collection.append(self.image2)
        self.data_collection.append(self.catalog)
        self.data_collection.append(self.hypercube)
        self.data_collection.append(self.image1_wcs)
        self.data_collection.append(self.hypercube_wcs)

        self.viewer = self.application.new_data_viewer(ImageViewer)

        self.data_collection.register_to_hub(self.hub)
        self.viewer.register_to_hub(self.hub)

        self.options_widget = self.viewer.options_widget()

    def teardown_method(self, method):
        self.viewer.close()
        self.viewer = None
        self.application.close()
        self.application = None

    def test_basic(self):

        # Check defaults when we add data

        self.viewer.add_data(self.image1)

        assert combo_as_string(self.options_widget.ui.combosel_x_att_world) == 'Coordinate components:World 0:World 1'
        assert combo_as_string(self.options_widget.ui.combosel_y_att_world) == 'Coordinate components:World 0:World 1'

        assert self.viewer.axes.get_xlabel() == 'World 1'
        assert self.viewer.state.x_att_world is self.image1.id['World 1']
        assert self.viewer.state.x_att is self.image1.pixel_component_ids[1]
        assert_allclose(self.viewer.state.x_min, -0.8419913419913423)
        assert_allclose(self.viewer.state.x_max, +1.8419913419913423)

        assert self.viewer.axes.get_ylabel() == 'World 0'
        assert self.viewer.state.y_att_world is self.image1.id['World 0']
        assert self.viewer.state.y_att is self.image1.pixel_component_ids[0]
        assert self.viewer.state.y_min == -0.5
        assert self.viewer.state.y_max == +1.5

        assert not self.viewer.state.x_log
        assert not self.viewer.state.y_log

        assert len(self.viewer.state.layers) == 1

    def test_custom_coords(self):

        # Check defaults when we add data with coordinates

        self.viewer.add_data(self.image2)

        assert combo_as_string(self.options_widget.ui.combosel_x_att_world) == 'Coordinate components:Banana:Apple'
        assert combo_as_string(self.options_widget.ui.combosel_x_att_world) == 'Coordinate components:Banana:Apple'

        assert self.viewer.axes.get_xlabel() == 'Apple'
        assert self.viewer.state.x_att_world is self.image2.id['Apple']
        assert self.viewer.state.x_att is self.image2.pixel_component_ids[1]
        assert self.viewer.axes.get_ylabel() == 'Banana'
        assert self.viewer.state.y_att_world is self.image2.id['Banana']
        assert self.viewer.state.y_att is self.image2.pixel_component_ids[0]

    def test_flip(self):

        self.viewer.add_data(self.image1)

        x_min_start = self.viewer.state.x_min
        x_max_start = self.viewer.state.x_max

        self.options_widget.button_flip_x.click()

        assert self.viewer.state.x_min == x_max_start
        assert self.viewer.state.x_max == x_min_start

        y_min_start = self.viewer.state.y_min
        y_max_start = self.viewer.state.y_max

        self.options_widget.button_flip_y.click()

        assert self.viewer.state.y_min == y_max_start
        assert self.viewer.state.y_max == y_min_start

    def test_combo_updates_with_component_add(self):
        self.viewer.add_data(self.image1)
        self.image1.add_component([[9, 9], [8, 8]], 'z')
        assert self.viewer.state.x_att_world is self.image1.id['World 1']
        assert self.viewer.state.y_att_world is self.image1.id['World 0']
        # TODO: there should be an easier way to do this
        layer_style_editor = self.viewer._view.layout_style_widgets[self.viewer.layers[0]]
        assert combo_as_string(layer_style_editor.ui.combosel_attribute) == 'x:y:z'

    def test_apply_roi(self):

        self.viewer.add_data(self.image1)

        roi = RectangularROI(0.4, 1.6, -0.6, 0.6)

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

        self.viewer.apply_roi(roi)

        assert len(self.viewer.layers) == 2
        assert len(self.image1.subsets) == 1

        assert_allclose(self.image1.subsets[0].to_mask(), [[0, 1], [0, 0]])

        state = self.image1.subsets[0].subset_state
        assert isinstance(state, RoiSubsetState)

    def test_apply_roi_empty(self):
        # Make sure that doing an ROI selection on an empty viewer doesn't
        # produce error messsages
        roi = XRangeROI(-0.2, 0.1)
        self.viewer.apply_roi(roi)

    def test_identical(self):

        # Check what happens if we set both attributes to the same coordinates

        self.viewer.add_data(self.image2)

        assert self.viewer.state.x_att_world is self.image2.id['Apple']
        assert self.viewer.state.y_att_world is self.image2.id['Banana']

        self.viewer.state.y_att_world = self.image2.id['Apple']

        assert self.viewer.state.x_att_world is self.image2.id['Banana']
        assert self.viewer.state.y_att_world is self.image2.id['Apple']

        self.viewer.state.x_att_world = self.image2.id['Apple']

        assert self.viewer.state.x_att_world is self.image2.id['Apple']
        assert self.viewer.state.y_att_world is self.image2.id['Banana']

    def test_duplicate_subsets(self):

        # Regression test: make sure that when adding a seconda layer for the
        # same dataset, we don't add the subsets all over again.

        self.viewer.add_data(self.image1)
        self.data_collection.new_subset_group(subset_state=self.image1.id['x'] > 1, label='A')

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

        self.viewer.add_data(self.image1)

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

    def test_aspect_subset(self):

        self.viewer.add_data(self.image1)

        assert self.viewer.state.aspect == 'equal'

        self.viewer.state.aspect = 'auto'

        self.data_collection.new_subset_group('s1', self.image1.id['x'] > 0.)

        assert len(self.viewer.state.layers) == 2

        assert self.viewer.state.aspect == 'auto'

        self.viewer.state.aspect = 'equal'

        self.data_collection.new_subset_group('s2', self.image1.id['x'] > 1.)

        assert len(self.viewer.state.layers) == 3

        assert self.viewer.state.aspect == 'equal'

    def test_hypercube(self):

        # Check defaults when we add data

        self.viewer.add_data(self.hypercube)

        assert combo_as_string(self.options_widget.ui.combosel_x_att_world) == 'Coordinate components:World 0:World 1:World 2:World 3'
        assert combo_as_string(self.options_widget.ui.combosel_x_att_world) == 'Coordinate components:World 0:World 1:World 2:World 3'

        assert self.viewer.axes.get_xlabel() == 'World 3'
        assert self.viewer.state.x_att_world is self.hypercube.id['World 3']
        assert self.viewer.state.x_att is self.hypercube.pixel_component_ids[3]
        assert_allclose(self.viewer.state.x_min, -0.6839826839826846)
        assert_allclose(self.viewer.state.x_max, +4.6839826839826846)

        assert self.viewer.axes.get_ylabel() == 'World 2'
        assert self.viewer.state.y_att_world is self.hypercube.id['World 2']
        assert self.viewer.state.y_att is self.hypercube.pixel_component_ids[2]
        assert self.viewer.state.y_min == -0.5
        assert self.viewer.state.y_max == +3.5

        assert not self.viewer.state.x_log
        assert not self.viewer.state.y_log

        assert len(self.viewer.state.layers) == 1

    def test_hypercube_world(self):

        # Check defaults when we add data

        wcs = WCS(naxis=4)
        hypercube2 = Data()
        hypercube2.coords = WCSCoordinates(wcs=wcs)
        hypercube2.add_component(np.random.random((2, 3, 4, 5)), 'a')

        self.data_collection.append(hypercube2)

        self.viewer.add_data(hypercube2)

    def test_incompatible_subset(self):
        self.viewer.add_data(self.image1)
        self.data_collection.new_subset_group(subset_state=self.catalog.id['c'] > 1, label='A')

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

    def test_apply_roi_single(self):

        # Regression test for a bug that caused mode.update to be called
        # multiple times and resulted in all other viewers receiving many
        # messages regarding subset updates (this occurred when multiple)
        # datasets were present.

        layer_tree = LayerTreeWidget(session=self.session)
        layer_tree.set_checkable(False)
        layer_tree.setup(self.data_collection)
        layer_tree.bind_selection_to_edit_subset()

        class Client(HubListener):

            def __init__(self, *args, **kwargs):
                super(Client, self).__init__(*args, **kwargs)
                self.count = Counter()

            def ping(self, message):
                self.count[message.sender] += 1

            def register_to_hub(self, hub):
                hub.subscribe(self, SubsetUpdateMessage, handler=self.ping)

        d1 = Data(a=[[1, 2], [3, 4]], label='d1')
        d2 = Data(b=[[1, 2], [3, 4]], label='d2')
        d3 = Data(c=[[1, 2], [3, 4]], label='d3')
        d4 = Data(d=[[1, 2], [3, 4]], label='d4')

        self.data_collection.append(d1)
        self.data_collection.append(d2)
        self.data_collection.append(d3)
        self.data_collection.append(d4)

        client = Client()
        client.register_to_hub(self.hub)

        self.viewer.add_data(d1)
        self.viewer.add_data(d3)

        roi = XRangeROI(2.5, 3.5)
        self.viewer.apply_roi(roi)

        for subset in client.count:
            assert client.count[subset] == 1

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

        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)

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

        # All layers should now be enabled

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

        self.data_collection.remove_link(link2)

        # 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

    def test_change_reference_data(self, capsys):

        # Test to make sure everything works fine if we change the reference data.

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

        assert self.viewer.state.reference_data is self.image1
        assert self.viewer.state.x_att_world is self.image1.world_component_ids[-1]
        assert self.viewer.state.y_att_world is self.image1.world_component_ids[-2]
        assert self.viewer.state.x_att is self.image1.pixel_component_ids[-1]
        assert self.viewer.state.y_att is self.image1.pixel_component_ids[-2]

        self.viewer.state.reference_data = self.image2

        assert self.viewer.state.reference_data is self.image2
        assert self.viewer.state.x_att_world is self.image2.world_component_ids[-1]
        assert self.viewer.state.y_att_world is self.image2.world_component_ids[-2]
        assert self.viewer.state.x_att is self.image2.pixel_component_ids[-1]
        assert self.viewer.state.y_att is self.image2.pixel_component_ids[-2]

        self.viewer.state.reference_data = self.image1

        assert self.viewer.state.reference_data is self.image1
        assert self.viewer.state.x_att_world is self.image1.world_component_ids[-1]
        assert self.viewer.state.y_att_world is self.image1.world_component_ids[-2]
        assert self.viewer.state.x_att is self.image1.pixel_component_ids[-1]
        assert self.viewer.state.y_att is self.image1.pixel_component_ids[-2]

        # 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() == ""

    @pytest.mark.parametrize('wcs', [False, True])
    def test_change_reference_data_dimensionality(self, capsys, wcs):

        # Regression test for a bug that caused an exception when changing
        # the dimensionality of the reference data

        if wcs:
            first = self.image1_wcs
            second = self.hypercube_wcs
        else:
            first = self.image1
            second = self.hypercube

        self.viewer.add_data(first)
        self.viewer.add_data(second)

        assert self.viewer.state.reference_data is first
        assert self.viewer.state.x_att_world is first.world_component_ids[-1]
        assert self.viewer.state.y_att_world is first.world_component_ids[-2]
        assert self.viewer.state.x_att is first.pixel_component_ids[-1]
        assert self.viewer.state.y_att is first.pixel_component_ids[-2]

        self.viewer.state.reference_data = second

        assert self.viewer.state.reference_data is second
        assert self.viewer.state.x_att_world is second.world_component_ids[-1]
        assert self.viewer.state.y_att_world is second.world_component_ids[-2]
        assert self.viewer.state.x_att is second.pixel_component_ids[-1]
        assert self.viewer.state.y_att is second.pixel_component_ids[-2]

        self.viewer.state.reference_data = first

        assert self.viewer.state.reference_data is first
        assert self.viewer.state.x_att_world is first.world_component_ids[-1]
        assert self.viewer.state.y_att_world is first.world_component_ids[-2]
        assert self.viewer.state.x_att is first.pixel_component_ids[-1]
        assert self.viewer.state.y_att is first.pixel_component_ids[-2]

        # 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() == ""

    def test_scatter_overlay(self):
        self.viewer.add_data(self.image1)
        self.viewer.add_data(self.catalog)

    def test_removed_subset(self):

        # Regression test for a bug in v0.11.0 that meant that if a subset
        # was removed, the image viewer would then crash when changing view
        # (e.g. zooming in). The bug was caused by undeleted references to
        # ModestImage due to circular references. We therefore check in this
        # test how many ModestImage objects exist.

        def get_modest_images():
            mi = []
            gc.collect()
            for obj in gc.get_objects():
                try:
                    if isinstance(obj, ModestImage):
                        mi.append(obj)
                except ReferenceError:
                    pass
            return mi

        # The viewer starts off with one ModestImage. This is also a good test
        # that other ModestImages in other tests have been removed.
        assert len(get_modest_images()) == 1

        large_image = Data(x=np.random.random((2048, 2048)))
        self.data_collection.append(large_image)

        # The subset group can be made from any dataset
        subset_group = self.data_collection.new_subset_group(subset_state=self.image1.id['x'] > 1, label='A')

        self.viewer.add_data(large_image)

        # Since the dataset added has a subset, and each subset has its own
        # ModestImage, this increases the count.
        assert len(get_modest_images()) == 2

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

        self.data_collection.remove_subset_group(subset_group)

        # Removing the subset should bring the count back to 1 again
        assert len(get_modest_images()) == 1

    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)

        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

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

    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)

        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)

        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

    def test_save_aggregate_slice(self, tmpdir):

        # Regression test to make sure that image viewers that include
        # aggregate slice objects in the slices can be saved/restored

        self.viewer.add_data(self.hypercube)
        self.viewer.state.slices = AggregateSlice(slice(1, 3), 10, np.sum), 3, 0, 0

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

        self.application.save_session(filename)
        self.application.close()

        app2 = GlueApplication.restore_session(filename)
        viewer_state = app2.viewers[0][0].state
        slices = viewer_state.slices
        assert isinstance(slices[0], AggregateSlice)
        assert slices[0].slice == slice(1, 3)
        assert slices[0].center == 10
        assert slices[0].function is np.sum
        assert slices[1:] == (3, 0, 0)

        app2.close()

    def test_subset_cube_image(self):

        # Regression test to make sure that if an image and cube are present
        # in an image viewer and a subset is also present, we don't get an
        # error when trying to access the subset shape

        self.viewer.add_data(self.image1)
        self.data_collection.new_subset_group(label='subset',
                                              subset_state=self.image1.id['x'] > 1.5)

        self.viewer.add_data(self.hypercube)
        self.viewer.state.reference_data = self.hypercube

        assert self.viewer.layers[1].subset_array.shape == (4, 5)
        assert self.viewer.layers[3].subset_array.shape == (4, 5)

    def test_preserve_slice(self):

        # Regression test to make sure that when adding a second dataset to
        # an image viewer, the current slice in a cube does not change.

        self.viewer.add_data(self.hypercube)
        self.viewer.state.slices = (1, 2, 3, 4)
        self.viewer.add_data(self.image1)
        assert self.viewer.state.slices == (1, 2, 3, 4)

    def test_close(self):

        # Regression test for a bug that caused an error related to the toolbar
        # and _mpl_nav not being present when closing the viewer.

        self.viewer.toolbar.active_tool = self.viewer.toolbar.tools['mpl:zoom']
        self.viewer.close(warn=False)
Пример #59
0
class TestScatterViewer(object):

    def setup_method(self, method):

        self.data = 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_2d = Data(label='d2', a=[[1, 2], [3, 4]], b=[[5, 6], [7, 8]],
                            x=[[3, 5], [5.4, 1]], y=[[1.2, 4], [7, 8]])

        self.app = GlueApplication()
        self.session = self.app.session
        self.hub = self.session.hub

        self.data_collection = self.session.data_collection
        self.data_collection.append(self.data)
        self.data_collection.append(self.data_2d)

        self.viewer = self.app.new_data_viewer(ScatterViewer)

        self.data_collection.register_to_hub(self.hub)
        self.viewer.register_to_hub(self.hub)

    def teardown_method(self, method):
        self.viewer.close()

    def test_basic(self):

        viewer_state = self.viewer.state

        # Check defaults when we add data
        self.viewer.add_data(self.data)

        assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]:World 0'
        assert combo_as_string(self.viewer.options_widget().ui.combosel_y_att) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]:World 0'

        assert viewer_state.x_att is self.data.id['x']
        assert viewer_state.x_min == -1.1
        assert viewer_state.x_max == 3.4

        assert viewer_state.y_att is self.data.id['y']
        assert viewer_state.y_min == 3.2
        assert viewer_state.y_max == 3.5

        assert not viewer_state.x_log
        assert not viewer_state.y_log

        assert len(viewer_state.layers) == 1

        # Change to categorical component and check new values

        viewer_state.y_att = self.data.id['z']

        assert viewer_state.x_att is self.data.id['x']
        assert viewer_state.x_min == -1.1
        assert viewer_state.x_max == 3.4

        assert viewer_state.y_att is self.data.id['z']
        assert viewer_state.y_min == -0.5
        assert viewer_state.y_max == 2.5

        assert not viewer_state.x_log
        assert not viewer_state.y_log

    def test_flip(self):

        viewer_state = self.viewer.state

        self.viewer.add_data(self.data)

        assert viewer_state.x_min == -1.1
        assert viewer_state.x_max == 3.4

        self.viewer.options_widget().button_flip_x.click()

        assert viewer_state.x_min == 3.4
        assert viewer_state.x_max == -1.1

        assert viewer_state.y_min == 3.2
        assert viewer_state.y_max == 3.5

        self.viewer.options_widget().button_flip_y.click()

        assert viewer_state.y_min == 3.5
        assert viewer_state.y_max == 3.2

    def test_remove_data(self):
        self.viewer.add_data(self.data)
        assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]:World 0'
        assert combo_as_string(self.viewer.options_widget().ui.combosel_y_att) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]:World 0'
        self.data_collection.remove(self.data)
        assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == ''
        assert combo_as_string(self.viewer.options_widget().ui.combosel_y_att) == ''

    def test_update_component_updates_title(self):
        self.viewer.add_data(self.data)
        assert self.viewer.windowTitle() == '2D Scatter'
        self.viewer.state.x_att = self.data.id['y']
        assert self.viewer.windowTitle() == '2D Scatter'

    def test_combo_updates_with_component_add(self):
        self.viewer.add_data(self.data)
        self.data.add_component([3, 4, 1, 2], 'a')
        assert self.viewer.state.x_att is self.data.id['x']
        assert self.viewer.state.y_att is self.data.id['y']
        assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:x:y:z:a:Coordinate components:Pixel Axis 0 [x]:World 0'
        assert combo_as_string(self.viewer.options_widget().ui.combosel_y_att) == 'Main components:x:y:z:a:Coordinate components:Pixel Axis 0 [x]:World 0'

    def test_nonnumeric_first_component(self):
        # regression test for #208. Shouldn't complain if
        # first component is non-numerical
        data = core.Data()
        data.add_component(['a', 'b', 'c'], label='c1')
        data.add_component([1, 2, 3], label='c2')
        self.data_collection.append(data)
        self.viewer.add_data(data)

    def test_apply_roi(self):

        self.viewer.add_data(self.data)

        roi = RectangularROI(0, 3, 3.25, 3.45)

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

        self.viewer.apply_roi(roi)

        assert len(self.viewer.layers) == 2
        assert len(self.data.subsets) == 1

        assert_allclose(self.data.subsets[0].to_mask(), [0, 1, 0, 0])

        state = self.data.subsets[0].subset_state
        assert isinstance(state, RoiSubsetState)

    def test_apply_roi_categorical(self):

        viewer_state = self.viewer.state

        self.viewer.add_data(self.data)

        viewer_state.y_att = self.data.id['z']

        roi = RectangularROI(0, 3, -0.4, 0.3)

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

        self.viewer.apply_roi(roi)

        assert len(self.viewer.layers) == 2
        assert len(self.data.subsets) == 1

        assert_allclose(self.data.subsets[0].to_mask(), [0, 0, 0, 1])

        state = self.data.subsets[0].subset_state
        assert isinstance(state, AndState)

    def test_apply_roi_empty(self):
        # Make sure that doing an ROI selection on an empty viewer doesn't
        # produce error messsages
        roi = XRangeROI(-0.2, 0.1)
        self.viewer.apply_roi(roi)

    def test_axes_labels(self):

        viewer_state = self.viewer.state

        self.viewer.add_data(self.data)

        assert self.viewer.axes.get_xlabel() == 'x'
        assert self.viewer.axes.get_ylabel() == 'y'

        viewer_state.x_log = True

        assert self.viewer.axes.get_xlabel() == 'Log x'
        assert self.viewer.axes.get_ylabel() == 'y'

        viewer_state.x_att = self.data.id['y']

        assert self.viewer.axes.get_xlabel() == 'y'
        assert self.viewer.axes.get_ylabel() == 'y'

        viewer_state.y_log = True

        assert self.viewer.axes.get_xlabel() == 'y'
        assert self.viewer.axes.get_ylabel() == 'Log y'

    def test_component_replaced(self):

        # regression test for 508 - if a component ID is replaced, we should
        # make sure that the component ID is selected if the old component ID
        # was selected

        self.viewer.add_data(self.data)
        self.viewer.state.x_att = self.data.id['x']
        test = ComponentID('test')
        self.data.update_id(self.viewer.state.x_att, test)
        assert self.viewer.state.x_att is test
        assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:test:y:z:Coordinate components:Pixel Axis 0 [x]:World 0'

    def test_density_map(self):

        self.viewer.add_data(self.data)
        self.viewer.state.layers[0].points_mode = 'auto'
        assert len(self.viewer.layers[0].density_artist._x) == 0
        self.viewer.state.layers[0].points_mode = 'density'
        assert len(self.viewer.layers[0].density_artist._x) == 4
        self.viewer.state.layers[0].points_mode = 'markers'
        assert len(self.viewer.layers[0].density_artist._x) == 0

    @pytest.mark.parametrize('protocol', [0, 1])
    def test_session_back_compat(self, protocol):

        filename = os.path.join(DATA, 'scatter_v{0}.glu'.format(protocol))

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

        state = GlueUnSerializer.loads(session)

        ga = state.object('__main__')

        dc = ga.session.data_collection

        assert len(dc) == 1

        assert dc[0].label == 'basic'

        viewer1 = ga.viewers[0][0]
        assert len(viewer1.state.layers) == 3
        assert viewer1.state.x_att is dc[0].id['a']
        assert viewer1.state.y_att is dc[0].id['b']
        assert_allclose(viewer1.state.x_min, -1.04)
        assert_allclose(viewer1.state.x_max, 1.04)
        assert_allclose(viewer1.state.y_min, 1.98)
        assert_allclose(viewer1.state.y_max, 3.02)
        assert not viewer1.state.x_log
        assert not viewer1.state.y_log
        assert viewer1.state.layers[0].visible
        assert viewer1.state.layers[1].visible
        assert viewer1.state.layers[2].visible

        viewer2 = ga.viewers[0][1]
        assert len(viewer2.state.layers) == 3
        assert viewer2.state.x_att is dc[0].id['a']
        assert viewer2.state.y_att is dc[0].id['c']
        assert_allclose(viewer2.state.x_min, 9.5e-6)
        assert_allclose(viewer2.state.x_max, 1.05)
        assert_allclose(viewer2.state.y_min, 0.38)
        assert_allclose(viewer2.state.y_max, 5.25)
        assert viewer2.state.x_log
        assert viewer2.state.y_log
        assert viewer2.state.layers[0].visible
        assert not viewer2.state.layers[1].visible
        assert viewer2.state.layers[2].visible

        viewer3 = ga.viewers[0][2]
        assert len(viewer3.state.layers) == 3
        assert viewer3.state.x_att is dc[0].id['b']
        assert viewer3.state.y_att is dc[0].id['a']
        assert_allclose(viewer3.state.x_min, 0)
        assert_allclose(viewer3.state.x_max, 5)
        assert_allclose(viewer3.state.y_min, -5)
        assert_allclose(viewer3.state.y_max, 5)
        assert not viewer3.state.x_log
        assert not viewer3.state.y_log
        assert viewer3.state.layers[0].visible
        assert viewer3.state.layers[1].visible
        assert not viewer3.state.layers[2].visible


    def test_session_line_back_compat(self):

        # Backward-compatibility for v0.11 files in which the line and scatter
        # plots were defined as separate styles.

        filename = os.path.join(DATA, 'scatter_and_line_v1.glu')

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

        state = GlueUnSerializer.loads(session)

        ga = state.object('__main__')

        dc = ga.session.data_collection

        assert len(dc) == 1

        assert dc[0].label == 'table'

        viewer1 = ga.viewers[0][0]
        assert len(viewer1.state.layers) == 1
        assert viewer1.state.x_att is dc[0].id['a']
        assert viewer1.state.y_att is dc[0].id['b']
        assert viewer1.state.layers[0].markers_visible
        assert not viewer1.state.layers[0].line_visible

        viewer1 = ga.viewers[0][1]
        assert len(viewer1.state.layers) == 1
        assert viewer1.state.x_att is dc[0].id['a']
        assert viewer1.state.y_att is dc[0].id['b']
        assert not viewer1.state.layers[0].markers_visible
        assert viewer1.state.layers[0].line_visible

    def test_save_svg(self, tmpdir):
        # Regression test for a bug in AxesCache that caused SVG saving to
        # fail (because renderer.buffer_rgba did not exist)
        self.viewer.add_data(self.data)
        filename = tmpdir.join('test.svg').strpath
        self.viewer.axes.figure.savefig(filename)

    def test_2d(self):

        viewer_state = self.viewer.state

        self.viewer.add_data(self.data_2d)

        assert viewer_state.x_att is self.data_2d.id['a']
        assert viewer_state.x_min == 1
        assert viewer_state.x_max == 4

        assert viewer_state.y_att is self.data_2d.id['b']
        assert viewer_state.y_min == 5
        assert viewer_state.y_max == 8

        assert self.viewer.layers[0].plot_artist.get_xdata().shape == (4,)

    def test_apply_roi_single(self):

        # Regression test for a bug that caused mode.update to be called
        # multiple times and resulted in all other viewers receiving many
        # messages regarding subset updates (this occurred when multiple)
        # datasets were present.

        layer_tree = LayerTreeWidget()
        layer_tree.set_checkable(False)
        layer_tree.setup(self.data_collection)
        layer_tree.bind_selection_to_edit_subset()

        class Client(HubListener):

            def __init__(self, *args, **kwargs):
                super(Client, self).__init__(*args, **kwargs)
                self.count = Counter()

            def ping(self, message):
                self.count[message.sender] += 1

            def register_to_hub(self, hub):
                hub.subscribe(self, SubsetUpdateMessage, handler=self.ping)

        d1 = Data(a=[1, 2, 3], label='d3')
        d2 = Data(b=[1, 2, 3], label='d4')
        d3 = Data(c=[1, 2, 3], label='d5')
        d4 = Data(d=[1, 2, 3], label='d6')

        self.data_collection.append(d1)
        self.data_collection.append(d2)
        self.data_collection.append(d3)
        self.data_collection.append(d4)

        client = Client()
        client.register_to_hub(self.hub)

        self.viewer.add_data(d1)
        self.viewer.add_data(d3)

        roi = XRangeROI(2.5, 3.5)
        self.viewer.apply_roi(roi)

        for subset in client.count:
            assert client.count[subset] == 1

    @pytest.mark.parametrize('ndim', [1, 2])
    def test_all_options(self, ndim):

        # This test makes sure that all the code for the different scatter modes
        # gets run, though does not check the result.

        viewer_state = self.viewer.state

        if ndim == 1:
            data = self.data
        elif ndim == 2:
            data = self.data_2d

        self.viewer.add_data(data)

        layer_state = viewer_state.layers[0]

        layer_state.style = 'Scatter'

        layer_state.size_mode = 'Linear'
        layer_state.size_att = data.id['y']
        layer_state.size_vmin = 1.2
        layer_state.size_vmax = 4.
        layer_state.size_scaling = 2

        layer_state.cmap_mode = 'Linear'
        layer_state.cmap_att = data.id['x']
        layer_state.cmap_vmin = -1
        layer_state.cmap_vmax = 2.
        layer_state.cmap = colormaps.members[3][1]

        # Check inverting works
        layer_state.cmap_vmin = 3.

        layer_state.size_mode = 'Fixed'

        layer_state.xerr_visible = True
        layer_state.xerr_att = data.id['x']
        layer_state.yerr_visible = True
        layer_state.yerr_att = data.id['y']

        layer_state.style = 'Line'
        layer_state.linewidth = 3
        layer_state.linestyle = 'dashed'

    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']

        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']

    def test_enable_disable_components_combo(self):

        # Regression test for a bug that caused an error when turning off pixel
        # components from combo boxes.

        self.viewer.add_data(self.data)

        self.data['a'] = self.data.id['x'] + 5

        self.viewer.state.x_att_helper.pixel_coord = True

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

        self.viewer.state.x_att_helper.pixel_coord = False

    def test_component_renamed(self):
        # If a component ID is renamed, this needs to be reflected in the combo
        self.viewer.add_data(self.data)
        self.data.id['x'].label = 'test'
        assert combo_as_string(self.viewer.options_widget().ui.combosel_x_att) == 'Main components:test:y:z:Coordinate components:Pixel Axis 0 [x]:World 0'