Skip to content

Commit c150327

Browse files
authored
Merge pull request #2087 from jerneju/gh-2054-selectrows-isdefined
[FIX] Select Rows: "is defined" fails
2 parents 352f051 + 7fa103f commit c150327

File tree

2 files changed

+50
-21
lines changed

2 files changed

+50
-21
lines changed

Orange/widgets/data/owselectrows.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55

66
from AnyQt.QtWidgets import (
77
QWidget, QTableWidget, QHeaderView, QComboBox, QLineEdit, QToolButton,
8-
QMessageBox, QMenu, QListView, QGridLayout, QPushButton, QSizePolicy
9-
)
8+
QMessageBox, QMenu, QListView, QGridLayout, QPushButton, QSizePolicy,
9+
QLabel)
1010
from AnyQt.QtGui import (
1111
QDoubleValidator, QRegExpValidator, QStandardItemModel, QStandardItem,
1212
QFontMetrics, QPalette
@@ -24,6 +24,7 @@
2424
from Orange.widgets.settings import Setting, ContextSetting, DomainContextHandler
2525
from Orange.widgets.utils import vartype
2626
from Orange.canvas import report
27+
from Orange.widgets.widget import Msg
2728

2829

2930
class SelectRowsContextHandler(DomainContextHandler):
@@ -39,7 +40,7 @@ def encode_setting(self, context, setting, value):
3940
CONTINUOUS = vartype(ContinuousVariable())
4041
for i, (attr, op, values) in enumerate(value):
4142
if context.attributes.get(attr) == CONTINUOUS:
42-
if isinstance(values[0], str):
43+
if values and isinstance(values[0], str):
4344
values = [QLocale().toDouble(v)[0] for v in values]
4445
value[i] = (attr, op, values)
4546
return super().encode_setting(context, setting, value)
@@ -120,6 +121,9 @@ class OWSelectRows(widget.OWWidget):
120121
operator_names = {vtype: [name for _, name in filters]
121122
for vtype, filters in Operators.items()}
122123

124+
class Error(widget.OWWidget.Error):
125+
parsing_error = Msg("{}")
126+
123127
def __init__(self):
124128
super().__init__()
125129

@@ -289,7 +293,7 @@ def _get_value_contents(box):
289293
names.append(item.text())
290294
child.desc_text = ', '.join(names)
291295
child.set_text()
292-
elif child is None:
296+
elif isinstance(child, QLabel) or child is None:
293297
pass
294298
else:
295299
raise TypeError('Type %s not supported.' % type(child))
@@ -340,8 +344,10 @@ def add_datetime(contents):
340344
lc = self._get_lineedit_contents(box) + lc
341345
oper = oper_combo.currentIndex()
342346

343-
if oper == oper_combo.count() - 1:
344-
self.cond_list.removeCellWidget(oper_combo.row, 2)
347+
if oper_combo.currentText() == "is defined":
348+
label = QLabel()
349+
label.var_type = vartype(var)
350+
self.cond_list.setCellWidget(oper_combo.row, 2, label)
345351
elif var.is_discrete:
346352
if oper_combo.currentText() == "is one of":
347353
if selected_values:
@@ -430,6 +436,8 @@ def conditions_changed(self):
430436
pass
431437

432438
def _values_to_floats(self, attr, values):
439+
if not len(values):
440+
return values
433441
if not all(values):
434442
return None
435443
if isinstance(attr, TimeVariable):
@@ -450,7 +458,7 @@ def _values_to_floats(self, attr, values):
450458
def commit(self):
451459
matching_output = self.data
452460
non_matching_output = None
453-
self.error()
461+
self.Error.clear()
454462
if self.data:
455463
domain = self.data.domain
456464
conditions = []
@@ -463,7 +471,7 @@ def commit(self):
463471
try:
464472
floats = self._values_to_floats(attr, values)
465473
except ValueError as e:
466-
self.error(e.args[0])
474+
self.Error.parsing_error(e.args[0])
467475
return
468476
if floats is None:
469477
continue
@@ -508,7 +516,7 @@ def commit(self):
508516
attr_flags = sum([Remove.RemoveConstant * purge_attrs,
509517
Remove.RemoveUnusedValues * purge_attrs])
510518
class_flags = sum([Remove.RemoveConstant * purge_classes,
511-
Remove.RemoveUnusedValues * purge_classes])
519+
Remove.RemoveUnusedValues * purge_classes])
512520
# same settings used for attributes and meta features
513521
remover = Remove(attr_flags, class_flags, attr_flags)
514522

@@ -532,7 +540,9 @@ def sp(s, capitalize=True):
532540
else:
533541
lab1.setText(label + "~%s row%s, %s variable%s" %
534542
(sp(data.approx_len()) +
535-
sp(len(data.domain.variables) + len(data.domain.metas))))
543+
sp(len(data.domain.variables) +
544+
len(data.domain.metas)))
545+
)
536546

537547
def send_report(self):
538548
if not self.data:

Orange/widgets/data/tests/test_owselectrows.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
Table, ContinuousVariable, StringVariable, DiscreteVariable)
99
from Orange.widgets.data.owselectrows import (
1010
OWSelectRows, FilterDiscreteType, SelectRowsContextHandler)
11-
from Orange.widgets.tests.base import WidgetTest
11+
from Orange.widgets.tests.base import WidgetTest, datasets
1212

1313
from Orange.data.filter import FilterContinuous, FilterString
1414
from Orange.widgets.tests.utils import simulate, override_locale
@@ -115,7 +115,7 @@ def test_continuous_filter_with_sl_SI_locale(self):
115115
self.enterFilter(iris.domain[2], "is below", "5.2")
116116
self.assertEqual(self.widget.conditions[0][2], ("52",))
117117

118-
def enterFilter(self, variable, filter, value, value2=None):
118+
def enterFilter(self, variable, filter, value=None, value2=None):
119119
row = self.widget.cond_list.model().rowCount()
120120
self.widget.add_button.click()
121121

@@ -125,15 +125,21 @@ def enterFilter(self, variable, filter, value, value2=None):
125125
oper_combo = self.widget.cond_list.cellWidget(row, 1)
126126
simulate.combobox_activate_item(oper_combo, filter, delay=0)
127127

128-
value_inputs = self.widget.cond_list.cellWidget(row, 2).children()
129-
value_inputs = [w for w in value_inputs if isinstance(w, QLineEdit)]
130-
QTest.mouseClick(value_inputs[0], Qt.LeftButton)
131-
QTest.keyClicks(value_inputs[0], value, delay=0)
132-
QTest.keyClick(value_inputs[0], Qt.Key_Enter)
133-
if value2 is not None:
134-
QTest.mouseClick(value_inputs[1], Qt.LeftButton)
135-
QTest.keyClicks(value_inputs[1], value2, delay=0)
136-
QTest.keyClick(value_inputs[1], Qt.Key_Enter)
128+
value_inputs = self._get_value_line_edits(row)
129+
for i, value in enumerate([value, value2]):
130+
if value is None:
131+
continue
132+
QTest.mouseClick(value_inputs[i], Qt.LeftButton)
133+
QTest.keyClicks(value_inputs[i], value, delay=0)
134+
QTest.keyClick(value_inputs[i], Qt.Key_Enter)
135+
136+
def _get_value_line_edits(self, row):
137+
value_inputs = self.widget.cond_list.cellWidget(row, 2)
138+
if value_inputs:
139+
value_inputs = [w for w in value_inputs.children()
140+
if isinstance(w, QLineEdit)]
141+
return value_inputs
142+
137143

138144
@override_locale(QLocale.Slovenian)
139145
def test_stores_settings_in_invariant_locale(self):
@@ -214,6 +220,19 @@ def test_load_settings(self):
214220
oper_combo = w2.cond_list.cellWidget(1, 1)
215221
self.assertEqual(oper_combo.currentText(), "is at most")
216222

223+
def test_is_defined_on_continuous_variable(self):
224+
# gh-2054 regression
225+
226+
data = Table(datasets.path("testing_dataset_cls"))
227+
self.send_signal("Data", data)
228+
229+
self.enterFilter(data.domain["c2"], "is defined")
230+
self.assertFalse(self.widget.Error.parsing_error.is_shown())
231+
self.assertEqual(len(self.get_output("Matching Data")), 3)
232+
self.assertEqual(len(self.get_output("Unmatched Data")), 1)
233+
234+
# Test saving of settings
235+
self.widget.settingsHandler.pack_data(self.widget)
217236

218237
def widget_with_context(self, domain, conditions):
219238
ch = SelectRowsContextHandler()

0 commit comments

Comments
 (0)