Skip to content

Commit

Permalink
Merge pull request biolab#1843 from kernc/VarListModel_dnd
Browse files Browse the repository at this point in the history
itemmodels.VariableListModel drag-and-drop updates
(cherry picked from commit f0e4ea3)
  • Loading branch information
lanzagar authored and astaric committed Jan 18, 2017
1 parent a15dbb8 commit 0d1d7de
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 147 deletions.
90 changes: 18 additions & 72 deletions Orange/widgets/data/owselectcolumns.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
QItemSelection, QItemSelectionModel
)

from Orange.util import deprecated
from Orange.widgets import gui, widget
from Orange.widgets.data.contexthandlers import \
SelectAttributesDomainContextHandler
from Orange.widgets.settings import *
from Orange.data.table import Table
from Orange.widgets.utils import itemmodels, vartype
from Orange.widgets.utils import vartype
from Orange.widgets.utils.itemmodels import VariableListModel, PyListModel
import Orange


Expand Down Expand Up @@ -57,86 +59,30 @@ def source_indexes(indexes, view):
def delslice(model, start, end):
""" Delete the start, end slice (rows) from the model.
"""
if isinstance(model, itemmodels.PyListModel):
if isinstance(model, PyListModel):
del model[start:end]
elif isinstance(model, QAbstractItemModel):
model.removeRows(start, end - start)
else:
raise TypeError(type(model))


class VariablesListItemModel(itemmodels.VariableListModel):
""" An Qt item model for for list of orange.Variable objects.
Supports drag operations
"""
def flags(self, index):
flags = super().flags(index)
if index.isValid():
flags |= Qt.ItemIsDragEnabled
else:
flags |= Qt.ItemIsDropEnabled
return flags

###########
# Drag/Drop
###########

MIME_TYPE = "application/x-Orange-VariableListModelData"

def supportedDropActions(self):
return Qt.MoveAction

def supportedDragActions(self):
return Qt.MoveAction

def mimeTypes(self):
return [self.MIME_TYPE]

def mimeData(self, indexlist):
descriptors = []
vars = []
for index in indexlist:
var = self[index.row()]
descriptors.append((var.name, vartype(var)))
vars.append(var)
mime = QMimeData()
mime.setData(self.MIME_TYPE,
QByteArray(str(descriptors).encode("utf-8")))
mime._vars = vars
return mime

def dropMimeData(self, mime, action, row, column, parent):
if action == Qt.IgnoreAction:
return True
vars = self.items_from_mime_data(mime)
if vars is None:
return False
if row == -1:
row = len(self)
self[row:row] = vars
return True

def items_from_mime_data(self, mime):
if not mime.hasFormat(self.MIME_TYPE):
return None, None
if hasattr(mime, "_vars"):
vars = mime._vars
return vars
else:
#TODO: get vars from orange.Variable.getExisting
return None, None
# owloadcorpus in orange3-text used this
@deprecated('Orange.widgets.utils.itemmodels.VariableListModel')
def VariablesListItemModel(*args, **kwargs):
return VariableListModel(*args, enable_dnd=True, **kwargs)


class ClassVarListItemModel(VariablesListItemModel):
class ClassVarListItemModel(VariableListModel):
def dropMimeData(self, mime, action, row, column, parent):
""" Ensure only one variable can be dropped onto the view.
"""
vars = self.items_from_mime_data(mime)
vars = mime.property('_items')
if vars is None or len(self) + len(vars) > 1:
return False
if action == Qt.IgnoreAction:
return True
return VariablesListItemModel.dropMimeData(
return VariableListModel.dropMimeData(
self, mime, action, row, column, parent)


Expand Down Expand Up @@ -206,7 +152,7 @@ def acceptsDropEvent(self, event):
return False

mime = event.mimeData()
vars = source_model(self).items_from_mime_data(mime)
vars = mime.property('_items')
if vars is None:
return False

Expand All @@ -230,7 +176,7 @@ def acceptsDropEvent(self, event):
"""
accepts = super().acceptsDropEvent(event)
mime = event.mimeData()
vars = source_model(self).items_from_mime_data(mime)
vars = mime.property('_items')
if vars is None:
return False

Expand Down Expand Up @@ -263,7 +209,7 @@ def filter_accepts_variable(self, var):

def filterAcceptsRow(self, source_row, source_parent):
model = self.sourceModel()
if isinstance(model, itemmodels.VariableListModel):
if isinstance(model, VariableListModel):
var = model[source_row]
return self.filter_accepts_variable(var)
else:
Expand Down Expand Up @@ -336,7 +282,7 @@ def __init__(self):
self.completer_navigator = CompleterNavigator(self)
self.filter_edit.installEventFilter(self.completer_navigator)

self.available_attrs = VariablesListItemModel()
self.available_attrs = VariableListModel(enable_dnd=True)
self.available_attrs.rowsRemoved.connect(self.update_completer_model)

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

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

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

box = gui.vBox(self.controlArea, "Meta Attributes", addToLayout=False)
self.meta_attrs = VariablesListItemModel()
self.meta_attrs = VariableListModel(enable_dnd=True)
self.meta_attrs.rowsRemoved.connect(self.update_completer_model)
self.meta_attrs_view = VariablesListItemView(
acceptedType=Orange.data.Variable)
Expand Down
33 changes: 21 additions & 12 deletions Orange/widgets/utils/itemmodels.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,16 +292,19 @@ def remove(self, val):
class PyListModel(QAbstractListModel):
""" A model for displaying python list like objects in Qt item view classes
"""
MIME_TYPES = ["application/x-Orange-PyListModelData"]
MIME_TYPE = "application/x-Orange-PyListModelData"
Separator = object()

def __init__(self, iterable=None, parent=None,
flags=Qt.ItemIsSelectable | Qt.ItemIsEnabled,
list_item_role=Qt.DisplayRole,
enable_dnd=False,
supportedDropActions=Qt.MoveAction):
super().__init__(parent)
self._list = []
self._other_data = []
if enable_dnd:
flags |= Qt.ItemIsDragEnabled
self._flags = flags
self.list_item_role = list_item_role

Expand Down Expand Up @@ -549,17 +552,18 @@ def supportedDropActions(self):
return self._supportedDropActions

def mimeTypes(self):
return self.MIME_TYPES + list(QAbstractListModel.mimeTypes(self))
return [self.MIME_TYPE] + list(QAbstractListModel.mimeTypes(self))

def mimeData(self, indexlist):
if len(indexlist) <= 0:
return None

items = [self[i.row()] for i in indexlist]
itemdata = [self.itemData(i) for i in indexlist]
mime = QAbstractListModel.mimeData(self, indexlist)
data = pickle.dumps(vars)
mime.set_data(self.MIME_TYPE, QByteArray(data))
mime._items = items
mime.setData(self.MIME_TYPE, b'see properties: _items, _itemdata')
mime.setProperty('_items', items)
mime.setProperty('_itemdata', itemdata)
return mime

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

if hasattr(mime, "_vars"):
vars_ = mime._vars
else:
desc = str(mime.data(self.MIME_TYPE))
vars_ = pickle.loads(desc)
items = mime.property('_items')
itemdata = mime.property('_itemdata')

if not items:
return False

return QAbstractListModel.dropMimeData(
self, mime, action, row, column, parent)
if row == -1:
row = len(self)

self[row:row] = items
for i, data in enumerate(itemdata):
self.setItemData(self.index(row + i), data)
return True


class PyListModelTooltip(PyListModel):
Expand Down
71 changes: 8 additions & 63 deletions Orange/widgets/visualize/owlinearprojection.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,71 +30,16 @@
from Orange.data import Table, Variable
from Orange.data.sql.table import SqlTable
from Orange.widgets import widget, gui, settings
from Orange.widgets.utils import itemmodels, colorpalette
from Orange.widgets.utils import colorpalette
from Orange.widgets.utils.annotated_data import (
create_annotated_table, ANNOTATED_DATA_SIGNAL_NAME
)
from Orange.widgets.utils.itemmodels import VariableListModel
from .owscatterplotgraph import LegendItem, legend_anchor_pos
from Orange.widgets.utils import classdensity
from Orange.canvas import report


class DnDVariableListModel(itemmodels.VariableListModel):

MimeType = "application/x-orange-variable-list"

def supportedDropActions(self):
return Qt.MoveAction

def supportedDragActions(self):
return Qt.MoveAction

def mimeTypes(self):
return [DnDVariableListModel.MimeType]

def mimeData(self, indexlist):
variables = []
itemdata = []
for index in indexlist:
variables.append(self[index.row()])
itemdata.append(self.itemData(index))

mime = QMimeData()
mime.setData(self.MimeType, b"see properties")
mime.setProperty("variables", variables)
mime.setProperty("itemdata", itemdata)
return mime

def dropMimeData(self, mime, action, row, column, parent):
if action == Qt.IgnoreAction:
return True
elif not mime.hasFormat(self.MimeType):
return False

variables = mime.property("variables")
itemdata = mime.property("itemdata")

if variables is None:
return False

if row == -1:
row = len(self)

# Insert variables at row and restore other item data
self[row:row] = variables
for i, data in enumerate(itemdata):
self.setItemData(self.index(row + i), data)
return True

def flags(self, index):
flags = super().flags(index)
if index.isValid():
flags |= Qt.ItemIsDragEnabled
else:
flags |= Qt.ItemIsDropEnabled
return flags


class ScatterPlotItem(pg.ScatterPlotItem):
Symbols = pyqtgraph.graphicsItems.ScatterPlotItem.Symbols

Expand Down Expand Up @@ -304,8 +249,8 @@ def __init__(self):
)
view.addAction(movedown)

self.varmodel_selected = model = DnDVariableListModel(
parent=self)
self.varmodel_selected = model = VariableListModel(
parent=self, enable_dnd=True)

model.rowsInserted.connect(self._invalidate_plot)
model.rowsRemoved.connect(self._invalidate_plot)
Expand Down Expand Up @@ -335,17 +280,17 @@ def __init__(self):
)
view.addAction(moveup)

self.varmodel_other = model = DnDVariableListModel(parent=self)
self.varmodel_other = model = VariableListModel(parent=self, enable_dnd=True)
view.setModel(model)

box1.layout().addWidget(view)

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

self.colorvar_model = itemmodels.VariableListModel(parent=self)
self.shapevar_model = itemmodels.VariableListModel(parent=self)
self.sizevar_model = itemmodels.VariableListModel(parent=self)
self.colorvar_model = VariableListModel(parent=self, enable_dnd=True)
self.shapevar_model = VariableListModel(parent=self, enable_dnd=True)
self.sizevar_model = VariableListModel(parent=self, enable_dnd=True)

form = QFormLayout(
formAlignment=Qt.AlignLeft,
Expand Down

0 comments on commit 0d1d7de

Please sign in to comment.