[PyQt] Drag & Drop with QTreeWidget
Baz Walter
bazwal at ftml.net
Mon Dec 14 23:27:18 GMT 2009
NARCISO, Rui wrote:
> It worked nicely up to the sorting part.
>
> And if th euser sorts the data himself, then eveything is ok.
>
> It's just when I try to implement default sorting that that fails.
>
> Maybe I can pick up on a signal telling that sorting is in progress to override this behaviour in the rowsInserted function...
>
> -----Message d'origine-----
> De : Baz Walter [mailto:bazwal at ftml.net]
> Envoyé : vendredi 11 décembre 2009 21:46
> À : NARCISO, Rui
> Cc : PyQt (E-mail)
> Objet : Re: [PyQt] Drag & Drop with QTreeWidget
>
>
> NARCISO, Rui wrote:
>> Hi again
>>
>> Your approach worked nicely but when I tried to enable sorting using:
>>
>> self.setSortingEnabled(True)
>> self.sortByColumn(0, QtCore.Qt.AscendingOrder)
>> I get a segmentation fault.
>>
>> If i disable the sortByColumn then it works.
>>
>> How then to set the sorting using column 0 by default ?
>
> actually, i now think my approach was too crude. the trouble with using
> rowsInserted is that it is called every time items are added or moved
> (or sorted).
>
> what is really needed is a way to add special handling only for dropped
> items. unfortunately, there does not appear to be an obvious way to
> detect what items were dropped and where. so i think the only reliable
> way to get the behaviour you want is to reimplement qtreewidget's drop
> event handling (which is probably doable, but not easy).
i had another look at reimplementing qtreewidget's drop event handling
and it is in fact much easier than i thought. the code below simply
reproduces the current behaviour (it's mostly a port of the qt source).
hopefully it should not be too difficult to see how to adapt the code in
the moveSelection function to get whatever alternate behaviours you
want. (note that this example assumes internal moves only).
import sys
from PyQt4 import QtGui, QtCore
class TreeWidget(QtGui.QTreeWidget):
def __init__(self, parent=None):
QtGui.QTreeWidget.__init__(self, parent)
self.header().setHidden(True)
self.setSelectionMode(self.ExtendedSelection)
self.setDragDropMode(self.InternalMove)
self.setDragEnabled(True)
self.setDropIndicatorShown(True)
def add(num):
item = QtGui.QTreeWidgetItem(
self, QtCore.QStringList('Item(%i)' % num))
for i in xrange(1, 4):
QtGui.QTreeWidgetItem(
item, QtCore.QStringList('Child(%i,%i)' % (num, i)))
item.setExpanded(True)
for i in xrange(1, 3):
add(i)
def dropEvent(self, event):
if event.source() == self:
QtGui.QAbstractItemView.dropEvent(self, event)
def dropMimeData(self, parent, row, data, action):
if action == QtCore.Qt.MoveAction:
return self.moveSelection(parent, row)
return False
def moveSelection(self, parent, position):
# save the selected items
selection = [QtCore.QPersistentModelIndex(i)
for i in self.selectedIndexes()]
parent_index = self.indexFromItem(parent)
if parent_index in selection:
return False
# save the drop location in case it gets moved
target = self.model().index(position, 0, parent_index).row()
if target < 0:
target = position
# remove the selected items
taken = []
for index in reversed(selection):
item = self.itemFromIndex(QtCore.QModelIndex(index))
if item is None or item.parent() is None:
taken.append(self.takeTopLevelItem(index.row()))
else:
taken.append(item.parent().takeChild(index.row()))
# insert the selected items at their new positions
while taken:
if position == -1:
# append the items if position not specified
if parent_index.isValid():
parent.insertChild(
parent.childCount(), taken.pop(0))
else:
self.insertTopLevelItem(
self.topLevelItemCount(), taken.pop(0))
else:
# insert the items at the specified position
if parent_index.isValid():
parent.insertChild(min(target,
parent.childCount()), taken.pop(0))
else:
self.insertTopLevelItem(min(target,
self.topLevelItemCount()), taken.pop(0))
return True
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
tree = TreeWidget()
tree.resize(200, 300)
tree.move(300, 300)
tree.show()
sys.exit(app.exec_())
More information about the PyQt
mailing list