Skip to content

Commit 94c4a61

Browse files
lanzagarastaric
authored andcommitted
Merge pull request biolab#1843 from kernc/VarListModel_dnd
itemmodels.VariableListModel drag-and-drop updates (cherry picked from commit f0e4ea3)
1 parent 70f4fbf commit 94c4a61

File tree

3 files changed

+47
-147
lines changed

3 files changed

+47
-147
lines changed

Orange/widgets/data/owselectcolumns.py

Lines changed: 18 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@
1010
QItemSelection, QItemSelectionModel
1111
)
1212

13+
from Orange.util import deprecated
1314
from Orange.widgets import gui, widget
1415
from Orange.widgets.data.contexthandlers import \
1516
SelectAttributesDomainContextHandler
1617
from Orange.widgets.settings import *
1718
from Orange.data.table import Table
18-
from Orange.widgets.utils import itemmodels, vartype
19+
from Orange.widgets.utils import vartype
20+
from Orange.widgets.utils.itemmodels import VariableListModel, PyListModel
1921
import Orange
2022

2123

@@ -57,86 +59,30 @@ def source_indexes(indexes, view):
5759
def delslice(model, start, end):
5860
""" Delete the start, end slice (rows) from the model.
5961
"""
60-
if isinstance(model, itemmodels.PyListModel):
62+
if isinstance(model, PyListModel):
6163
del model[start:end]
6264
elif isinstance(model, QAbstractItemModel):
6365
model.removeRows(start, end - start)
6466
else:
6567
raise TypeError(type(model))
6668

6769

68-
class VariablesListItemModel(itemmodels.VariableListModel):
69-
""" An Qt item model for for list of orange.Variable objects.
70-
Supports drag operations
71-
"""
72-
def flags(self, index):
73-
flags = super().flags(index)
74-
if index.isValid():
75-
flags |= Qt.ItemIsDragEnabled
76-
else:
77-
flags |= Qt.ItemIsDropEnabled
78-
return flags
79-
80-
###########
81-
# Drag/Drop
82-
###########
83-
84-
MIME_TYPE = "application/x-Orange-VariableListModelData"
85-
86-
def supportedDropActions(self):
87-
return Qt.MoveAction
88-
89-
def supportedDragActions(self):
90-
return Qt.MoveAction
91-
92-
def mimeTypes(self):
93-
return [self.MIME_TYPE]
94-
95-
def mimeData(self, indexlist):
96-
descriptors = []
97-
vars = []
98-
for index in indexlist:
99-
var = self[index.row()]
100-
descriptors.append((var.name, vartype(var)))
101-
vars.append(var)
102-
mime = QMimeData()
103-
mime.setData(self.MIME_TYPE,
104-
QByteArray(str(descriptors).encode("utf-8")))
105-
mime._vars = vars
106-
return mime
107-
108-
def dropMimeData(self, mime, action, row, column, parent):
109-
if action == Qt.IgnoreAction:
110-
return True
111-
vars = self.items_from_mime_data(mime)
112-
if vars is None:
113-
return False
114-
if row == -1:
115-
row = len(self)
116-
self[row:row] = vars
117-
return True
118-
119-
def items_from_mime_data(self, mime):
120-
if not mime.hasFormat(self.MIME_TYPE):
121-
return None, None
122-
if hasattr(mime, "_vars"):
123-
vars = mime._vars
124-
return vars
125-
else:
126-
#TODO: get vars from orange.Variable.getExisting
127-
return None, None
70+
# owloadcorpus in orange3-text used this
71+
@deprecated('Orange.widgets.utils.itemmodels.VariableListModel')
72+
def VariablesListItemModel(*args, **kwargs):
73+
return VariableListModel(*args, enable_dnd=True, **kwargs)
12874

12975

130-
class ClassVarListItemModel(VariablesListItemModel):
76+
class ClassVarListItemModel(VariableListModel):
13177
def dropMimeData(self, mime, action, row, column, parent):
13278
""" Ensure only one variable can be dropped onto the view.
13379
"""
134-
vars = self.items_from_mime_data(mime)
80+
vars = mime.property('_items')
13581
if vars is None or len(self) + len(vars) > 1:
13682
return False
13783
if action == Qt.IgnoreAction:
13884
return True
139-
return VariablesListItemModel.dropMimeData(
85+
return VariableListModel.dropMimeData(
14086
self, mime, action, row, column, parent)
14187

14288

@@ -206,7 +152,7 @@ def acceptsDropEvent(self, event):
206152
return False
207153

208154
mime = event.mimeData()
209-
vars = source_model(self).items_from_mime_data(mime)
155+
vars = mime.property('_items')
210156
if vars is None:
211157
return False
212158

@@ -230,7 +176,7 @@ def acceptsDropEvent(self, event):
230176
"""
231177
accepts = super().acceptsDropEvent(event)
232178
mime = event.mimeData()
233-
vars = source_model(self).items_from_mime_data(mime)
179+
vars = mime.property('_items')
234180
if vars is None:
235181
return False
236182

@@ -263,7 +209,7 @@ def filter_accepts_variable(self, var):
263209

264210
def filterAcceptsRow(self, source_row, source_parent):
265211
model = self.sourceModel()
266-
if isinstance(model, itemmodels.VariableListModel):
212+
if isinstance(model, VariableListModel):
267213
var = model[source_row]
268214
return self.filter_accepts_variable(var)
269215
else:
@@ -336,7 +282,7 @@ def __init__(self):
336282
self.completer_navigator = CompleterNavigator(self)
337283
self.filter_edit.installEventFilter(self.completer_navigator)
338284

339-
self.available_attrs = VariablesListItemModel()
285+
self.available_attrs = VariableListModel(enable_dnd=True)
340286
self.available_attrs.rowsRemoved.connect(self.update_completer_model)
341287

342288
self.available_attrs_proxy = VariableFilterProxyModel()
@@ -356,7 +302,7 @@ def __init__(self):
356302
layout.addWidget(box, 0, 0, 3, 1)
357303

358304
box = gui.vBox(self.controlArea, "Features", addToLayout=False)
359-
self.used_attrs = VariablesListItemModel()
305+
self.used_attrs = VariableListModel(enable_dnd=True)
360306
self.used_attrs.rowsRemoved.connect(self.update_completer_model)
361307
self.used_attrs_view = VariablesListItemView(
362308
acceptedType=(Orange.data.DiscreteVariable,
@@ -369,7 +315,7 @@ def __init__(self):
369315
layout.addWidget(box, 0, 2, 1, 1)
370316

371317
box = gui.vBox(self.controlArea, "Target Variable", addToLayout=False)
372-
self.class_attrs = ClassVarListItemModel()
318+
self.class_attrs = ClassVarListItemModel(enable_dnd=True)
373319
self.class_attrs.rowsRemoved.connect(self.update_completer_model)
374320
self.class_attrs_view = ClassVariableItemView(
375321
acceptedType=(Orange.data.DiscreteVariable,
@@ -382,7 +328,7 @@ def __init__(self):
382328
layout.addWidget(box, 1, 2, 1, 1)
383329

384330
box = gui.vBox(self.controlArea, "Meta Attributes", addToLayout=False)
385-
self.meta_attrs = VariablesListItemModel()
331+
self.meta_attrs = VariableListModel(enable_dnd=True)
386332
self.meta_attrs.rowsRemoved.connect(self.update_completer_model)
387333
self.meta_attrs_view = VariablesListItemView(
388334
acceptedType=Orange.data.Variable)

Orange/widgets/utils/itemmodels.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -292,16 +292,19 @@ def remove(self, val):
292292
class PyListModel(QAbstractListModel):
293293
""" A model for displaying python list like objects in Qt item view classes
294294
"""
295-
MIME_TYPES = ["application/x-Orange-PyListModelData"]
295+
MIME_TYPE = "application/x-Orange-PyListModelData"
296296
Separator = object()
297297

298298
def __init__(self, iterable=None, parent=None,
299299
flags=Qt.ItemIsSelectable | Qt.ItemIsEnabled,
300300
list_item_role=Qt.DisplayRole,
301+
enable_dnd=False,
301302
supportedDropActions=Qt.MoveAction):
302303
super().__init__(parent)
303304
self._list = []
304305
self._other_data = []
306+
if enable_dnd:
307+
flags |= Qt.ItemIsDragEnabled
305308
self._flags = flags
306309
self.list_item_role = list_item_role
307310

@@ -549,17 +552,18 @@ def supportedDropActions(self):
549552
return self._supportedDropActions
550553

551554
def mimeTypes(self):
552-
return self.MIME_TYPES + list(QAbstractListModel.mimeTypes(self))
555+
return [self.MIME_TYPE] + list(QAbstractListModel.mimeTypes(self))
553556

554557
def mimeData(self, indexlist):
555558
if len(indexlist) <= 0:
556559
return None
557560

558561
items = [self[i.row()] for i in indexlist]
562+
itemdata = [self.itemData(i) for i in indexlist]
559563
mime = QAbstractListModel.mimeData(self, indexlist)
560-
data = pickle.dumps(vars)
561-
mime.set_data(self.MIME_TYPE, QByteArray(data))
562-
mime._items = items
564+
mime.setData(self.MIME_TYPE, b'see properties: _items, _itemdata')
565+
mime.setProperty('_items', items)
566+
mime.setProperty('_itemdata', itemdata)
563567
return mime
564568

565569
def dropMimeData(self, mime, action, row, column, parent):
@@ -569,14 +573,19 @@ def dropMimeData(self, mime, action, row, column, parent):
569573
if not mime.hasFormat(self.MIME_TYPE):
570574
return False
571575

572-
if hasattr(mime, "_vars"):
573-
vars_ = mime._vars
574-
else:
575-
desc = str(mime.data(self.MIME_TYPE))
576-
vars_ = pickle.loads(desc)
576+
items = mime.property('_items')
577+
itemdata = mime.property('_itemdata')
578+
579+
if not items:
580+
return False
577581

578-
return QAbstractListModel.dropMimeData(
579-
self, mime, action, row, column, parent)
582+
if row == -1:
583+
row = len(self)
584+
585+
self[row:row] = items
586+
for i, data in enumerate(itemdata):
587+
self.setItemData(self.index(row + i), data)
588+
return True
580589

581590

582591
class PyListModelTooltip(PyListModel):

Orange/widgets/visualize/owlinearprojection.py

Lines changed: 8 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -30,71 +30,16 @@
3030
from Orange.data import Table, Variable
3131
from Orange.data.sql.table import SqlTable
3232
from Orange.widgets import widget, gui, settings
33-
from Orange.widgets.utils import itemmodels, colorpalette
33+
from Orange.widgets.utils import colorpalette
3434
from Orange.widgets.utils.annotated_data import (
3535
create_annotated_table, ANNOTATED_DATA_SIGNAL_NAME
3636
)
37+
from Orange.widgets.utils.itemmodels import VariableListModel
3738
from .owscatterplotgraph import LegendItem, legend_anchor_pos
3839
from Orange.widgets.utils import classdensity
3940
from Orange.canvas import report
4041

4142

42-
class DnDVariableListModel(itemmodels.VariableListModel):
43-
44-
MimeType = "application/x-orange-variable-list"
45-
46-
def supportedDropActions(self):
47-
return Qt.MoveAction
48-
49-
def supportedDragActions(self):
50-
return Qt.MoveAction
51-
52-
def mimeTypes(self):
53-
return [DnDVariableListModel.MimeType]
54-
55-
def mimeData(self, indexlist):
56-
variables = []
57-
itemdata = []
58-
for index in indexlist:
59-
variables.append(self[index.row()])
60-
itemdata.append(self.itemData(index))
61-
62-
mime = QMimeData()
63-
mime.setData(self.MimeType, b"see properties")
64-
mime.setProperty("variables", variables)
65-
mime.setProperty("itemdata", itemdata)
66-
return mime
67-
68-
def dropMimeData(self, mime, action, row, column, parent):
69-
if action == Qt.IgnoreAction:
70-
return True
71-
elif not mime.hasFormat(self.MimeType):
72-
return False
73-
74-
variables = mime.property("variables")
75-
itemdata = mime.property("itemdata")
76-
77-
if variables is None:
78-
return False
79-
80-
if row == -1:
81-
row = len(self)
82-
83-
# Insert variables at row and restore other item data
84-
self[row:row] = variables
85-
for i, data in enumerate(itemdata):
86-
self.setItemData(self.index(row + i), data)
87-
return True
88-
89-
def flags(self, index):
90-
flags = super().flags(index)
91-
if index.isValid():
92-
flags |= Qt.ItemIsDragEnabled
93-
else:
94-
flags |= Qt.ItemIsDropEnabled
95-
return flags
96-
97-
9843
class ScatterPlotItem(pg.ScatterPlotItem):
9944
Symbols = pyqtgraph.graphicsItems.ScatterPlotItem.Symbols
10045

@@ -304,8 +249,8 @@ def __init__(self):
304249
)
305250
view.addAction(movedown)
306251

307-
self.varmodel_selected = model = DnDVariableListModel(
308-
parent=self)
252+
self.varmodel_selected = model = VariableListModel(
253+
parent=self, enable_dnd=True)
309254

310255
model.rowsInserted.connect(self._invalidate_plot)
311256
model.rowsRemoved.connect(self._invalidate_plot)
@@ -335,17 +280,17 @@ def __init__(self):
335280
)
336281
view.addAction(moveup)
337282

338-
self.varmodel_other = model = DnDVariableListModel(parent=self)
283+
self.varmodel_other = model = VariableListModel(parent=self, enable_dnd=True)
339284
view.setModel(model)
340285

341286
box1.layout().addWidget(view)
342287

343288
box = gui.vBox(self.controlArea, box=True)
344289
box.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum)
345290

346-
self.colorvar_model = itemmodels.VariableListModel(parent=self)
347-
self.shapevar_model = itemmodels.VariableListModel(parent=self)
348-
self.sizevar_model = itemmodels.VariableListModel(parent=self)
291+
self.colorvar_model = VariableListModel(parent=self, enable_dnd=True)
292+
self.shapevar_model = VariableListModel(parent=self, enable_dnd=True)
293+
self.sizevar_model = VariableListModel(parent=self, enable_dnd=True)
349294

350295
form = QFormLayout(
351296
formAlignment=Qt.AlignLeft,

0 commit comments

Comments
 (0)