Skip to content

Commit 1602d56

Browse files
authored
Merge pull request #3436 from VesnaT/resize
[ENH] OWScatterPlotBase: Animate dot resize
2 parents cdfa54f + 9691941 commit 1602d56

File tree

3 files changed

+63
-7
lines changed

3 files changed

+63
-7
lines changed

Orange/widgets/visualize/owscatterplotgraph.py

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import numpy as np
88

9-
from AnyQt.QtCore import Qt, QRectF, QSize
9+
from AnyQt.QtCore import Qt, QRectF, QSize, QTimer
1010
from AnyQt.QtGui import (
1111
QStaticText, QColor, QPen, QBrush, QPainterPath, QTransform, QPainter
1212
)
@@ -405,6 +405,8 @@ def __init__(self, scatter_widget, parent=None, view_box=ViewBox):
405405
self.plot_widget.scene().installEventFilter(self._tooltip_delegate)
406406
self.view_box.sigTransformChanged.connect(self.update_density)
407407

408+
self.timer = None
409+
408410
def _create_legend(self, anchor):
409411
legend = LegendItem()
410412
legend.setParentItem(self.plot_widget.getViewBox())
@@ -485,6 +487,9 @@ def clear(self):
485487
self.plot_widget.clear()
486488

487489
self.density_img = None
490+
if self.timer is not None and self.timer.isActive():
491+
self.timer.stop()
492+
self.timer = None
488493
self.scatterplot_item = None
489494
self.scatterplot_item_sel = None
490495
self.labels = []
@@ -704,8 +709,10 @@ def get_sizes(self):
704709
self.MinShapeSize + (5 + self.point_width) * 0.5)
705710
size_column = self._filter_visible(size_column)
706711
size_column = size_column.copy()
707-
size_column -= np.nanmin(size_column)
708-
mx = np.nanmax(size_column)
712+
with warnings.catch_warnings():
713+
warnings.simplefilter("ignore", category=RuntimeWarning)
714+
size_column -= np.nanmin(size_column)
715+
mx = np.nanmax(size_column)
709716
if mx > 0:
710717
size_column /= mx
711718
else:
@@ -725,8 +732,42 @@ def update_sizes(self):
725732
size_imputer = getattr(
726733
self.master, "impute_sizes", self.default_impute_sizes)
727734
size_imputer(size_data)
728-
self.scatterplot_item.setSize(size_data)
729-
self.scatterplot_item_sel.setSize(size_data + SELECTION_WIDTH)
735+
736+
if self.timer is not None and self.timer.isActive():
737+
self.timer.stop()
738+
self.timer = None
739+
740+
current_size_data = self.scatterplot_item.data["size"].copy()
741+
diff = size_data - current_size_data
742+
widget = self
743+
744+
class Timeout:
745+
# 0.5 - np.cos(np.arange(0.17, 1, 0.17) * np.pi) / 2
746+
factors = [0.07, 0.26, 0.52, 0.77, 0.95, 1]
747+
748+
def __init__(self):
749+
self._counter = 0
750+
751+
def __call__(self):
752+
factor = self.factors[self._counter]
753+
self._counter += 1
754+
size = current_size_data + diff * factor
755+
if len(self.factors) == self._counter:
756+
widget.timer.stop()
757+
widget.timer = None
758+
size = size_data
759+
widget.scatterplot_item.setSize(size)
760+
widget.scatterplot_item_sel.setSize(size + SELECTION_WIDTH)
761+
762+
if np.sum(current_size_data) / self.n_valid != -1 and np.sum(diff):
763+
# If encountered any strange behaviour when updating sizes,
764+
# implement it with threads
765+
self.timer = QTimer(self.scatterplot_item, interval=50)
766+
self.timer.timeout.connect(Timeout())
767+
self.timer.start()
768+
else:
769+
self.scatterplot_item.setSize(size_data)
770+
self.scatterplot_item_sel.setSize(size_data + SELECTION_WIDTH)
730771

731772
update_point_size = update_sizes # backward compatibility (needed?!)
732773
update_size = update_sizes

Orange/widgets/visualize/tests/test_owscatterplotbase.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88

99
from pyqtgraph import mkPen
1010

11-
from Orange.widgets.tests.base import GuiTest
11+
from Orange.widgets.settings import SettingProvider
12+
from Orange.widgets.tests.base import WidgetTest
1213
from Orange.widgets.utils.colorpalette import ColorPaletteGenerator, \
1314
ContinuousPaletteGenerator, NAN_GREY
1415
from Orange.widgets.visualize.owscatterplotgraph import OWScatterPlotBase, \
@@ -34,14 +35,17 @@ class MockWidget(OWWidget):
3435
combined_legend = Mock(return_value=False)
3536
selection_changed = Mock(return_value=None)
3637

38+
GRAPH_CLASS = OWScatterPlotBase
39+
graph = SettingProvider(OWScatterPlotBase)
40+
3741
def get_palette(self):
3842
if self.is_continuous_color():
3943
return ContinuousPaletteGenerator(Qt.white, Qt.black, False)
4044
else:
4145
return ColorPaletteGenerator(12)
4246

4347

44-
class TestOWScatterPlotBase(GuiTest):
48+
class TestOWScatterPlotBase(WidgetTest):
4549
def setUp(self):
4650
self.master = MockWidget()
4751
self.graph = OWScatterPlotBase(self.master)
@@ -160,6 +164,8 @@ def test_sampling(self):
160164
master.get_label_data = lambda: \
161165
np.array([str(x) for x in d], dtype=object)
162166
graph.reset_graph()
167+
self.process_events(until=lambda: not (
168+
self.graph.timer is not None and self.graph.timer.isActive()))
163169

164170
# Check proper sampling
165171
scatterplot_item = graph.scatterplot_item
@@ -187,6 +193,8 @@ def test_sampling(self):
187193

188194
# Check that sample is extended when sample size is changed
189195
graph.set_sample_size(4)
196+
self.process_events(until=lambda: not (
197+
self.graph.timer is not None and self.graph.timer.isActive()))
190198
scatterplot_item = graph.scatterplot_item
191199
x, y = scatterplot_item.getData()
192200
data = scatterplot_item.data
@@ -226,6 +234,8 @@ def test_sampling(self):
226234

227235
# Enable sampling when data is already present and not sampled
228236
graph.set_sample_size(3)
237+
self.process_events(until=lambda: not (
238+
self.graph.timer is not None and self.graph.timer.isActive()))
229239
scatterplot_item = graph.scatterplot_item
230240
x, y = scatterplot_item.getData()
231241
data = scatterplot_item.data
@@ -261,6 +271,8 @@ def test_sampling(self):
261271
np.arange(100, 105, dtype=float))
262272
d = self.xy[0] - 100
263273
graph.reset_graph()
274+
self.process_events(until=lambda: not (
275+
self.graph.timer is not None and self.graph.timer.isActive()))
264276
scatterplot_item = graph.scatterplot_item
265277
x, y = scatterplot_item.getData()
266278
self.assertEqual(len(x), 3)
@@ -365,6 +377,8 @@ def test_size_with_nans(self):
365377

366378
d[4] = np.nan
367379
graph.update_sizes()
380+
self.process_events(until=lambda: not (
381+
self.graph.timer is not None and self.graph.timer.isActive()))
368382
sizes2 = scatterplot_item.data["size"]
369383

370384
self.assertEqual(sizes[1] - sizes[0], sizes2[1] - sizes2[0])

Orange/widgets/visualize/utils/widget.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,7 @@ def onDeleteWidget(self):
583583
super().onDeleteWidget()
584584
self.graph.plot_widget.getViewBox().deleteLater()
585585
self.graph.plot_widget.clear()
586+
self.graph.clear()
586587

587588

588589
class OWAnchorProjectionWidget(OWDataProjectionWidget):

0 commit comments

Comments
 (0)