From 6009e0434f5a0258cff45d0ab0173cf6a6fcbcba Mon Sep 17 00:00:00 2001 From: Vesna Tanko Date: Mon, 3 Dec 2018 12:36:46 +0100 Subject: [PATCH 1/3] OWScatterPlotBase: Resize dots --- .../widgets/visualize/owscatterplotgraph.py | 38 +++++++++++++++++-- .../visualize/tests/test_owscatterplotbase.py | 16 +++++++- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/Orange/widgets/visualize/owscatterplotgraph.py b/Orange/widgets/visualize/owscatterplotgraph.py index 96783a0a95d..da19c4cd696 100644 --- a/Orange/widgets/visualize/owscatterplotgraph.py +++ b/Orange/widgets/visualize/owscatterplotgraph.py @@ -6,7 +6,7 @@ import numpy as np -from AnyQt.QtCore import Qt, QRectF, QSize +from AnyQt.QtCore import Qt, QRectF, QSize, QTimer from AnyQt.QtGui import ( QStaticText, QColor, QPen, QBrush, QPainterPath, QTransform, QPainter ) @@ -405,6 +405,8 @@ def __init__(self, scatter_widget, parent=None, view_box=ViewBox): self.plot_widget.scene().installEventFilter(self._tooltip_delegate) self.view_box.sigTransformChanged.connect(self.update_density) + self._timer = QTimer() + def _create_legend(self, anchor): legend = LegendItem() legend.setParentItem(self.plot_widget.getViewBox()) @@ -725,8 +727,38 @@ def update_sizes(self): size_imputer = getattr( self.master, "impute_sizes", self.default_impute_sizes) size_imputer(size_data) - self.scatterplot_item.setSize(size_data) - self.scatterplot_item_sel.setSize(size_data + SELECTION_WIDTH) + + if self._timer.isActive(): + self._timer.stop() + + current_size_data = self.scatterplot_item.data["size"] + diff = size_data - current_size_data + + class Timeout: + n_iter = 5 + + def __init__(self, diff_=diff, item=self.scatterplot_item, + item_sel=self.scatterplot_item_sel): + self._counter = 0 + self._step = diff_ / self.n_iter + self._scatter_item, self._scatter_item_sel = item, item_sel + + def __call__(self): + self._counter += 1 + size = current_size_data + self._step + if self.n_iter == self._counter: + tmr.stop() + size = size_data + self._scatter_item.setSize(size) + self._scatter_item_sel.setSize(size + SELECTION_WIDTH) + + if np.sum(current_size_data) / self.n_valid != -1 and np.sum(diff): + self._timer = tmr = QTimer(self.scatterplot_item, interval=50) + self._timer.timeout.connect(Timeout()) + self._timer.start() + else: + self.scatterplot_item.setSize(size_data) + self.scatterplot_item_sel.setSize(size_data + SELECTION_WIDTH) update_point_size = update_sizes # backward compatibility (needed?!) update_size = update_sizes diff --git a/Orange/widgets/visualize/tests/test_owscatterplotbase.py b/Orange/widgets/visualize/tests/test_owscatterplotbase.py index e966a4fc664..02899fc0712 100644 --- a/Orange/widgets/visualize/tests/test_owscatterplotbase.py +++ b/Orange/widgets/visualize/tests/test_owscatterplotbase.py @@ -8,7 +8,8 @@ from pyqtgraph import mkPen -from Orange.widgets.tests.base import GuiTest +from Orange.widgets.settings import SettingProvider +from Orange.widgets.tests.base import WidgetTest from Orange.widgets.utils.colorpalette import ColorPaletteGenerator, \ ContinuousPaletteGenerator, NAN_GREY from Orange.widgets.visualize.owscatterplotgraph import OWScatterPlotBase, \ @@ -16,6 +17,9 @@ from Orange.widgets.widget import OWWidget +DEFAULT_TIMEOUT = 5000 + + class MockWidget(OWWidget): name = "Mock" @@ -34,6 +38,9 @@ class MockWidget(OWWidget): combined_legend = Mock(return_value=False) selection_changed = Mock(return_value=None) + GRAPH_CLASS = OWScatterPlotBase + graph = SettingProvider(OWScatterPlotBase) + def get_palette(self): if self.is_continuous_color(): return ContinuousPaletteGenerator(Qt.white, Qt.black, False) @@ -41,7 +48,7 @@ def get_palette(self): return ColorPaletteGenerator(12) -class TestOWScatterPlotBase(GuiTest): +class TestOWScatterPlotBase(WidgetTest): def setUp(self): self.master = MockWidget() self.graph = OWScatterPlotBase(self.master) @@ -160,6 +167,7 @@ def test_sampling(self): master.get_label_data = lambda: \ np.array([str(x) for x in d], dtype=object) graph.reset_graph() + self.process_events(until=lambda: not self.graph._timer.isActive()) # Check proper sampling scatterplot_item = graph.scatterplot_item @@ -187,6 +195,7 @@ def test_sampling(self): # Check that sample is extended when sample size is changed graph.set_sample_size(4) + self.process_events(until=lambda: not self.graph._timer.isActive()) scatterplot_item = graph.scatterplot_item x, y = scatterplot_item.getData() data = scatterplot_item.data @@ -226,6 +235,7 @@ def test_sampling(self): # Enable sampling when data is already present and not sampled graph.set_sample_size(3) + self.process_events(until=lambda: not self.graph._timer.isActive()) scatterplot_item = graph.scatterplot_item x, y = scatterplot_item.getData() data = scatterplot_item.data @@ -261,6 +271,7 @@ def test_sampling(self): np.arange(100, 105, dtype=float)) d = self.xy[0] - 100 graph.reset_graph() + self.process_events(until=lambda: not self.graph._timer.isActive()) scatterplot_item = graph.scatterplot_item x, y = scatterplot_item.getData() self.assertEqual(len(x), 3) @@ -365,6 +376,7 @@ def test_size_with_nans(self): d[4] = np.nan graph.update_sizes() + self.process_events(until=lambda: not self.graph._timer.isActive()) sizes2 = scatterplot_item.data["size"] self.assertEqual(sizes[1] - sizes[0], sizes2[1] - sizes2[0]) From 0384bde188741036f31f5501eb11b9d4da13c3ed Mon Sep 17 00:00:00 2001 From: Vesna Tanko Date: Mon, 3 Dec 2018 14:51:48 +0100 Subject: [PATCH 2/3] OWScatterPlotBase: Ignore warnings --- Orange/widgets/visualize/owscatterplotgraph.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Orange/widgets/visualize/owscatterplotgraph.py b/Orange/widgets/visualize/owscatterplotgraph.py index da19c4cd696..7cc505f5d6c 100644 --- a/Orange/widgets/visualize/owscatterplotgraph.py +++ b/Orange/widgets/visualize/owscatterplotgraph.py @@ -706,8 +706,10 @@ def get_sizes(self): self.MinShapeSize + (5 + self.point_width) * 0.5) size_column = self._filter_visible(size_column) size_column = size_column.copy() - size_column -= np.nanmin(size_column) - mx = np.nanmax(size_column) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=RuntimeWarning) + size_column -= np.nanmin(size_column) + mx = np.nanmax(size_column) if mx > 0: size_column /= mx else: From 96919417b97021258f94972f3c4883bdc31df49d Mon Sep 17 00:00:00 2001 From: Vesna Tanko Date: Mon, 3 Dec 2018 15:21:56 +0100 Subject: [PATCH 3/3] Enable various animations --- .../widgets/visualize/owscatterplotgraph.py | 41 +++++++++++-------- .../visualize/tests/test_owscatterplotbase.py | 18 ++++---- Orange/widgets/visualize/utils/widget.py | 1 + 3 files changed, 35 insertions(+), 25 deletions(-) diff --git a/Orange/widgets/visualize/owscatterplotgraph.py b/Orange/widgets/visualize/owscatterplotgraph.py index 7cc505f5d6c..4497abadfa9 100644 --- a/Orange/widgets/visualize/owscatterplotgraph.py +++ b/Orange/widgets/visualize/owscatterplotgraph.py @@ -405,7 +405,7 @@ def __init__(self, scatter_widget, parent=None, view_box=ViewBox): self.plot_widget.scene().installEventFilter(self._tooltip_delegate) self.view_box.sigTransformChanged.connect(self.update_density) - self._timer = QTimer() + self.timer = None def _create_legend(self, anchor): legend = LegendItem() @@ -487,6 +487,9 @@ def clear(self): self.plot_widget.clear() self.density_img = None + if self.timer is not None and self.timer.isActive(): + self.timer.stop() + self.timer = None self.scatterplot_item = None self.scatterplot_item_sel = None self.labels = [] @@ -730,34 +733,38 @@ def update_sizes(self): self.master, "impute_sizes", self.default_impute_sizes) size_imputer(size_data) - if self._timer.isActive(): - self._timer.stop() + if self.timer is not None and self.timer.isActive(): + self.timer.stop() + self.timer = None - current_size_data = self.scatterplot_item.data["size"] + current_size_data = self.scatterplot_item.data["size"].copy() diff = size_data - current_size_data + widget = self class Timeout: - n_iter = 5 + # 0.5 - np.cos(np.arange(0.17, 1, 0.17) * np.pi) / 2 + factors = [0.07, 0.26, 0.52, 0.77, 0.95, 1] - def __init__(self, diff_=diff, item=self.scatterplot_item, - item_sel=self.scatterplot_item_sel): + def __init__(self): self._counter = 0 - self._step = diff_ / self.n_iter - self._scatter_item, self._scatter_item_sel = item, item_sel def __call__(self): + factor = self.factors[self._counter] self._counter += 1 - size = current_size_data + self._step - if self.n_iter == self._counter: - tmr.stop() + size = current_size_data + diff * factor + if len(self.factors) == self._counter: + widget.timer.stop() + widget.timer = None size = size_data - self._scatter_item.setSize(size) - self._scatter_item_sel.setSize(size + SELECTION_WIDTH) + widget.scatterplot_item.setSize(size) + widget.scatterplot_item_sel.setSize(size + SELECTION_WIDTH) if np.sum(current_size_data) / self.n_valid != -1 and np.sum(diff): - self._timer = tmr = QTimer(self.scatterplot_item, interval=50) - self._timer.timeout.connect(Timeout()) - self._timer.start() + # If encountered any strange behaviour when updating sizes, + # implement it with threads + self.timer = QTimer(self.scatterplot_item, interval=50) + self.timer.timeout.connect(Timeout()) + self.timer.start() else: self.scatterplot_item.setSize(size_data) self.scatterplot_item_sel.setSize(size_data + SELECTION_WIDTH) diff --git a/Orange/widgets/visualize/tests/test_owscatterplotbase.py b/Orange/widgets/visualize/tests/test_owscatterplotbase.py index 02899fc0712..dd6cd626b41 100644 --- a/Orange/widgets/visualize/tests/test_owscatterplotbase.py +++ b/Orange/widgets/visualize/tests/test_owscatterplotbase.py @@ -17,9 +17,6 @@ from Orange.widgets.widget import OWWidget -DEFAULT_TIMEOUT = 5000 - - class MockWidget(OWWidget): name = "Mock" @@ -167,7 +164,8 @@ def test_sampling(self): master.get_label_data = lambda: \ np.array([str(x) for x in d], dtype=object) graph.reset_graph() - self.process_events(until=lambda: not self.graph._timer.isActive()) + self.process_events(until=lambda: not ( + self.graph.timer is not None and self.graph.timer.isActive())) # Check proper sampling scatterplot_item = graph.scatterplot_item @@ -195,7 +193,8 @@ def test_sampling(self): # Check that sample is extended when sample size is changed graph.set_sample_size(4) - self.process_events(until=lambda: not self.graph._timer.isActive()) + self.process_events(until=lambda: not ( + self.graph.timer is not None and self.graph.timer.isActive())) scatterplot_item = graph.scatterplot_item x, y = scatterplot_item.getData() data = scatterplot_item.data @@ -235,7 +234,8 @@ def test_sampling(self): # Enable sampling when data is already present and not sampled graph.set_sample_size(3) - self.process_events(until=lambda: not self.graph._timer.isActive()) + self.process_events(until=lambda: not ( + self.graph.timer is not None and self.graph.timer.isActive())) scatterplot_item = graph.scatterplot_item x, y = scatterplot_item.getData() data = scatterplot_item.data @@ -271,7 +271,8 @@ def test_sampling(self): np.arange(100, 105, dtype=float)) d = self.xy[0] - 100 graph.reset_graph() - self.process_events(until=lambda: not self.graph._timer.isActive()) + self.process_events(until=lambda: not ( + self.graph.timer is not None and self.graph.timer.isActive())) scatterplot_item = graph.scatterplot_item x, y = scatterplot_item.getData() self.assertEqual(len(x), 3) @@ -376,7 +377,8 @@ def test_size_with_nans(self): d[4] = np.nan graph.update_sizes() - self.process_events(until=lambda: not self.graph._timer.isActive()) + self.process_events(until=lambda: not ( + self.graph.timer is not None and self.graph.timer.isActive())) sizes2 = scatterplot_item.data["size"] self.assertEqual(sizes[1] - sizes[0], sizes2[1] - sizes2[0]) diff --git a/Orange/widgets/visualize/utils/widget.py b/Orange/widgets/visualize/utils/widget.py index 6362cad4cea..d05c6a33225 100644 --- a/Orange/widgets/visualize/utils/widget.py +++ b/Orange/widgets/visualize/utils/widget.py @@ -583,6 +583,7 @@ def onDeleteWidget(self): super().onDeleteWidget() self.graph.plot_widget.getViewBox().deleteLater() self.graph.plot_widget.clear() + self.graph.clear() class OWAnchorProjectionWidget(OWDataProjectionWidget):