Skip to content

Commit a8ea66b

Browse files
authored
Merge pull request #3030 from ales-erjavec/select-attributes-selection-filter
[FIX] owselectcolumns: Fix performance on filtering with selection
2 parents fd479b0 + b1bf6cc commit a8ea66b

File tree

1 file changed

+39
-12
lines changed

1 file changed

+39
-12
lines changed

Orange/widgets/data/owselectcolumns.py

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import sys
22
from functools import partial
3+
from typing import Optional # pylint: disable=unused-import
34

45
from AnyQt.QtWidgets import QWidget, QGridLayout
5-
from AnyQt.QtCore import Qt, QSortFilterProxyModel, QItemSelection, QItemSelectionModel
6+
from AnyQt.QtWidgets import QListView # pylint: disable=unused-import
7+
from AnyQt.QtCore import (
8+
Qt, QTimer, QSortFilterProxyModel, QItemSelection, QItemSelectionModel
9+
)
610

711
from Orange.util import deprecated
812
from Orange.widgets import gui, widget
@@ -77,6 +81,7 @@ def acceptsDropEvent(self, event):
7781

7882

7983
class OWSelectAttributes(widget.OWWidget):
84+
# pylint: disable=too-many-instance-attributes
8085
name = "Select Columns"
8186
description = "Select columns from the data table and assign them to " \
8287
"data features, classes or meta variables."
@@ -99,6 +104,20 @@ class Outputs:
99104

100105
def __init__(self):
101106
super().__init__()
107+
# Schedule interface updates (enabled buttons) using a coalescing
108+
# single shot timer (complex interactions on selection and filtering
109+
# updates in the 'available_attrs_view')
110+
self.__interface_update_timer = QTimer(self, interval=0, singleShot=True)
111+
self.__interface_update_timer.timeout.connect(
112+
self.__update_interface_state)
113+
# The last view that has the selection for move operation's source
114+
self.__last_active_view = None # type: Optional[QListView]
115+
116+
def update_on_change(view):
117+
# Schedule interface state update on selection change in `view`
118+
self.__last_active_view = view
119+
self.__interface_update_timer.start()
120+
102121
self.controlArea = QWidget(self.controlArea)
103122
self.layout().addWidget(self.controlArea)
104123
layout = QGridLayout()
@@ -117,9 +136,7 @@ def dropcompleted(action):
117136
self.commit()
118137

119138
self.available_attrs_view.selectionModel().selectionChanged.connect(
120-
partial(self.update_interface_state, self.available_attrs_view))
121-
self.available_attrs_view.selectionModel().selectionChanged.connect(
122-
partial(self.update_interface_state, self.available_attrs_view))
139+
partial(update_on_change, self.available_attrs_view))
123140
self.available_attrs_view.dragDropActionDidComplete.connect(dropcompleted)
124141

125142
box.layout().addWidget(self.available_attrs_view)
@@ -133,7 +150,7 @@ def dropcompleted(action):
133150

134151
self.used_attrs_view.setModel(self.used_attrs)
135152
self.used_attrs_view.selectionModel().selectionChanged.connect(
136-
partial(self.update_interface_state, self.used_attrs_view))
153+
partial(update_on_change, self.used_attrs_view))
137154
self.used_attrs_view.dragDropActionDidComplete.connect(dropcompleted)
138155
box.layout().addWidget(self.used_attrs_view)
139156
layout.addWidget(box, 0, 2, 1, 1)
@@ -145,7 +162,7 @@ def dropcompleted(action):
145162
Orange.data.ContinuousVariable))
146163
self.class_attrs_view.setModel(self.class_attrs)
147164
self.class_attrs_view.selectionModel().selectionChanged.connect(
148-
partial(self.update_interface_state, self.class_attrs_view))
165+
partial(update_on_change, self.class_attrs_view))
149166
self.class_attrs_view.dragDropActionDidComplete.connect(dropcompleted)
150167
self.class_attrs_view.setMaximumHeight(72)
151168
box.layout().addWidget(self.class_attrs_view)
@@ -157,7 +174,7 @@ def dropcompleted(action):
157174
acceptedType=Orange.data.Variable)
158175
self.meta_attrs_view.setModel(self.meta_attrs)
159176
self.meta_attrs_view.selectionModel().selectionChanged.connect(
160-
partial(self.update_interface_state, self.meta_attrs_view))
177+
partial(update_on_change, self.meta_attrs_view))
161178
self.meta_attrs_view.dragDropActionDidComplete.connect(dropcompleted)
162179
box.layout().addWidget(self.meta_attrs_view)
163180
layout.addWidget(box, 2, 2, 1, 1)
@@ -338,10 +355,16 @@ def move_from_to(self, src, dst, rows, exclusive=False):
338355

339356
self.commit()
340357

358+
def __update_interface_state(self):
359+
last_view = self.__last_active_view
360+
if last_view is not None:
361+
self.update_interface_state(last_view)
362+
341363
def update_interface_state(self, focus=None, selected=None, deselected=None):
342364
for view in [self.available_attrs_view, self.used_attrs_view,
343365
self.class_attrs_view, self.meta_attrs_view]:
344-
if view is not focus and not view.hasFocus() and self.selected_rows(view):
366+
if view is not focus and not view.hasFocus() \
367+
and view.selectionModel().hasSelection():
345368
view.selectionModel().clear()
346369

347370
def selected_vars(view):
@@ -358,7 +381,7 @@ def selected_vars(view):
358381
for var in available_types)
359382

360383
move_attr_enabled = (available_selected and all_primitive) or \
361-
attrs_selected
384+
attrs_selected
362385

363386
self.move_attr_button.setEnabled(bool(move_attr_enabled))
364387
if move_attr_enabled:
@@ -375,6 +398,9 @@ def selected_vars(view):
375398
if move_meta_enabled:
376399
self.move_meta_button.setText(">" if available_selected else "<")
377400

401+
self.__last_active_view = None
402+
self.__interface_update_timer.stop()
403+
378404
def commit(self):
379405
self.update_domain_role_hints()
380406
if self.data is not None:
@@ -418,7 +444,7 @@ def send_report(self):
418444
self.report_items((("Removed", text),))
419445

420446

421-
def test_main(argv=None):
447+
def main(argv=None): # pragma: no cover
422448
from AnyQt.QtWidgets import QApplication
423449
if argv is None:
424450
argv = sys.argv
@@ -440,5 +466,6 @@ def test_main(argv=None):
440466
w.saveSettings()
441467
return rval
442468

443-
if __name__ == "__main__":
444-
sys.exit(test_main())
469+
470+
if __name__ == "__main__": # pragma: no cover
471+
sys.exit(main())

0 commit comments

Comments
 (0)