Skip to content
2 changes: 1 addition & 1 deletion Orange/widgets/data/owtable.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ def update(_):
self.selected_cols = []

self.set_selection()
self.commit()
self.unconditional_commit()

def _setup_table_view(self, view, data):
"""Setup the `view` (QTableView) with `data` (Orange.data.Table)
Expand Down
14 changes: 13 additions & 1 deletion Orange/widgets/data/tests/test_owtable.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from unittest.mock import Mock
import unittest
from unittest.mock import Mock, patch

from Orange.widgets.data.owtable import OWDataTable
from Orange.widgets.tests.base import WidgetTest, WidgetOutputsTestMixin
Expand Down Expand Up @@ -57,3 +58,14 @@ def test_attrs_appear_in_corner_text(self):
# false positive, pylint: disable=unsubscriptable-object
self.assertEqual(
self.widget.set_corner_text.call_args[0][1], "\na\nb\nc")

def test_unconditional_commit_on_new_signal(self):
with patch.object(self.widget, 'unconditional_commit') as commit:
self.widget.auto_commit = False
commit.reset_mock()
self.send_signal(self.widget.Inputs.data, self.data)
commit.assert_called()


if __name__ == "__main__":
unittest.main()
2 changes: 1 addition & 1 deletion Orange/widgets/tests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -620,7 +620,7 @@ def test_plot_once(self, timeout=DEFAULT_TIMEOUT):
"""Test if data is plotted only once but committed on every input change"""
table = Table("heart_disease")
self.widget.setup_plot = Mock()
self.widget.commit = Mock()
self.widget.commit = self.widget.unconditional_commit = Mock()
self.send_signal(self.widget.Inputs.data, table)
self.widget.setup_plot.assert_called_once()
self.widget.commit.assert_called_once()
Expand Down
22 changes: 22 additions & 0 deletions Orange/widgets/unsupervised/owdistancemap.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,16 @@ def selections(self):
range(r.left(), r.right() + 1))
for r in selections]

def set_selections(self, ranges):
self.__clearSelections()
for y, x in ranges:
area = QRectF(x.start, y.start,
x.stop - x.start - 1, y.stop - y.start - 1)
item = DistanceMapItem.SelectionRect(area, self)
item.setPen(QPen(Qt.red, 0))
self.__selections.append((item, area))
self.selectionChanged.emit()

def hoverMoveEvent(self, event):
super().hoverMoveEvent(event)
i, j = self._cellAt(event.pos())
Expand Down Expand Up @@ -268,6 +278,7 @@ class Outputs:
color_high = settings.Setting(1.0)

annotation_idx = settings.ContextSetting(0)
pending_selection = settings.Setting(None, schema_only=True)

autocommit = settings.Setting(True)

Expand Down Expand Up @@ -393,6 +404,14 @@ def __init__(self):

self.grid_widget.scene().installEventFilter(self)

self.settingsAboutToBePacked.connect(self.pack_settings)

def pack_settings(self):
if self.matrix_item is not None:
self.pending_selection = self.matrix_item.selections()
else:
self.pending_selection = None

@Inputs.distances
def set_distances(self, matrix):
self.closeContext()
Expand Down Expand Up @@ -473,6 +492,9 @@ def handleNewSignals(self):
self._update_ordering()
self._setup_scene()
self._update_labels()
if self.pending_selection is not None:
self.matrix_item.set_selections(self.pending_selection)
self.pending_selection = None
self.unconditional_commit()

def _clear_plot(self):
Expand Down
19 changes: 19 additions & 0 deletions Orange/widgets/unsupervised/tests/test_owdistancemap.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Test methods with long descriptive names can omit docstrings
# pylint: disable=missing-docstring
import random
import unittest

from Orange.distance import Euclidean
from Orange.widgets.unsupervised.owdistancemap import OWDistanceMap
from Orange.widgets.tests.base import WidgetTest, WidgetOutputsTestMixin
Expand All @@ -24,3 +26,20 @@ def _select_data(self):
self.widget._selection = selected_indices
self.widget.commit()
return selected_indices

def test_saved_selection(self):
self.widget.settingsHandler.pack_data(self.widget)
# no assert here, just test is doesn't crash on empty

self.send_signal(self.signal_name, self.signal_data)
random.seed(42)
self.widget.matrix_item.set_selections([(range(5, 10), range(8, 15))])
settings = self.widget.settingsHandler.pack_data(self.widget)

w = self.create_widget(OWDistanceMap, stored_settings=settings)
self.send_signal(self.signal_name, self.signal_data)
self.assertEqual(len(self.get_output(w.Outputs.selected_data)), 10)


if __name__ == "__main__":
unittest.main()
2 changes: 1 addition & 1 deletion Orange/widgets/unsupervised/tests/test_owmds.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def test_plot_once(self, timeout=5000):
"""Test if data is plotted only once but committed on every input change"""
table = Table("heart_disease")
self.widget.setup_plot = Mock()
self.widget.commit = Mock()
self.widget.commit = self.widget.unconditional_commit = Mock()
self.send_signal(self.widget.Inputs.data, table)
self.widget.commit.reset_mock()
self.wait_until_stop_blocking()
Expand Down
2 changes: 1 addition & 1 deletion Orange/widgets/unsupervised/tests/test_owtsne.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ def _check_exaggeration(call, exaggeration):
def test_plot_once(self):
"""Test if data is plotted only once but committed on every input change"""
self.widget.setup_plot = Mock()
self.widget.commit = Mock()
self.widget.commit = self.widget.unconditional_commit = Mock()

self.send_signal(self.widget.Inputs.data, self.data)
# TODO: The base widget immediately calls `setup_plot` and `commit`
Expand Down
2 changes: 1 addition & 1 deletion Orange/widgets/utils/tests/test_concurrent_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def test_button_toggle(self):
def test_plot_once(self):
table = Table("heart_disease")
self.widget.setup_plot = Mock()
self.widget.commit = Mock()
self.widget.commit = self.widget.unconditional_commit = Mock()
self.send_signal(self.widget.Inputs.data, table)
self.widget.setup_plot.assert_called_once()
self.widget.commit.assert_called_once()
Expand Down
13 changes: 12 additions & 1 deletion Orange/widgets/visualize/owheatmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ class Outputs:

palette_index = settings.Setting(_default_palette_index)
column_label_pos = settings.Setting(PositionTop)
selected_rows = settings.Setting(None, schema_only=True)

auto_commit = settings.Setting(True)

Expand Down Expand Up @@ -454,6 +455,7 @@ class Warning(widget.OWWidget.Warning):

def __init__(self):
super().__init__()
self.__pending_selection = self.selected_rows

# set default settings
self.space_x = 10
Expand Down Expand Up @@ -719,7 +721,14 @@ def set_dataset(self, data=None):
self.openContext(self.data)
if self.annotation_index >= len(self.annotation_vars):
self.annotation_index = 0

self.update_heatmaps()
if data is not None and self.__pending_selection is not None:
self.selection_manager.select_rows(self.__pending_selection)
self.selected_rows = self.__pending_selection
self.__pending_selection = None

self.unconditional_commit()

def update_heatmaps(self):
if self.data is not None:
Expand All @@ -739,13 +748,13 @@ def update_heatmaps(self):
self.selected_rows = []
else:
self.clear()
self.commit()

def update_merge(self):
self.kmeans_model = None
self.merge_indices = None
if self.data is not None and self.merge_kmeans:
self.update_heatmaps()
self.commit()

def _make_parts(self, data, group_var=None, group_key=None):
"""
Expand Down Expand Up @@ -1393,9 +1402,11 @@ def update_color_schema(self):

def update_sorting_examples(self):
self.update_heatmaps()
self.commit()

def update_clustering_examples(self):
self.update_heatmaps()
self.commit()

def update_legend(self):
for item in self.heatmap_scene.items():
Expand Down
2 changes: 1 addition & 1 deletion Orange/widgets/visualize/owlineplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ def set_data(self, data):

self.openContext(data)
self.setup_plot()
self.commit()
self.unconditional_commit()

def check_data(self):
def error(err):
Expand Down
12 changes: 10 additions & 2 deletions Orange/widgets/visualize/owmosaic.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ class Outputs:
variable3 = ContextSetting(None)
variable4 = ContextSetting(None)
variable_color = ContextSetting(None)
selection = ContextSetting(set())
selection = Setting(set(), schema_only=True)

BAR_WIDTH = 5
SPACING = 4
Expand All @@ -317,6 +317,8 @@ def __init__(self):
self.discrete_data = None
self.subset_data = None
self.subset_indices = None
self.__pending_selection = self.selection
self.selection = set()

self.color_data = None

Expand Down Expand Up @@ -460,8 +462,14 @@ def handleNewSignals(self):
else:
indices = {e.id for e in transformed}
self.subset_indices = [ex.id in indices for ex in self.data]
if self.data is not None and self.__pending_selection is not None:
self.selection = self.__pending_selection
self.__pending_selection = None
else:
self.selection = set()
self.set_color_data()
self.reset_graph()
self.update_graph()
self.send_selection()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@VesnaT, copying from projection widget indeed helped (and you were right about not using pack_settings, I should have listened more carefully and be less self confident :)).

However, every widget has also its own tricks, so you can't just copy. In this case, it was important to call send_selection after update_graph.


def clear_selection(self):
self.selection = set()
Expand Down
16 changes: 16 additions & 0 deletions Orange/widgets/visualize/owsilhouetteplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ class Outputs:
add_scores = settings.Setting(False)
auto_commit = settings.Setting(True)

pending_selection = settings.Setting(None, schema_only=True)

Distances = [("Euclidean", Orange.distance.Euclidean),
("Manhattan", Orange.distance.Manhattan),
("Cosine", Orange.distance.Cosine)]
Expand Down Expand Up @@ -165,10 +167,18 @@ def __init__(self):
self.view.setAlignment(Qt.AlignTop | Qt.AlignLeft)
self.mainArea.layout().addWidget(self.view)

self.settingsAboutToBePacked.connect(self.pack_settings)

def sizeHint(self):
sh = self.controlArea.sizeHint()
return sh.expandedTo(QSize(600, 720))

def pack_settings(self):
if self.data and self._silplot is not None:
self.pending_selection = list(self._silplot.selection())
else:
self.pending_selection = None

@Inputs.data
@check_sql_input
def set_data(self, data):
Expand Down Expand Up @@ -209,6 +219,12 @@ def handleNewSignals(self):
if self.data is not None:
self._update()
self._replot()
if self.pending_selection is not None and self._silplot is not None:
# If selection contains indices that are too large, the data
# file must had been modified, so we ignore selection
if max(self.pending_selection) < len(self.data):
self._silplot.setSelection(np.array(self.pending_selection))
self.pending_selection = None

self.unconditional_commit()

Expand Down
6 changes: 5 additions & 1 deletion Orange/widgets/visualize/owvenndiagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class Outputs:
selection: list

# Selected disjoint subset indices
selection = settings.Setting([])
selection = settings.Setting([], schema_only=True)
#: Stored input set hints
#: {(index, inputname, attributes): (selectedattrname, itemsettitle)}
#: The 'selectedattrname' can be None
Expand Down Expand Up @@ -210,6 +210,10 @@ def handleNewSignals(self):
del self._queue[:]

self._createDiagram()
# If autocommit is enabled, _createDiagram already outputs data
# If not, call unconditional_commit from here
if not self.autocommit:
self.unconditional_commit()
if self.data:
self.infolabel.setText(f"{len(self.data)} datasets on input.\n")
else:
Expand Down
26 changes: 26 additions & 0 deletions Orange/widgets/visualize/tests/test_owheatmap.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Test methods with long descriptive names can omit docstrings
# pylint: disable=missing-docstring
import warnings
import unittest
from unittest.mock import patch

import numpy as np
from sklearn.exceptions import ConvergenceWarning
Expand Down Expand Up @@ -175,3 +177,27 @@ def test_cls_with_single_instance(self):
np.array([[1], [2], [3]]), np.array([[0], [0], [1]]))
self.send_signal(self.widget.Inputs.data, table)
self.widget.row_check.setChecked(True)

def test_unconditional_commit_on_new_signal(self):
with patch.object(self.widget, 'unconditional_commit') as commit:
self.widget.auto_commit = False
commit.reset_mock()
self.send_signal(self.widget.Inputs.data, self.titanic)
commit.assert_called()

def test_saved_selection(self):
iris = Table("iris")

self.send_signal(self.widget.Inputs.data, iris)
selected_indices = list(range(10, 31))
self.widget.selection_manager.select_rows(selected_indices)
self.widget.on_selection_finished()
settings = self.widget.settingsHandler.pack_data(self.widget)

w = self.create_widget(OWHeatMap, stored_settings=settings)
self.send_signal(w.Inputs.data, iris, widget=w)
self.assertEqual(len(self.get_output(w.Outputs.selected_data)), 21)


if __name__ == "__main__":
unittest.main()
7 changes: 7 additions & 0 deletions Orange/widgets/visualize/tests/test_owlineplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,13 @@ def test_send_report(self):
self.send_signal(self.widget.Inputs.data, None)
self.widget.report_button.click()

def test_unconditional_commit_on_new_signal(self):
with patch.object(self.widget, 'unconditional_commit') as commit:
self.widget.auto_commit = False
commit.reset_mock()
self.send_signal(self.widget.Inputs.data, self.titanic)
commit.assert_called()


class TestSegmentsIntersection(unittest.TestCase):
def test_ccw(self):
Expand Down
Loading