Skip to content

Commit fe007c7

Browse files
committed
owselectcolumns: Fix performance on filtering with selection
1 parent 422bc94 commit fe007c7

File tree

1 file changed

+40
-12
lines changed

1 file changed

+40
-12
lines changed

Orange/widgets/data/owselectcolumns.py

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

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

712
from Orange.util import deprecated
813
from Orange.widgets import gui, widget
@@ -77,6 +82,7 @@ def acceptsDropEvent(self, event):
7782

7883

7984
class OWSelectAttributes(widget.OWWidget):
85+
# pylint: disable=too-many-instance-attributes
8086
name = "Select Columns"
8187
description = "Select columns from the data table and assign them to " \
8288
"data features, classes or meta variables."
@@ -99,6 +105,20 @@ class Outputs:
99105

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

119139
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))
140+
partial(update_on_change, self.available_attrs_view))
123141
self.available_attrs_view.dragDropActionDidComplete.connect(dropcompleted)
124142

125143
box.layout().addWidget(self.available_attrs_view)
@@ -133,7 +151,7 @@ def dropcompleted(action):
133151

134152
self.used_attrs_view.setModel(self.used_attrs)
135153
self.used_attrs_view.selectionModel().selectionChanged.connect(
136-
partial(self.update_interface_state, self.used_attrs_view))
154+
partial(update_on_change, self.used_attrs_view))
137155
self.used_attrs_view.dragDropActionDidComplete.connect(dropcompleted)
138156
box.layout().addWidget(self.used_attrs_view)
139157
layout.addWidget(box, 0, 2, 1, 1)
@@ -145,7 +163,7 @@ def dropcompleted(action):
145163
Orange.data.ContinuousVariable))
146164
self.class_attrs_view.setModel(self.class_attrs)
147165
self.class_attrs_view.selectionModel().selectionChanged.connect(
148-
partial(self.update_interface_state, self.class_attrs_view))
166+
partial(update_on_change, self.class_attrs_view))
149167
self.class_attrs_view.dragDropActionDidComplete.connect(dropcompleted)
150168
self.class_attrs_view.setMaximumHeight(72)
151169
box.layout().addWidget(self.class_attrs_view)
@@ -157,7 +175,7 @@ def dropcompleted(action):
157175
acceptedType=Orange.data.Variable)
158176
self.meta_attrs_view.setModel(self.meta_attrs)
159177
self.meta_attrs_view.selectionModel().selectionChanged.connect(
160-
partial(self.update_interface_state, self.meta_attrs_view))
178+
partial(update_on_change, self.meta_attrs_view))
161179
self.meta_attrs_view.dragDropActionDidComplete.connect(dropcompleted)
162180
box.layout().addWidget(self.meta_attrs_view)
163181
layout.addWidget(box, 2, 2, 1, 1)
@@ -338,10 +356,16 @@ def move_from_to(self, src, dst, rows, exclusive=False):
338356

339357
self.commit()
340358

359+
def __update_interface_state(self):
360+
last_view = self.__last_active_view
361+
if last_view is not None:
362+
self.update_interface_state(last_view)
363+
341364
def update_interface_state(self, focus=None, selected=None, deselected=None):
342365
for view in [self.available_attrs_view, self.used_attrs_view,
343366
self.class_attrs_view, self.meta_attrs_view]:
344-
if view is not focus and not view.hasFocus() and self.selected_rows(view):
367+
if view is not focus and not view.hasFocus() \
368+
and view.selectionModel().hasSelection():
345369
view.selectionModel().clear()
346370

347371
def selected_vars(view):
@@ -358,7 +382,7 @@ def selected_vars(view):
358382
for var in available_types)
359383

360384
move_attr_enabled = (available_selected and all_primitive) or \
361-
attrs_selected
385+
attrs_selected
362386

363387
self.move_attr_button.setEnabled(bool(move_attr_enabled))
364388
if move_attr_enabled:
@@ -375,6 +399,9 @@ def selected_vars(view):
375399
if move_meta_enabled:
376400
self.move_meta_button.setText(">" if available_selected else "<")
377401

402+
self.__last_active_view = None
403+
self.__interface_update_timer.stop()
404+
378405
def commit(self):
379406
self.update_domain_role_hints()
380407
if self.data is not None:
@@ -418,7 +445,7 @@ def send_report(self):
418445
self.report_items((("Removed", text),))
419446

420447

421-
def test_main(argv=None):
448+
def main(argv=None): # pragma: no cover
422449
from AnyQt.QtWidgets import QApplication
423450
if argv is None:
424451
argv = sys.argv
@@ -440,5 +467,6 @@ def test_main(argv=None):
440467
w.saveSettings()
441468
return rval
442469

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

0 commit comments

Comments
 (0)