Skip to content

Commit 445613d

Browse files
committed
Select Columns: Accept partially correct drops
1 parent 07807eb commit 445613d

File tree

1 file changed

+85
-7
lines changed

1 file changed

+85
-7
lines changed

Orange/widgets/data/owselectcolumns.py

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Qt, QTimer, QSortFilterProxyModel, QItemSelection, QItemSelectionModel,
88
QMimeData, QAbstractItemModel
99
)
10+
from AnyQt.QtGui import QDrag
1011

1112
from Orange.data import Domain, Variable
1213
from Orange.widgets import gui, widget
@@ -50,6 +51,10 @@ class VariablesListItemModel(VariableListModel):
5051
"""
5152
MIME_TYPE = "application/x-Orange-VariableListModelData"
5253

54+
def __init__(self, *args, primitive=False, **kwargs):
55+
super().__init__(*args, **kwargs)
56+
self.primitive = primitive
57+
5358
def flags(self, index):
5459
flags = super().flags(index)
5560
if index.isValid():
@@ -97,7 +102,76 @@ def dropMimeData(self, mime, action, row, column, parent):
97102
if row < 0:
98103
row = self.rowCount()
99104

105+
if self.primitive and not all(var.is_primitive() for var in variables):
106+
variables = [var for var in variables if var.is_primitive()]
107+
self[row:row] = variables
108+
mime.setProperty("_moved", variables)
109+
return False
110+
100111
self[row:row] = variables
112+
mime.setProperty("_moved", True)
113+
return True
114+
115+
116+
class SelectListItemView(VariablesListItemView):
117+
"""
118+
VariableListItemView that accepts information on what has been dropped
119+
120+
At the end of the drag action, only dropped items (i.e. accepted by the
121+
target) are removed.
122+
"""
123+
def startDrag(self, supported_actions):
124+
indexes = self.selectedIndexes()
125+
if len(indexes) == 0:
126+
return
127+
data = self.model().mimeData(indexes)
128+
if not data:
129+
return
130+
drag = QDrag(self)
131+
drag.setMimeData(data)
132+
drag.exec(supported_actions, self.defaultDropAction())
133+
134+
moved = data.property("_moved")
135+
if moved is None:
136+
return
137+
138+
if moved is True:
139+
# A quicker path if everyhing is moved
140+
for index in self.selectionModel().selection():
141+
self.model().removeRows(index.top(),
142+
index.bottom() - index.top() + 1)
143+
else:
144+
moved = set(moved)
145+
for row in sorted((ind.row()
146+
for ind in self.selectionModel().selectedIndexes()
147+
if ind.data(gui.TableVariable) in moved),
148+
reverse=True):
149+
self.model().removeRows(row, 1)
150+
151+
152+
class FeatureListItemView(SelectListItemView):
153+
"""
154+
A SelectListItemView whose acceptsDropEvent checks if there are any
155+
primitive variables. Derived from a class that would reject the drop
156+
if there are any non-primitive.
157+
"""
158+
def acceptsDropEvent(self, event):
159+
"""Should the drop event be accepted?"""
160+
# Overridden to allow drops if some, not necessarily all variable types match
161+
# disallow drag/drops between windows
162+
if event.source() is not None and \
163+
event.source().window() is not self.window():
164+
return False
165+
166+
mime = event.mimeData()
167+
vars = mime.property('_items')
168+
if vars is None:
169+
return False
170+
171+
if not any(var.is_primitive for var in vars):
172+
return False
173+
174+
event.accept()
101175
return True
102176

103177

@@ -217,7 +291,9 @@ def update_on_change(view):
217291

218292
self.available_attrs = VariablesListItemModel()
219293
filter_edit, self.available_attrs_view = variables_filter(
220-
parent=self, model=self.available_attrs)
294+
parent=self, model=self.available_attrs,
295+
view_type=SelectListItemView
296+
)
221297
box.layout().addWidget(filter_edit)
222298
self.view_boxes.append((name, box, self.available_attrs_view))
223299
filter_edit.textChanged.connect(self.__var_counts_update_timer.start)
@@ -236,11 +312,12 @@ def dropcompleted(action):
236312
# 3rd column
237313
name = "Features"
238314
box = gui.vBox(self.controlArea, name, addToLayout=False)
239-
self.used_attrs = VariablesListItemModel()
315+
self.used_attrs = VariablesListItemModel(primitive=True)
240316
filter_edit, self.used_attrs_view = variables_filter(
241317
parent=self, model=self.used_attrs,
242318
accepted_type=(Orange.data.DiscreteVariable,
243-
Orange.data.ContinuousVariable))
319+
Orange.data.ContinuousVariable),
320+
view_type=FeatureListItemView)
244321
self.used_attrs.rowsInserted.connect(self.__used_attrs_changed)
245322
self.used_attrs.rowsRemoved.connect(self.__used_attrs_changed)
246323
self.used_attrs_view.selectionModel().selectionChanged.connect(
@@ -263,9 +340,9 @@ def dropcompleted(action):
263340
name = "Target"
264341
box = gui.vBox(self.controlArea, name, addToLayout=False)
265342
self.class_attrs = VariablesListItemModel()
266-
self.class_attrs_view = VariablesListItemView(
343+
self.class_attrs_view = SelectListItemView(
267344
acceptedType=(Orange.data.DiscreteVariable,
268-
Orange.data.ContinuousVariable)
345+
Orange.data.ContinuousVariable),
269346
)
270347
self.class_attrs_view.setModel(self.class_attrs)
271348
self.class_attrs_view.selectionModel().selectionChanged.connect(
@@ -279,8 +356,9 @@ def dropcompleted(action):
279356
name = "Metas"
280357
box = gui.vBox(self.controlArea, name, addToLayout=False)
281358
self.meta_attrs = VariablesListItemModel()
282-
self.meta_attrs_view = VariablesListItemView(
283-
acceptedType=Orange.data.Variable)
359+
self.meta_attrs_view = SelectListItemView(
360+
acceptedType=Orange.data.Variable,
361+
)
284362
self.meta_attrs_view.setModel(self.meta_attrs)
285363
self.meta_attrs_view.selectionModel().selectionChanged.connect(
286364
partial(update_on_change, self.meta_attrs_view))

0 commit comments

Comments
 (0)