Repository: biolab/orange3

Issue:
Select Rows - do not change filter after column change if it is not required
When performing filtering with Select Rows the filters are reset after changing the column on which the filtering is performed. This is annoying when one has multiple similar columns that require the same filters. Could there be an option not to reset the filter columns, except if necessary due to a variable type change?
Additionally, even if a filter is changed from colA to colB and back to colA the previous settings used for colA are not retained.

Version:
Orange 3.26.


Discussion:
> When performing filtering with Select Rows the filters are reset after changing the column on which the filtering is performed.

Indeed, it requires all variables. It suppose it would be possible to modify the context handler by relaxing the match method and adding `filter_values` to remove non-matching conditions.

> Additionally, even if a filter is changed from colA to colB and back to colA the previous settings used for colA are not retained.

This happens on your request. :) Some time ago we modified the behaviour of contexts to match the most recent context. This is not fixable (= not compatible with the current strategy), but may no longer matter after/if we fix the first point.
Yes, the second comment was just because I remembered that we previously discussed that Orange remembers contexts and was not sure if my main request would be irrelevant because of it. Maybe Orange could have options in widgets to set how to remember things? (If that would not be too cluttering.) The best remembering option is highly context dependent. 
Actually, #4740 might fulfill all your wishes (regarding the Select Rows widget, that is). Try it.

I wouldn't let the user select the way contexts are matched because few people would understand what it does.
The #4740  did not change the behaviour.
Hm, it worked for me. Have you tried with clear settings?
Yes, but still the old behaviour. I tried on my workflow and on Iris - I first set the filter to _is outside_ and then changing the numerical column which set the filter back to _equals_. 
I can confirm. The dropdown changes to default.
What am I missing? What am I doing differently?

![select-rows](https://user-images.githubusercontent.com/2387315/81225664-e0ce7480-8fe9-11ea-91b9-ce321b873a38.gif)

That is not what I meant. The problem arises if you consider only the topmost filter: 
First you have: _sepal length, is outside, [2,4]_.
Then you change this first filter to sepal width (leaving other two parameters intact). Expected: _sepal width, is outside, [2,4]_. Actual: _sepal width, equals, 2_. 
So the second and third column change, although only the first column was changed manually. Since both original and new variable are of the same type the change of the second and third column is not necessary. I would expect the other two columns to remain unchanged (or be set to whatever was previously used for the new variable (sepal width), if this is the Orange way of doing things). Although in my use case I would strongly prefer if second and third column remain as they are. Namely, I had problems in past when things changed by themselves and I did not notice it. 

Also, out of topic: it looks a bit odd that the field heights are different between the filter columns on your video/operating system. But I like the spacing between them (there is no spacing on the Linux i am using).

Resolving patch:
diff --git a/Orange/widgets/data/owselectrows.py b/Orange/widgets/data/owselectrows.py
--- a/Orange/widgets/data/owselectrows.py
+++ b/Orange/widgets/data/owselectrows.py
@@ -339,6 +339,8 @@ def remove_all_rows(self):
 
     def set_new_operators(self, attr_combo, adding_all,
                           selected_index=None, selected_values=None):
+        old_combo = self.cond_list.cellWidget(attr_combo.row, 1)
+        prev_text = old_combo.currentText() if old_combo else ""
         oper_combo = QComboBox()
         oper_combo.row = attr_combo.row
         oper_combo.attr_combo = attr_combo
@@ -348,7 +350,11 @@ def set_new_operators(self, attr_combo, adding_all,
         else:
             var = self.data.domain[attr_name]
             oper_combo.addItems(self.operator_names[type(var)])
-        oper_combo.setCurrentIndex(selected_index or 0)
+        if selected_index is None:
+            selected_index = oper_combo.findText(prev_text)
+            if selected_index == -1:
+                selected_index = 0
+        oper_combo.setCurrentIndex(selected_index)
         self.cond_list.setCellWidget(oper_combo.row, 1, oper_combo)
         self.set_new_values(oper_combo, adding_all, selected_values)
         oper_combo.currentIndexChanged.connect(
@@ -789,4 +795,4 @@ def resizeEvent(self, QResizeEvent):
 
 
 if __name__ == "__main__":  # pragma: no cover
-    WidgetPreview(OWSelectRows).run(Table("zoo"))
+    WidgetPreview(OWSelectRows).run(Table("heart_disease"))


Test patch:
diff --git a/Orange/widgets/data/tests/test_owselectrows.py b/Orange/widgets/data/tests/test_owselectrows.py
--- a/Orange/widgets/data/tests/test_owselectrows.py
+++ b/Orange/widgets/data/tests/test_owselectrows.py
@@ -315,6 +315,26 @@ def test_change_var_type(self):
         new_iris = iris.transform(new_domain)
         self.send_signal(self.widget.Inputs.data, new_iris)
 
+    def test_keep_operator(self):
+        data = Table("heart_disease")
+        domain = data.domain
+
+        self.send_signal(self.widget.Inputs.data, data)
+
+        self.widget.remove_all_button.click()
+        self.enterFilter(domain["age"], "is not", "42")
+        simulate.combobox_activate_item(
+            self.widget.cond_list.cellWidget(0, 0), "chest pain", delay=0)
+        self.assertEqual(
+            self.widget.cond_list.cellWidget(0, 1).currentText(), "is not")
+
+        self.widget.remove_all_button.click()
+        self.enterFilter(domain["age"], "is below", "42")
+        simulate.combobox_activate_item(
+            self.widget.cond_list.cellWidget(0, 0), "chest pain", delay=0)
+        self.assertEqual(
+            self.widget.cond_list.cellWidget(0, 1).currentText(), "is")
+
     # Uncomment this on 2022/2/2
     #
     # def test_migration_to_version_1(self):