Baffled by QListView drag-drop for reordering list
-
I've read the docs repeatedly and read the numerous items in this forum that seem to be related, and am getting nowhere. Using PyQt4 on OSX10.6 I have implemented a basic list model including besides the data, setData, and flags methods also insertRows, removeRows, itemData and setItemData. flags() returns Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsDragEnabled.
Using this as model I have a QListView initialized with:
@ self.setAcceptDrops(True)
self.setDefaultDropAction(Qt.MoveAction)
self.setDragDropMode(QAbstractItemView.InternalMove)
self.setDragDropOverwriteMode(False)
self.setDragEnabled(True)
self.setDropIndicatorShown(True)@When I attempt to drag an item, the itemData() method is called and I can pull the item around -- but the no-drag icon (/) is shown and nothing happens on release.
If I change the model flags() to return also Qt.ItemIsDropEnabled for any item, then the green copy-drag icon (+) is shown but when the mouse is released, the setItemData() method is called to replace the data of the item under the mouse and removeRows() is called to remove the dragged item row. So the model ends up with one fewer items.
I just want the list view to let the user re-order the list by dragging. What am I missing?
-
The root index needs to accept drops but the items don't. The following is a working example, the flags() implementation should solve your problem I think:
@
from PyQt4.QtCore import *
from PyQt4.QtGui import *class ListModel(QAbstractListModel):
def init(self, parent=None):
QAbstractListModel.init(self, parent)self._data=["Row %d" % i for i in xrange(10)] def rowCount(self, parent=QModelIndex()): if parent.isValid(): return 0 return len(self._data) def data(self, index, role=Qt.DisplayRole): if not index.isValid(): return QVariant() if role==Qt.DisplayRole: return self._data[index.row()] return QVariant() def flags(self, index): if index.isValid(): return Qt.ItemIsSelectable|Qt.ItemIsDragEnabled|Qt.ItemIsEnabled return Qt.ItemIsSelectable|Qt.ItemIsDragEnabled| \ Qt.ItemIsDropEnabled|Qt.ItemIsEnabled def insertRows(self, row, count, parent=QModelIndex()): if parent.isValid(): return False beginRow=max(0,row) endRow=min(row+count-1,len(self._data)) self.beginInsertRows(parent, beginRow, endRow) for i in xrange(beginRow, endRow+1): self._data.insert(i,"") self.endInsertRows() return True def setData(self, index, value, role=Qt.EditRole): if not index.isValid() or role!=Qt.DisplayRole: return False self._data[index.row()]=str(value.toString()) self.dataChanged.emit(index,index) return True def removeRows(self, row, count, parent=QModelIndex()): if parent.isValid(): return False if row>=len(self._data) or row+count<=0: return False beginRow=max(0,row) endRow=min(row+count-1,len(self._data)-1) self.beginRemoveRows(parent, beginRow, endRow) for i in xrange(beginRow, endRow+1): self._data.pop(i) self.endRemoveRows() return True
class ListView(QListView):
def init(self, model, parent=None):
QTreeView.init(self, parent)self.setModel(model) self.setMovement(QListView.Snap) self.setDragDropMode(QListView.InternalMove) self.setDragDropOverwriteMode(False)
class Window(QWidget):
def init(self, parent=None):
QWidget.init(self, parent)l=QHBoxLayout(self) self.model=ListModel(self) self.view=ListView(self.model) l.addWidget(self.view)
if name=="main":
from sys import argv, exit
a=QApplication(argv)
w=Window()
w.show()
w.raise_()
exit(a.exec_())
@Hope this helps ;o)
-
It helps very much in fact it solved the problem! All I had to do was recode the flags() and rowCount() methods to match yours, and it worked. I was ignoring the parent index.
I find the role of the parent index very confusing. It is discussed in http://doc.qt.digia.com/qt/model-view-programming.html but not in these practical terms.
-
All of Qt's models are essentially tree models. List models are a tree with only one level of branches and only one column (see diagrams "here":http://doc.qt.digia.com/qt/model-view-programming.html#basic-concepts). In basic terms, the view has to have a single entry point to the model structure and this is the root index. In the case of list and table models this is the parent of all cells in the model.
Looking at "this":http://doc.qt.digia.com/qt/itemviews-simpletreemodel.html example of a tree structure you'll see where all this functionality is fully leveraged. It essentially works like a double linked list.
Anywho, glad I could help, can you mark this thread as [SOLVED] if that's the case.
-
[quote author="dcortesi" date="1355338472"]It helps very much in fact it solved the problem! All I had to do was recode the flags() and rowCount() methods to match yours, and it worked. I was ignoring the parent index.
I find the role of the parent index very confusing. It is discussed in http://doc.qt.digia.com/qt/model-view-programming.html but not in these practical terms.[/quote]
This thread you posted give me much help.Can you give me some snippets that must can save me lots of time.I need to achieve it as soon as possible.Thanks.
-
I know this is a really old post, but figured I'll update it because it was helpful to me. The solution provide here didn't work 100%. I had to also provide implementations for supportedDragActions() and supportedDropActions(), both of which returned MoveAction. Then it worked great.
The differences for me are (1) I'm working in C++ and (2) I'm on version 5.6.2
Thanks!