[我一直在努力学习如何使用QTreeView和自定义Item类实现QAbstractItemModel,并且除了拖放操作之外,我都能正常工作。
最终,我希望能够使用shift键在“移动”和“复制”项目之间进行切换,但是现在,我只是试图让InternalMove完全起作用。...
我正在像这样重新实现mimeData和dropMimeData。...
class BuildModel( QAbstractItemModel ):
def __init__( self, root):
super( BuildModel, self ).__init__()
def mimeTypes( self ):
return ['sushi-build-items']
def mimeData( self, indices ):
mimedata = QMimeData()
mimedata.setData('sushi-build-items', self.getSerializedData(indices) )
return mimedata
def dropMimeData( self, mimedata, action, row, column, parentIndex ):
if not mimedata.hasFormat( 'sushi-build-items' ):
return False
data = pickle.loads((str(mimedata.data('sushi-build-items'))))
items = dataToItems(data)
self.insertItems(row, items, parentIndex)
return True
def insertItems( self, row, items, parentIndex):
parent = self.itemFromIndex(parentIndex)
self.beginInsertRows( parentIndex, row, row+len(items)-1 )
if row == -1:
parent.addChildren(items)
else:
parent.insertChildren(row, items)
self.endInsertRows()
self.dataChanged.emit(parentIndex, parentIndex)
return True
而且我的树状视图设置为这样的InternalMove。...
class TreeView(QTreeView):
def __init__(self, parent = None, model = None):
super(TreeView, self).__init__(parent = parent)
self.setDragDropMode(QAbstractItemView.InternalMove)
self.setDragEnabled(True)
self.setAcceptDrops(True)
但是当我拖放源项目时,它保持原样,它只是删除重复的项目。TreeView不应该处理所拖动项目吗?如果没有,我该在哪里手动删除它?
我确信我在这里缺少什么。。
您需要在模型中实现removeRows
方法;它应该会自动被调用。
这里是可执行代码。
Disclaimer:您的代码有时与我的代码不同,我喜欢您的编码风格。这是一个琐碎的,仅可执行的代码。
[dropMimeData
返回bool
True
表示您在removeRows
之后自动使用insertRows
。(拖放我认为它将绝对使用insertRows ...)False
表示您不使用它。
有时自动使用removeRows不方便。
例如,如果在其上拖放多个项目,则如果在True
中返回dropMimeData
,通常位置不准确。(position参数增加的越多,子代的长度越减少。这取决于您的编码...)
在singleSelection中,您可能应该返回True
。
除此以外,QAbstractItemModel
具有removeRow
。最终返回removeRows(row,1,parent)。因此removeRows的内容最终决定了删除的方式。
这里是C++
的代码
inline bool QAbstractItemModel :: removeRow(int arow,const QModelIndex&aparent){return removeRows(arow,1,aparent); }
和removeRows
,您需要重写并定义自己的方式。
bool QAbstractItemModel :: removeRows(int,int,const QModelIndex&){返回false;}
from PySide import QtGui
from PySide import QtCore
import pickle
import sys
class TreeView(QtGui.QTreeView):
def __init__(self, parent = None, model = None):
super(TreeView, self).__init__(parent = parent)
self.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.setDragEnabled(True)
self.setAcceptDrops(True)
class BuildModel( QtCore.QAbstractItemModel ):
def __init__( self, items, parent):
super( BuildModel, self ).__init__()
self.__parent = parent
self.root = TreeItem(["root", 0], None)
if items is not None:
for i in items:
self.root.addChild(i)
def rowCount(self, parent=QtCore.QModelIndex()):
if not parent.isValid():
r = self.root
return r.childCount()
else:
r = parent.internalPointer()
return r.childCount()
def columnCount(self, parent=QtCore.QModelIndex()):
return 2
def flags(self, index):
return QtCore.Qt.ItemFlag.ItemIsDragEnabled|QtCore.Qt.ItemIsDropEnabled|QtCore.Qt.ItemIsEditable|QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if orientation == QtCore.Qt.Orientation.Horizontal:
if role == QtCore.Qt.DisplayRole:
if section == 0:
return "Country"
elif section == 1:
return "CountryID"
def index(self, row, column , _parent = QtCore.QModelIndex()):
if not _parent.isValid():
parent = self.root
else:
parent = _parent.internalPointer()
if not self.hasIndex(row, column, _parent):
return QtCore.QModelIndex()
child = parent.child(row)
if child:
childIndex = self.createIndex(row, column, child)
childIndex = QtCore.QPersistentModelIndex(childIndex)
return childIndex
else:
return QtCore.QModelIndex()
def data(self, index, role=QtCore.Qt.DisplayRole):
if not index.isValid():
return None
treeitem = index.internalPointer()
if role == QtCore.Qt.DisplayRole:
column = index.column()
return treeitem._itemData[column]
if role == QtCore.Qt.DecorationRole:
column = index.column()
if column == 0:
return treeitem.icon
def parent(self, child):
if not child.isValid():
return QtCore.QModelIndex()
treeitem = child.internalPointer()
parent = treeitem.parent()
if parent is not None:
parentindex =self.createIndex(treeitem.at(), 0, parent)
return parentindex
else:
return QtCore.QModelIndex()
def supportedDragActions(self):
return QtCore.Qt.MoveAction
def supportedDropActions(self):
return QtCore.Qt.MoveAction
def mimeTypes( self ):
return ['sushi-build-items']
def mimeData( self, indices ):
mimedata = QtCore.QMimeData()
mimedata.setData('sushi-build-items', self.getSerializedData(indices) )
return mimedata
def dropMimeData( self, data, action, row, column, parent):
if not data.hasFormat( 'sushi-build-items' ):
return False
byteArray = QtCore.QByteArray(data.data(('sushi-build-items')))
out = QtCore.QDataStream(byteArray , QtCore.QIODevice.ReadOnly)
newItems = []
if self.__parent.selectionMode() == QtGui.QTreeView.SelectionMode.SingleSelection:
indicator = False
if row != -1:
beginRow = row
indicator = True
parentItem = parent.internalPointer()
elif self.__parent.dropIndicatorPosition() == QtGui.QAbstractItemView.OnItem:
beginRow = 0
parentItem = parent.internalPointer()
else:
beginRow = self.rowCount(parent)
indicator = True
parentItem = parent.internalPointer()
rows = 0
while not out.atEnd():
text = out.readQStringList()
icon = QtGui.QIcon()
out >> icon
newItem = TreeItem(text, icon, parentItem)
newItems.append(newItem)
rows += 1
startRow = beginRow
self.insertRows(beginRow, len(newItems), parent, newItems)
beginRow = startRow
for i in newItems:
idx = self.index(beginRow, 0, parent)
self.setData(idx, i)
beginRow += 1
return True
def getSerializedData(self, indexes):
blob = QtCore.QByteArray()
out = QtCore.QDataStream(blob, QtCore.QIODevice.WriteOnly)
selectedColumnZeroIndexes = [i for i in indexes if i.column() == 0]
for i in selectedColumnZeroIndexes:
p = i.internalPointer()
stringList = [str(k) for k in p._itemData]
out.writeQStringList(stringList)
out << p.icon
return blob
def insertRows( self, row, count, parent , items):
if not parent.isValid():
parentitem = self.root
else:
parentitem = parent.internalPointer()
self.beginInsertRows( parent, row, row+ count-1 )
for i in range(0, count):
item = items[i]
item.setParent(parentitem)
success = parentitem.insertChild(row+i, item)
self.emit(QtCore.SIGNAL("dataChanged(QModelIndex*, QModelIndex*)"))
self.emit(QtCore.SIGNAL("layoutChanged()"))
self.endInsertRows()
self.dataChanged.emit(parent, parent)
return True
def removeRows(self, position, count = 1, parent=QtCore.QModelIndex()):
success = False
if not parent.isValid():
parentitem = self.root
else:
parentitem = parent.internalPointer()
items = parentitem.children[position: position + count]
if items:
self.beginRemoveRows(parent, position, position + count - 1)
success = parentitem.removeChildren(position, count)
self.endRemoveRows()
self.emit(QtCore.SIGNAL("dataChanged(QModelIndex*, QModelIndex*)"))
self.emit(QtCore.SIGNAL("layoutChanged()"))
self.beginResetModel()
self.endResetModel()
return success
def setData(self, index, value, role=QtCore.Qt.EditRole):
if role == QtCore.Qt.EditRole:
if isinstance(value, TreeItem):
item = self.nodeFromIndex(index.parent())
item.children[index.row()]._data = value._data
item.children[index.row()]._itemData = value._itemData
item.children[index.row()].icon = value.icon
self.dataChanged.emit(index, index)
return True
else:
item = self.nodeFromIndex(index.parent())
item.children[index.row()]._itemData[index.column()] = value
self.dataChanged.emit(index, index)
return True
return False
elif role == QtCore.Qt.DisplayRole:
if isinstance(value, TreeItem):
item = self.nodeFromIndex(index.parent())
item.children[index.row()]._itemData = value._data
item.children[index.row()]._itemData = value._itemData
item.children[index.row()].icon = value.icon
self.dataChanged.emit(index, index)
return True
else:
item = self.nodeFromIndex(index.parent())
item.children[index.row()]._itemData[index.column()] = value
self.dataChanged.emit(index, index)
return True
return False
return False
def nodeFromIndex(self, index):
return index.internalPointer() if index.isValid() else self.root
class TreeItem(object):
def __init__(self, data, icon = QtGui.QIcon(), parent=None):
super(TreeItem, self).__init__()
self._data = data
self._itemData = data
self.icon = icon
self.children = []
self.row = 0
self._parent = parent
self._parentid = -1
if parent is not None:
self._parentid = int(self._parent._itemData[1])
def at(self):
if self._parent is not None:
if self in self._parent.children:
index = self._parent.children.index(self)
return index
return 0
def child(self, row):
if row < 0 or row > len(self.children)-1:
return None
child = self.children[row]
return child
def childCount(self):
return len(self.children)
def addChild(self, item):
item.setParent(self)
self.children.append(item)
return True
def columnCount(self):
return len(self._itemData)
def data(self, column):
if column < 0 or column >= self.columnCount():
return None
return self._itemData[column]
def setData(self, column, value):
if column < 0 or column >= self.columnCount():
return None
self._itemData[column] = value
return True
def insertColumns(self, position, columns):
if position < 0 or position >= self.columnCount():
return False
for column in range(0, columns):
self._itemData.insert(position, None)
for i in self.children:
i.insert(position, columns)
def insertChild(self, insertrow, item):
if item in self.children:
return False
if insertrow == -1:
item.setParent(self)
self.children.append(item)
return True
elif len(self.children) > insertrow:
item.setParent(self)
self.children.insert(insertrow, item)
return True
elif len(self.children) <= insertrow:
item.setParent(self)
self.children.append(item)
return True
else:
return False
def insertChildren(self, position, lastposition, data):
if position < 0 or lastposition >= self.columnCount():
return False
for i in range(0, lastposition):
if data in self.children:
return False
data.setParent(self)
self.children.insert(position + i, data)
return True
def addChildren(self, items):
self.children.extend(items)
return True
def removeChild(self, item):
if item in self.children:
self.children.remove(item)
return True
else:
return False
def removeChildren(self, position, count):
if position < 0 or position + count > len(self.children):
return False
removedItems = self.children[position: position + count]
for i in removedItems:
assert(i in removedItems)
self.children.remove(i)
return True
def setParent(self, parent):
self._parent = parent
self._parentid = parent._itemData[1]
def parent(self):
return self._parent
def movedItems(self, row, lastRow):
if (0 < self.childCount() or self.childCount() > lastRow) and row != lastRow:
movedItems = self.children[row : lastRow]
return movedItems
if row == lastRow:
print(self.children)
movedItems = self.children[row]
return [movedItems]
return []
def main():
if QtGui.QApplication.instance() is not None:
app = QtGui.QApplication.instance()
else:
app = QtGui.QApplication([])
items = [TreeItem(["Peru", 1], QtGui.QIcon(), None), \
TreeItem(["Japan", 2], QtGui.QIcon(), None), \
TreeItem(["France", 3], QtGui.QIcon(), None), \
TreeItem(["England", 4], QtGui.QIcon(), None), \
TreeItem(["Germany", 5], QtGui.QIcon(), None), \
TreeItem(["Spain", 6], QtGui.QIcon(), None), \
TreeItem(["Canada", 7], QtGui.QIcon(), None), \
TreeItem(["China", 8], QtGui.QIcon(), None), \
TreeItem(["Egypt", 9], QtGui.QIcon(), None), \
TreeItem(["Estonia", 10], QtGui.QIcon(), None),\
TreeItem(["Finland", 11], QtGui.QIcon(), None),\
TreeItem(["Brazil", 12], QtGui.QIcon(), None),\
TreeItem(["Batwa", 13], QtGui.QIcon(), None),\
TreeItem(["Angola", 14], QtGui.QIcon(), None),\
TreeItem(["Andorra", 15], QtGui.QIcon(), None),\
TreeItem(["Chad", 16], QtGui.QIcon(), None)]
view = TreeView()
model = BuildModel(items, view)
view.setModel(model)
view.show()
sys.exit(QtGui.QApplication.exec_())
if __name__ == "__main__":
main()