def test_component_id_combo_helper_replaced(): # Make sure that when components are replaced, the equivalent combo index # remains selected and an event is broadcast so that any attached callback # properties can be sure to pull the latest text/userData. callback = MagicMock() state = ExampleState() state.add_callback('combo', callback) dc = DataCollection([]) helper = ComponentIDComboHelper(state, 'combo', dc) assert selection_choices(state, 'combo') == "" data1 = Data(x=[1, 2, 3], y=[2, 3, 4], label='data1') callback.reset_mock() dc.append(data1) helper.append_data(data1) callback.assert_called_once_with(0) callback.reset_mock() assert selection_choices(state, 'combo') == "x:y" new_id = ComponentID(label='new') data1.update_id(data1.id['x'], new_id) callback.assert_called_once_with(0) callback.reset_mock() assert selection_choices(state, 'combo') == "new:y"
def test_component_id_combo_helper_replaced(): # Make sure that when components are replaced, the equivalent combo index # remains selected and an event is broadcast so that any attached callback # properties can be sure to pull the latest text/userData. callback = MagicMock() combo = QtWidgets.QComboBox() combo.currentIndexChanged.connect(callback) dc = DataCollection([]) helper = ComponentIDComboHelper(combo, dc) assert combo_as_string(combo) == "" data1 = Data(x=[1, 2, 3], y=[2, 3, 4], label='data1') callback.reset_mock() dc.append(data1) helper.append_data(data1) callback.assert_called_once_with(0) callback.reset_mock() assert combo_as_string(combo) == "x:y" new_id = ComponentID(label='new') data1.update_id(data1.id['x'], new_id) callback.assert_called_once_with(0) callback.reset_mock() assert combo_as_string(combo) == "new:y"
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.session = simple_session() self.hub = self.session.hub self.data_collection = self.session.data_collection self.data_collection.append(self.data) self.viewer = HistogramViewer(self.session) 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.combodata_x_att) == 'x:y' 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_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.combodata_x_att) == 'x:y' self.data_collection.remove(self.data) assert combo_as_string(self.viewer.options_widget().ui.combodata_x_att) == '' def test_update_component_updates_title(self): self.viewer.add_data(self.data) self.viewer.windowTitle() == 'x' self.viewer.state.x_att = self.data.id['y'] self.viewer.windowTitle() == 'y' 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.combodata_x_att) == 'x:y:z' 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_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].mpl_hist, [0, 1, 2, 1]) assert_allclose(self.viewer.layers[0].mpl_bins, [-5, -2.5, 0, 2.5, 5]) cid = self.data.visible_components[0] self.data_collection.new_subset_group('subset 1', cid < 2) assert_allclose(self.viewer.layers[1].mpl_hist, [0, 1, 1, 0]) assert_allclose(self.viewer.layers[1].mpl_bins, [-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].mpl_hist, [0, 0.1, 0.2, 0.1]) assert_allclose(self.viewer.layers[0].mpl_bins, [-5, -2.5, 0, 2.5, 5]) assert_allclose(self.viewer.layers[1].mpl_hist, [0, 0.2, 0.2, 0]) assert_allclose(self.viewer.layers[1].mpl_bins, [-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].mpl_hist, [0, 0.25, 0.75, 1.0]) assert_allclose(self.viewer.layers[0].mpl_bins, [-5, -2.5, 0, 2.5, 5]) assert_allclose(self.viewer.layers[1].mpl_hist, [0, 0.5, 1.0, 1.0]) assert_allclose(self.viewer.layers[1].mpl_bins, [-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].mpl_hist, [0, 1, 3, 4]) assert_allclose(self.viewer.layers[0].mpl_bins, [-5, -2.5, 0, 2.5, 5]) assert_allclose(self.viewer.layers[1].mpl_hist, [0, 1, 2, 2]) assert_allclose(self.viewer.layers[1].mpl_bins, [-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].mpl_hist, [2, 1, 1]) assert_allclose(self.viewer.layers[0].mpl_bins, [-0.5, 0.5, 1.5, 2.5]) assert_allclose(self.viewer.layers[1].mpl_hist, [1, 0, 1]) assert_allclose(self.viewer.layers[1].mpl_bins, [-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].mpl_hist, [0.5, 0.25, 0.25]) assert_allclose(self.viewer.layers[0].mpl_bins, [-0.5, 0.5, 1.5, 2.5]) assert_allclose(self.viewer.layers[1].mpl_hist, [0.5, 0, 0.5]) assert_allclose(self.viewer.layers[1].mpl_bins, [-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].mpl_hist, [0.5, 0.75, 1]) assert_allclose(self.viewer.layers[0].mpl_bins, [-0.5, 0.5, 1.5, 2.5]) assert_allclose(self.viewer.layers[1].mpl_hist, [0.5, 0.5, 1]) assert_allclose(self.viewer.layers[1].mpl_bins, [-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].mpl_hist, [2, 3, 4]) assert_allclose(self.viewer.layers[0].mpl_bins, [-0.5, 0.5, 1.5, 2.5]) assert_allclose(self.viewer.layers[1].mpl_hist, [1, 1, 2]) assert_allclose(self.viewer.layers[1].mpl_bins, [-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].mpl_hist, [0, 1, 2, 1]) assert_allclose(self.viewer.layers[1].mpl_hist, [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].mpl_hist, [2, 1, 1]) assert_allclose(self.viewer.layers[1].mpl_hist, [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_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.visible_components[0] self.data_collection.new_subset_group('subset 1', cid < 1) cid = self.data.visible_components[0] self.data_collection.new_subset_group('subset 2', cid < 2) cid = self.data.visible_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 viewer_state.hist_x_max = +10 viewer_state.hist_n_bin = 5 assert_allclose(self.viewer.layers[0].mpl_hist, [0, 0, 3, 1, 0]) viewer_state.x_att = self.data.id['x'] viewer_state.hist_x_min = -10 viewer_state.hist_x_max = +10 viewer_state.hist_n_bin = 5 assert_allclose(self.viewer.layers[0].mpl_hist, [0, 0, 2, 2, 0]) viewer_state.x_att = self.data.id['y'] assert_allclose(self.viewer.layers[0].mpl_hist, [0, 0, 3, 1, 0]) viewer_state.x_att = self.data.id['x'] assert_allclose(self.viewer.layers[0].mpl_hist, [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.components[0] 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.combodata_x_att) == 'test:y' 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]) 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.037037037037037035) 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
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()
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) == 'x:y:z' assert combo_as_string( self.viewer.options_widget().ui.combosel_y_att) == 'x:y: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['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) == 'x:y:z' assert combo_as_string( self.viewer.options_widget().ui.combosel_y_att) == 'x:y:z' 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) == 'x:y:z:a' assert combo_as_string( self.viewer.options_widget().ui.combosel_y_att) == 'x:y:z:a' 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.components[0] 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) == 'test:y:z' @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_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']
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'
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.session = simple_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 = ScatterViewer(self.session) 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) == 'x:y:z' assert combo_as_string(self.viewer.options_widget().ui.combosel_y_att) == 'x:y: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['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) == 'x:y:z' assert combo_as_string(self.viewer.options_widget().ui.combosel_y_att) == 'x:y:z' 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) == 'x:y:z:a' assert combo_as_string(self.viewer.options_widget().ui.combosel_y_att) == 'x:y:z:a' 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_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.components[0] 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) == 'test:y:z' @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_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'
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]' 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 process_events() labels = [ x.get_text() for x in self.viewer.axes.xaxis.get_ticklabels() ] # Different Matplotlib versions return slightly different # labels, but the ones below should be present regardless # of Matplotlib version. expected_present = [ '$\\mathdefault{10^{-5}}$', '$\\mathdefault{10^{-3}}$', '$\\mathdefault{10^{-1}}$', '$\\mathdefault{10^{1}}$', '$\\mathdefault{10^{3}}$', '$\\mathdefault{10^{5}}$' ] for label in expected_present: assert label in labels 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]' 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]' 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]' 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 ga.close() 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 @pytest.mark.filterwarnings('ignore:elementwise') 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' ga.close() @requires_matplotlib_ge_22 def test_categorical_labels(self, tmpdir): # Fix a bug that caused labels on histograms of categorical variables # to not be restored correctly after saving and reloading session self.viewer.add_data(self.data) self.viewer.state.x_att = self.data.id['y'] self.viewer.figure.canvas.draw() assert [x.get_text() for x in self.viewer.axes.xaxis.get_ticklabels() ] == ['', 'a', 'b', 'c', ''] # Make sure that everything works fine after saving/reloading filename = tmpdir.join('test_categorical_labels.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] viewer.figure.canvas.draw() assert [x.get_text() for x in viewer.axes.xaxis.get_ticklabels() ] == ['', 'a', 'b', 'c', ''] ga.close()
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.session = simple_session() self.hub = self.session.hub self.data_collection = self.session.data_collection self.data_collection.append(self.data) self.viewer = HistogramViewer(self.session) 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: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_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_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].mpl_hist, [0, 1, 2, 1]) assert_allclose(self.viewer.layers[0].mpl_bins, [-5, -2.5, 0, 2.5, 5]) cid = self.data.visible_components[0] self.data_collection.new_subset_group('subset 1', cid < 2) assert_allclose(self.viewer.layers[1].mpl_hist, [0, 1, 1, 0]) assert_allclose(self.viewer.layers[1].mpl_bins, [-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].mpl_hist, [0, 0.1, 0.2, 0.1]) assert_allclose(self.viewer.layers[0].mpl_bins, [-5, -2.5, 0, 2.5, 5]) assert_allclose(self.viewer.layers[1].mpl_hist, [0, 0.2, 0.2, 0]) assert_allclose(self.viewer.layers[1].mpl_bins, [-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].mpl_hist, [0, 0.25, 0.75, 1.0]) assert_allclose(self.viewer.layers[0].mpl_bins, [-5, -2.5, 0, 2.5, 5]) assert_allclose(self.viewer.layers[1].mpl_hist, [0, 0.5, 1.0, 1.0]) assert_allclose(self.viewer.layers[1].mpl_bins, [-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].mpl_hist, [0, 1, 3, 4]) assert_allclose(self.viewer.layers[0].mpl_bins, [-5, -2.5, 0, 2.5, 5]) assert_allclose(self.viewer.layers[1].mpl_hist, [0, 1, 2, 2]) assert_allclose(self.viewer.layers[1].mpl_bins, [-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].mpl_hist, [2, 1, 1]) assert_allclose(self.viewer.layers[0].mpl_bins, [-0.5, 0.5, 1.5, 2.5]) assert_allclose(self.viewer.layers[1].mpl_hist, [1, 0, 1]) assert_allclose(self.viewer.layers[1].mpl_bins, [-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].mpl_hist, [0.5, 0.25, 0.25]) assert_allclose(self.viewer.layers[0].mpl_bins, [-0.5, 0.5, 1.5, 2.5]) assert_allclose(self.viewer.layers[1].mpl_hist, [0.5, 0, 0.5]) assert_allclose(self.viewer.layers[1].mpl_bins, [-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].mpl_hist, [0.5, 0.75, 1]) assert_allclose(self.viewer.layers[0].mpl_bins, [-0.5, 0.5, 1.5, 2.5]) assert_allclose(self.viewer.layers[1].mpl_hist, [0.5, 0.5, 1]) assert_allclose(self.viewer.layers[1].mpl_bins, [-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].mpl_hist, [2, 3, 4]) assert_allclose(self.viewer.layers[0].mpl_bins, [-0.5, 0.5, 1.5, 2.5]) assert_allclose(self.viewer.layers[1].mpl_hist, [1, 1, 2]) assert_allclose(self.viewer.layers[1].mpl_bins, [-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].mpl_hist, [0, 1, 2, 1]) assert_allclose(self.viewer.layers[1].mpl_hist, [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].mpl_hist, [2, 1, 1]) assert_allclose(self.viewer.layers[1].mpl_hist, [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.visible_components[0] self.data_collection.new_subset_group('subset 1', cid < 1) cid = self.data.visible_components[0] self.data_collection.new_subset_group('subset 2', cid < 2) cid = self.data.visible_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 viewer_state.hist_x_max = +10 viewer_state.hist_n_bin = 5 assert_allclose(self.viewer.layers[0].mpl_hist, [0, 0, 3, 1, 0]) viewer_state.x_att = self.data.id['x'] viewer_state.hist_x_min = -10 viewer_state.hist_x_max = +10 viewer_state.hist_n_bin = 5 assert_allclose(self.viewer.layers[0].mpl_hist, [0, 0, 2, 2, 0]) viewer_state.x_att = self.data.id['y'] assert_allclose(self.viewer.layers[0].mpl_hist, [0, 0, 3, 1, 0]) viewer_state.x_att = self.data.id['x'] assert_allclose(self.viewer.layers[0].mpl_hist, [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() 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
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() 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.225) assert_allclose(viewer_state.x_max, 3.4 + 0.225) assert viewer_state.y_att is self.data.id['y'] assert_allclose(viewer_state.y_min, 3.2 - 0.015) assert_allclose(viewer_state.y_max, 3.5 + 0.015) 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.225) assert_allclose(viewer_state.x_max, 3.4 + 0.225) assert viewer_state.y_att is self.data.id['z'] assert_allclose(viewer_state.y_min, -0.5 - 0.15) assert_allclose(viewer_state.y_max, 2.5 + 0.15) 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.225) assert_allclose(viewer_state.x_max, 3.4 + 0.225) self.viewer.options_widget().button_flip_x.click() assert_allclose(viewer_state.x_max, -1.1 - 0.225) assert_allclose(viewer_state.x_min, 3.4 + 0.225) assert_allclose(viewer_state.y_min, 3.2 - 0.015) assert_allclose(viewer_state.y_max, 3.5 + 0.015) self.viewer.options_widget().button_flip_y.click() assert_allclose(viewer_state.y_max, 3.2 - 0.015) assert_allclose(viewer_state.y_min, 3.5 + 0.015) 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_allclose(viewer_state.x_min, 1 - 0.15) assert_allclose(viewer_state.x_max, 4 + 0.15) assert viewer_state.y_att is self.data_2d.id['b'] assert_allclose(viewer_state.y_min, 5 - 0.15) assert_allclose(viewer_state.y_max, 8 + 0.15) 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'] 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_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() == (719248.0, 719578.0) assert self.viewer.axes.get_ylim() == (3.2 - 0.015, 3.5 + 0.015) # 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() == (719248.0, 719578.0) assert self.viewer.axes.get_ylim() == (719348.0, 719678.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-27' assert options.valuetext_x_max.text() == '1971-02-20' assert options.valuetext_y_min.text() == '3.185' assert options.valuetext_y_max.text() == '3.515' # Make sure that we can set the xmin/xmax to a string date assert_equal(self.viewer.state.x_min, np.datetime64('1970-03-27', 'D')) options.valuetext_x_min.setText('1970-04-14') options.valuetext_x_min.editingFinished.emit() assert self.viewer.axes.get_xlim() == (719266.0, 719578.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-20' assert options.valuetext_y_min.text() == '3.185' assert options.valuetext_y_max.text() == '3.515'
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]' assert combo_as_string( self.viewer.options_widget().ui.combosel_y_att ) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]' 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]' assert combo_as_string( self.viewer.options_widget().ui.combosel_y_att ) == 'Main components:x:y:z:Coordinate components:Pixel Axis 0 [x]' 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]' assert combo_as_string( self.viewer.options_widget().ui.combosel_y_att ) == 'Main components:x:y:z:a:Coordinate components:Pixel Axis 0 [x]' 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]' 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() def test_datetime64_disabled(self, capsys): # Make sure that datetime components aren't options for the vector and # error markers. data = Data(label='test') data.add_component(np.array([100, 200, 300, 400], dtype='M8[D]'), 't1') data.add_component(np.array([200, 300, 400, 500], dtype='M8[D]'), 't2') data.add_component(np.array([200., 300., 400., 500.]), 'x') data.add_component(np.array([200., 300., 400., 500.]), 'y') self.data_collection.append(data) self.viewer.add_data(data) self.viewer.state.x_att = data.id['x'] self.viewer.state.y_att = data.id['y'] self.viewer.state.layers[0].cmap_mode = 'Linear' self.viewer.state.layers[0].cmap_att = data.id['x'] self.viewer.state.layers[0].size_mode = 'Linear' self.viewer.state.layers[0].size_att = data.id['y'] self.viewer.state.layers[0].vector_visible = True self.viewer.state.layers[0].xerr_visible = True self.viewer.state.layers[0].yerr_visible = True process_events() self.viewer.state.x_att = data.id['t1'] self.viewer.state.y_att = data.id['t2'] process_events() # We use capsys here because the # error is otherwise only apparent in stderr. out, err = capsys.readouterr() assert out.strip() == "" assert err.strip() == "" def test_density_map_incompatible_subset(self, capsys): # Regression test for a bug that caused the scatter viewer to crash # if subset for density map was incompatible. data2 = Data(label='d1', x=[3.4, 2.3, -1.1, 0.3], y=[3.2, 3.3, 3.4, 3.5], z=['a', 'b', 'c', 'a']) self.data_collection.append(data2) self.viewer.add_data(self.data) self.viewer.add_data(data2) self.data_collection.new_subset_group('test', self.data.id['x'] > 1) for layer in self.viewer.state.layers: layer.density_map = True self.viewer.figure.canvas.draw() process_events() assert self.viewer.layers[0].enabled assert not self.viewer.layers[1].enabled assert self.viewer.layers[2].enabled assert not self.viewer.layers[3].enabled def test_density_map_line_error_vector(self, capsys): # Make sure that we don't allow/show lines/errors/vectors # if in density map mode. self.viewer.add_data(self.data) self.viewer.state.layers[0].line_visible = True self.viewer.state.layers[0].xerr_visible = True self.viewer.state.layers[0].yerr_visible = True self.viewer.state.layers[0].vector_visible = True # Setting density_map to True resets the visibility of # lines/errors/vectors. self.viewer.state.layers[0].density_map = True assert not self.viewer.state.layers[0].line_visible assert not self.viewer.state.layers[0].xerr_visible assert not self.viewer.state.layers[0].yerr_visible assert not self.viewer.state.layers[0].vector_visible def test_legend(self): viewer_state = self.viewer.state self.viewer.add_data(self.data) viewer_state.legend.visible = True handles, labels, handler_dict = self.viewer.get_handles_legend() assert len(handles) == 1 assert labels[0] == 'd1' self.data_collection.new_subset_group('test', self.data.id['x'] > 1) assert len(viewer_state.layers) == 2 handles, labels, handler_dict = self.viewer.get_handles_legend() assert len(handles) == 2 assert labels[1] == 'test' print(handles[1][0]) # assert handles[1][0].get_color() == viewer_state.layers[1].state.color # Add a non visible layer data2 = Data(label='d2', x=[3.4, 2.3, -1.1, 0.3], y=[3.2, 3.3, 3.4, 3.5]) self.data_collection.append(data2) self.viewer.add_data(data2) assert len(viewer_state.layers) == 4 # 'd2' is not enabled (no linked component) handles, labels, handler_dict = self.viewer.get_handles_legend() assert len(handles) == 2 def test_changing_plot_modes(self): viewer_state = self.viewer.state viewer_state.plot_mode = 'polar' assert 'polar' in str(type(self.viewer.axes)).lower() viewer_state.plot_mode = 'aitoff' assert 'aitoff' in str(type(self.viewer.axes)).lower() viewer_state.plot_mode = 'hammer' assert 'hammer' in str(type(self.viewer.axes)).lower() viewer_state.plot_mode = 'lambert' assert 'lambert' in str(type(self.viewer.axes)).lower() viewer_state.plot_mode = 'mollweide' assert 'mollweide' in str(type(self.viewer.axes)).lower() # For the time being, the polar plots don't support log scaling # def test_limit_log_set_polar(self): # self.viewer.add_data(self.data) # viewer_state = self.viewer.state # viewer_state.plot_mode = "polar" # axes = self.viewer.axes # # viewer_state.x_min = 0.5 # viewer_state.x_max = 1.5 # assert_allclose(axes.get_xlim(), [0.5, 1.5]) # # viewer_state.y_min = -2.5 # viewer_state.y_max = 2.5 # assert_allclose(axes.get_ylim(), [-2.5, 2.5]) # # viewer_state.y_log = True # assert axes.get_yscale() == 'log' def test_limit_set_fullsphere(self): # Make sure that the full-sphere projections ignore instead of throwing exceptions self.viewer.add_data(self.data) viewer_state = self.viewer.state for proj in fullsphere_projections: viewer_state.plot_mode = proj error_msg = 'Issue with {} projection'.format(proj) axes = self.viewer.axes viewer_state.x_min = 0.5 viewer_state.x_max = 1.5 viewer_state.y_min = -2.5 viewer_state.y_max = 2.5 assert_allclose(axes.get_xlim(), [-np.pi, np.pi], err_msg=error_msg) assert_allclose(axes.get_ylim(), [-np.pi / 2, np.pi / 2], err_msg=error_msg) def test_changing_mode_limits(self): self.viewer.add_data(self.data) viewer_state = self.viewer.state old_xmin = viewer_state.x_min old_xmax = viewer_state.x_max old_ymin = viewer_state.y_min old_ymax = viewer_state.y_max # Make sure limits are reset first viewer_state.x_max += 3 # Currently, when we change to polar mode, the x-limits are changed to 0, 2pi viewer_state.plot_mode = 'polar' assert_allclose(viewer_state.x_min, 0) assert_allclose(viewer_state.x_max, 2 * np.pi) assert_allclose(self.viewer.axes.get_xlim(), [0, 2 * np.pi]) assert_allclose(viewer_state.y_min, old_ymin) assert_allclose(viewer_state.y_max, old_ymax) assert_allclose(self.viewer.axes.get_ylim(), [old_ymin, old_ymax]) viewer_state.plot_mode = 'rectilinear' assert_allclose(viewer_state.x_min, old_xmin) assert_allclose(viewer_state.x_max, old_xmax) assert_allclose(self.viewer.axes.get_xlim(), [old_xmin, old_xmax]) assert_allclose(viewer_state.y_min, old_ymin) assert_allclose(viewer_state.y_max, old_ymax) assert_allclose(self.viewer.axes.get_ylim(), [old_ymin, old_ymax]) for proj in fullsphere_projections: viewer_state.plot_mode = 'rectilinear' viewer_state.plot_mode = proj error_msg = 'Issue with {} projection'.format(proj) assert_allclose(viewer_state.x_min, -np.pi) assert_allclose(viewer_state.x_max, np.pi) assert_allclose(self.viewer.axes.get_xlim(), [-np.pi, np.pi], err_msg=error_msg) assert_allclose(viewer_state.y_min, -np.pi / 2) assert_allclose(viewer_state.y_max, np.pi / 2) assert_allclose(self.viewer.axes.get_ylim(), [-np.pi / 2, np.pi / 2], err_msg=error_msg) def test_changing_mode_log(self): # Test to make sure we reset the log axes to false when changing modes to prevent problems self.viewer.add_data(self.data) viewer_state = self.viewer.state viewer_state.x_log = True viewer_state.y_log = True viewer_state.plot_mode = 'polar' assert not viewer_state.x_log assert not viewer_state.y_log assert self.viewer.axes.get_xscale() == 'linear' assert self.viewer.axes.get_yscale() == 'linear' viewer_state.y_log = True viewer_state.plot_mode = 'rectilinear' assert not viewer_state.x_log assert not viewer_state.y_log assert self.viewer.axes.get_xscale() == 'linear' assert self.viewer.axes.get_yscale() == 'linear' for proj in fullsphere_projections: viewer_state.plot_mode = 'rectilinear' viewer_state.x_log = True viewer_state.y_log = True viewer_state.plot_mode = proj error_msg = 'Issue with {} projection'.format(proj) assert not viewer_state.x_log, error_msg assert not viewer_state.y_log, error_msg assert self.viewer.axes.get_xscale() == 'linear', error_msg assert self.viewer.axes.get_yscale() == 'linear', error_msg def test_full_circle_utility(self): # Make sure that the full circle function behaves well self.viewer.add_data(self.data) viewer_state = self.viewer.state old_xmin = viewer_state.x_min old_xmax = viewer_state.x_max old_ymin = viewer_state.y_min old_ymax = viewer_state.y_max viewer_state.full_circle() assert_allclose([viewer_state.x_min, viewer_state.x_max], [old_xmin, old_xmax]) assert_allclose([viewer_state.y_min, viewer_state.y_max], [old_ymin, old_ymax]) viewer_state.plot_mode = 'polar' viewer_state.full_circle() assert_allclose([viewer_state.x_min, viewer_state.x_max], [0, 2 * np.pi]) assert_allclose([viewer_state.y_min, viewer_state.y_max], [old_ymin, old_ymax]) for proj in fullsphere_projections: error_msg = 'Issue with {} projection'.format(proj) viewer_state.plot_mode = proj viewer_state.full_circle() assert_allclose([viewer_state.x_min, viewer_state.x_max], [-np.pi, np.pi], err_msg=error_msg) assert_allclose([viewer_state.y_min, viewer_state.y_max], [-np.pi / 2, np.pi / 2], err_msg=error_msg) def test_limits_log_widget_polar_cartesian(self): ui = self.viewer.options_widget().ui viewer_state = self.viewer.state viewer_state.plot_mode = 'polar' assert not ui.bool_x_log.isEnabled() assert not ui.bool_x_log_.isEnabled() assert not ui.bool_y_log.isEnabled() assert not ui.bool_y_log_.isEnabled() assert ui.valuetext_x_min.isEnabled() assert ui.button_flip_x.isEnabled() assert ui.valuetext_x_max.isEnabled() assert ui.valuetext_y_min.isEnabled() assert ui.button_flip_y.isEnabled() assert ui.valuetext_y_max.isEnabled() assert ui.button_full_circle.isHidden() viewer_state.plot_mode = 'rectilinear' assert ui.bool_x_log.isEnabled() assert ui.bool_x_log_.isEnabled() assert ui.bool_y_log.isEnabled() assert ui.bool_y_log_.isEnabled() assert ui.valuetext_x_min.isEnabled() assert ui.button_flip_x.isEnabled() assert ui.valuetext_x_max.isEnabled() assert ui.valuetext_y_min.isEnabled() assert ui.button_flip_y.isEnabled() assert ui.valuetext_y_max.isEnabled() assert ui.button_full_circle.isHidden() assert ui.button_full_circle.isHidden() def test_limits_log_widget_fullsphere(self): ui = self.viewer.options_widget().ui viewer_state = self.viewer.state for proj in fullsphere_projections: error_msg = 'Issue with {} projection'.format(proj) viewer_state.plot_mode = proj not ui.bool_x_log.isEnabled() assert not ui.bool_x_log_.isEnabled(), error_msg assert not ui.bool_y_log.isEnabled(), error_msg assert not ui.bool_y_log_.isEnabled(), error_msg assert not ui.valuetext_x_min.isEnabled(), error_msg assert not ui.button_flip_x.isEnabled(), error_msg assert not ui.valuetext_x_max.isEnabled(), error_msg assert not ui.valuetext_y_min.isEnabled(), error_msg assert not ui.button_flip_y.isEnabled(), error_msg assert not ui.valuetext_y_max.isEnabled(), error_msg assert ui.button_full_circle.isHidden(), error_msg viewer_state.plot_mode = 'rectilinear' assert ui.bool_x_log.isEnabled() assert ui.bool_x_log_.isEnabled() assert ui.bool_y_log.isEnabled() assert ui.bool_y_log_.isEnabled() assert ui.valuetext_x_min.isEnabled() assert ui.button_flip_x.isEnabled() assert ui.valuetext_x_max.isEnabled() assert ui.valuetext_y_min.isEnabled() assert ui.button_flip_y.isEnabled() assert ui.valuetext_y_max.isEnabled() assert ui.button_full_circle.isHidden() def test_apply_roi_polar(self): self.viewer.add_data(self.data) viewer_state = self.viewer.state roi = RectangularROI(0, 0.5, 0, 0.5) viewer_state.plot_mode = 'polar' viewer_state.full_circle() 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(), [1, 0, 0, 0]) state = self.data.subsets[0].subset_state assert isinstance(state, RoiSubsetState) assert state.pretransform pretrans = state.pretransform assert isinstance(pretrans, ProjectionMplTransform) assert pretrans._state['projection'] == 'polar' assert_allclose(pretrans._state['x_lim'], [viewer_state.x_min, viewer_state.x_max]) assert_allclose(pretrans._state['y_lim'], [viewer_state.y_min, viewer_state.y_max]) assert pretrans._state['x_scale'] == 'linear' assert pretrans._state['y_scale'] == 'linear' self.data.subsets[0].delete() viewer_state.y_log = True self.viewer.apply_roi(roi) state = self.data.subsets[0].subset_state assert state.pretransform pretrans = state.pretransform assert isinstance(pretrans, ProjectionMplTransform) assert pretrans._state['y_scale'] == 'log' def test_apply_roi_fullsphere(self): self.viewer.add_data(self.data) viewer_state = self.viewer.state roi = RectangularROI(0, 0.5, 0, 0.5) for proj in fullsphere_projections: viewer_state.plot_mode = proj assert len(self.viewer.layers) == 1 self.viewer.apply_roi(roi) assert len(self.viewer.layers) == 2 assert len(self.data.subsets) == 1 subset = self.data.subsets[0] state = subset.subset_state assert isinstance(state, RoiSubsetState) assert state.pretransform pretrans = state.pretransform assert isinstance(pretrans, ProjectionMplTransform) assert pretrans._state['projection'] == proj assert_allclose(pretrans._state['x_lim'], [viewer_state.x_min, viewer_state.x_max]) assert_allclose(pretrans._state['y_lim'], [viewer_state.y_min, viewer_state.y_max]) assert pretrans._state['x_scale'] == 'linear' assert pretrans._state['y_scale'] == 'linear' subset.delete()
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'