Skip to content

Commit

Permalink
Merge pull request #2691 from ales-erjavec/fixes/schema-only-settings…
Browse files Browse the repository at this point in the history
…-clear

[FIX] settings: Do not clear schema_only settings on close_context
  • Loading branch information
astaric authored Oct 23, 2017
2 parents 63df7e1 + 8767e6e commit 445176c
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 27 deletions.
11 changes: 0 additions & 11 deletions Orange/widgets/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -787,17 +787,6 @@ def close_context(self, widget):
if widget.current_context is None:
return

# Clear schema-only settings when *closing* the context
# FIXME: Sadly, schema-only settings aren't cleared when non-contextual
# SettingsHandler is used. Example: widget has non-contextual
# SettingsHandler, user loads schema that includes widget, user
# passes new data to widget => widget's schema-only settings still
# have previous values because equivalent of below reset was never
# called (i.e. `close_context()`).
for name, setting in self.known_settings.items():
if setting.schema_only:
setattr(widget, name, setting.default)

self.settings_from_widget(widget)
widget.current_context = None

Expand Down
12 changes: 12 additions & 0 deletions Orange/widgets/tests/test_context_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class SimpleWidget:
settings_version = 1

setting = Setting(42)
schema_only_setting = Setting(None, schema_only=True)

context_setting = ContextSetting(42)

Expand Down Expand Up @@ -181,6 +182,17 @@ def test_write_defaults_stores_version(self):
for c in contexts:
self.assertEqual(c.values.get("__version__", 0xBAD), 1)

def test_close_context(self):
handler = ContextHandler()
handler.bind(SimpleWidget)
widget = SimpleWidget()
widget.storeSpecificSettings = Mock()
handler.initialize(widget)
widget.schema_only_setting = 0xD06F00D
widget.current_context = handler.new_context()
handler.close_context(widget)
self.assertEqual(widget.schema_only_setting, 0xD06F00D)


class TestSettingsPrinter(TestCase):
def test_formats_contexts(self):
Expand Down
11 changes: 8 additions & 3 deletions Orange/widgets/visualize/owlinearprojection.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,9 @@ def __init__(self):
self.__legend = None
self.__selection_item = None
self.__replot_requested = False
#: Remember the saved state to restore
self.__pending_selection_restore = self.selection_indices
self.selection_indices = None

box = gui.vBox(self.controlArea, "Axes")

Expand Down Expand Up @@ -425,6 +428,7 @@ def sizeHint(self):

def clear(self):
self.data = None
self.selection_indices = None
self._subset_mask = None
self._selection_mask = None
self.varmodel_selected[:] = []
Expand Down Expand Up @@ -515,8 +519,9 @@ def set_data(self, data):
if set(selected_keys).issubset(set(state.keys())):
pass

if self.selection_indices is not None:
self.select_indices(self.selection_indices)
if self.__pending_selection_restore is not None:
self.select_indices(self.__pending_selection_restore)
self.__pending_selection_restore = None

# update the defaults state (the encoded state must contain
# all variables in the input domain)
Expand Down Expand Up @@ -942,6 +947,7 @@ def select_indices(self, indices, modifiers=Qt.NoModifier):
self._selection_mask[indices] = True

self._on_color_change()
self.selection_indices = numpy.flatnonzero(self._selection_mask).tolist()
self.commit()

def commit(self):
Expand All @@ -952,7 +958,6 @@ def commit(self):
if len(indices) > 0:
subset = self.data[indices]

self.selection_indices = indices
self.Outputs.selected_data.send(subset)
self.Outputs.annotated_data.send(create_annotated_table(self.data, indices))

Expand Down
36 changes: 25 additions & 11 deletions Orange/widgets/visualize/owscatterplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ class Outputs:

attr_x = ContextSetting(None)
attr_y = ContextSetting(None)

#: Serialized selection state to be restored
selection_group = Setting(None, schema_only=True)

graph = SettingProvider(OWScatterPlotGraph)
Expand Down Expand Up @@ -142,6 +144,9 @@ def __init__(self):
self.attribute_selection_list = None # list of Orange.data.Variable
self.__timer = QTimer(self, interval=1200)
self.__timer.timeout.connect(self.add_data)
#: Remember the saved state to restore
self.__pending_selection_restore = self.selection_group
self.selection_group = None

common_options = dict(
labelWidth=50, orientation=Qt.Horizontal, sendSelectedValue=True,
Expand Down Expand Up @@ -332,14 +337,16 @@ def handleNewSignals(self):
self.update_graph()
self.cb_class_density.setEnabled(self.graph.can_draw_density())
self.cb_reg_line.setEnabled(self.graph.can_draw_regresssion_line())
self.apply_selection()
if self.data is not None and self.__pending_selection_restore is not None:
self.apply_selection(self.__pending_selection_restore)
self.__pending_selection_restore = None
self.unconditional_commit()

def apply_selection(self):
"""Apply selection saved in workflow."""
if self.data is not None and self.selection_group is not None:
def apply_selection(self, selection):
"""Apply `selection` to the current plot."""
if self.data is not None:
self.graph.selection = np.zeros(len(self.data), dtype=np.uint8)
self.selection_group = [x for x in self.selection_group if x[0] < len(self.data)]
self.selection_group = [x for x in selection if x[0] < len(self.data)]
selection_array = np.array(self.selection_group).T
self.graph.selection[selection_array[0]] = selection_array[1]
self.graph.update_colors(keep_colors=True)
Expand Down Expand Up @@ -389,6 +396,19 @@ def update_graph(self, reset_view=True, **_):
self.graph.update_data(self.attr_x, self.attr_y, reset_view)

def selection_changed(self):

# Store current selection in a setting that is stored in workflow
if isinstance(self.data, SqlTable):
selection = None
elif self.data is not None:
selection = self.graph.get_selection()
else:
selection = None
if selection is not None and len(selection):
self.selection_group = list(zip(selection, self.graph.selection[selection]))
else:
self.selection_group = None

self.commit()

def send_data(self):
Expand All @@ -410,12 +430,6 @@ def _get_annotated():
self.Outputs.annotated_data.send(_get_annotated())
self.Outputs.selected_data.send(_get_selected())

# Store current selection in a setting that is stored in workflow
if len(selection):
self.selection_group = list(zip(selection, graph.selection[selection]))
else:
self.selection_group = None

def send_features(self):
features = [attr for attr in [self.attr_x, self.attr_y] if attr]
self.Outputs.features.send(features or None)
Expand Down
10 changes: 8 additions & 2 deletions Orange/widgets/visualize/tests/test_owscatterplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,10 @@ def test_saving_selection(self):

def test_points_selection(self):
# Opening widget with saved selection should restore it
self.widget.selection_group = [(i, 1) for i in range(50)]
self.widget = self.create_widget(
OWScatterPlot, stored_settings={
"selection_group": [(i, 1) for i in range(50)]}
)
self.send_signal(self.widget.Inputs.data, self.data) # iris
selected_data = self.get_output(self.widget.Outputs.selected_data)
self.assertEqual(len(selected_data), 50)
Expand All @@ -269,7 +272,10 @@ def test_migrate_selection(self):
def test_invalid_points_selection(self):
# if selection contains rows that are not present in the current
# dataset, widget should select what can be selected.
self.widget.selection_group = [(i, 1) for i in range(50)]
self.widget = self.create_widget(
OWScatterPlot, stored_settings={
"selection_group": [(i, 1) for i in range(50)]}
)
self.send_signal(self.widget.Inputs.data, self.data[:10])
selected_data = self.get_output(self.widget.Outputs.selected_data)
self.assertEqual(len(selected_data), 10)
Expand Down

0 comments on commit 445176c

Please sign in to comment.