這是我的解決方案(完整的代碼在最後),子類QTreeWidget
。我試圖讓一些非常一般的東西適用於很多情況。拖動時,視覺提示仍然存在一個問題。以前的版本沒有在Windows上工作,我希望這個會。它在Linux上絕對正常。
定義類別
樹中的每個項目都有一個類別(字符串),我保存在QtCore.Qt.ToolTipRole
。您還可以繼承QTreeWidgetItem
以具有特定屬性category
。
我們在詞典settings
中定義了所有的類別,並列出了可以放入的類別列表以及要設置的標誌。例如:
default=QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled
drag=QtCore.Qt.ItemIsDragEnabled
drop=QtCore.Qt.ItemIsDropEnabled
settings={
"family":(["root"],default|drag|drop),
"children":(["family"],default|drag)
}
類別「家庭」的每個項目都可以接收拖動,並且只能放在「root」(不可見的根項目)中。 每個類別的「孩子」只能放入「家庭」。
添加項目到樹
的方法addItem(strings,category,parent=None)
用刀尖「類別」和setting
匹配標誌創建QTreeWidgetItem(strings,parent)
。它返回該項目。例如:
dupont=ex.addItem(["Dupont"],"family")
robert=ex.addItem(["Robertsons"],"family")
ex.addItem(["Laura"],"children",dupont)
ex.addItem(["Matt"],"children",robert)
...
拖動的重新實現和Drop
被拖動與self.currentItem()
確定的項目(多選擇不被處理)。該項目可以被刪除的類別列表是okList=self.settings[itemBeingDragged.data(0,role)][0]
。
鼠標下的項目,即「放置目標」,爲self.itemAt(event.pos())
。如果將鼠標放在空白處,放置目標被設置爲根項目。
dragMoveEvent
如果放置目標是okList
(爲下降是否會被接受/忽略視覺提示),我們稱之爲正規dragMoveEvent
。 如果不是,我們必須檢查「下降目標」。在圖像下方,鼠標下的物品是羅伯遜,但真正的下落目標是根物品(參見羅伯遜的行)。爲了解決這個問題,我們檢查它可以拖放放置目標的父項。如果沒有,我們稱event.ignore()
。
剩下的唯一問題是鼠標實際在「Robertsons」上時:拖動事件被接受。視覺提示說,當不在時,將會接受放棄。
dropEvent
除了接受或忽略的下降,這是因爲非常棘手的「下一個下跌目標」,我們總是接受放置,然後修正錯誤。
如果新父母與老父母相同,或者在okList
,我們什麼也不做。否則,我們將拖動的項目放回到舊父項中。
有時候掉落物品將被摺疊,但是這很容易被固定itemBeingDragged.setExpanded()
最後,完整的代碼有兩個例子:
import sys
from PyQt4 import QtCore, QtGui
class CustomTreeWidget(QtGui.QTreeWidget):
def __init__(self,settings, parent=None):
QtGui.QTreeWidget.__init__(self, parent)
#self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.setItemsExpandable(True)
self.setAnimated(True)
self.setDragEnabled(True)
self.setDropIndicatorShown(True)
self.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.settings=settings
root=self.invisibleRootItem()
root.setData(0,QtCore.Qt.ToolTipRole,"root")
def dragMoveEvent(self, event):
role=QtCore.Qt.ToolTipRole
itemToDropIn = self.itemAt(event.pos())
itemBeingDragged=self.currentItem()
okList=self.settings[itemBeingDragged.data(0,role)][0]
if itemToDropIn is None:
itemToDropIn=self.invisibleRootItem()
if itemToDropIn.data(0,role) in okList:
super(CustomTreeWidget, self).dragMoveEvent(event)
return
else:
# possible "next to drop target" case
parent=itemToDropIn.parent()
if parent is None:
parent=self.invisibleRootItem()
if parent.data(0,role) in okList:
super(CustomTreeWidget, self).dragMoveEvent(event)
return
event.ignore()
def dropEvent(self, event):
role=QtCore.Qt.ToolTipRole
#item being dragged
itemBeingDragged=self.currentItem()
okList=self.settings[itemBeingDragged.data(0,role)][0]
#parent before the drag
oldParent=itemBeingDragged.parent()
if oldParent is None:
oldParent=self.invisibleRootItem()
oldIndex=oldParent.indexOfChild(itemBeingDragged)
#accept any drop
super(CustomTreeWidget,self).dropEvent(event)
#look at where itemBeingDragged end up
newParent=itemBeingDragged.parent()
if newParent is None:
newParent=self.invisibleRootItem()
if newParent.data(0,role) in okList:
# drop was ok
return
else:
# drop was not ok, put back the item
newParent.removeChild(itemBeingDragged)
oldParent.insertChild(oldIndex,itemBeingDragged)
def addItem(self,strings,category,parent=None):
if category not in self.settings:
print("unknown categorie" +str(category))
return False
if parent is None:
parent=self.invisibleRootItem()
item=QtGui.QTreeWidgetItem(parent,strings)
item.setData(0,QtCore.Qt.ToolTipRole,category)
item.setExpanded(True)
item.setFlags(self.settings[category][1])
return item
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
default=QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled|QtCore.Qt.ItemIsEditable
drag=QtCore.Qt.ItemIsDragEnabled
drop=QtCore.Qt.ItemIsDropEnabled
#family example
settings={
"family":(["root"],default|drag|drop),
"children":(["family"],default|drag)
}
ex = CustomTreeWidget(settings)
dupont=ex.addItem(["Dupont"],"family")
robert=ex.addItem(["Robertsons"],"family")
smith=ex.addItem(["Smith"],"family")
ex.addItem(["Laura"],"children",dupont)
ex.addItem(["Matt"],"children",dupont)
ex.addItem(["Kim"],"children",robert)
ex.addItem(["Stephanie"],"children",robert)
ex.addItem(["John"],"children",smith)
ex.show()
sys.exit(app.exec_())
#food example: issue with "in between"
settings={
"food":([],default|drop),
"allVegetable":(["food"],default|drag|drop),
"allFruit":(["food"],default|drag|drop),
"fruit":(["allFruit","fruit"],default|drag|drop),
"veggie":(["allVegetable","veggie"],default|drag|drop),
}
ex = CustomTreeWidget(settings)
top=ex.addItem(["Food"],"food")
fruits=ex.addItem(["Fruits"],"allFruit",top)
ex.addItem(["apple"],"fruit",fruits)
ex.addItem(["orange"],"fruit",fruits)
vegetable=ex.addItem(["Vegetables"],"allVegetable",top)
ex.addItem(["carrots"],"veggie",vegetable)
ex.addItem(["lettuce"],"veggie",vegetable)
ex.addItem(["leek"],"veggie",vegetable)
ex.show()
sys.exit(app.exec_())
你提到它可能是通過使用setData完成。你能證明這是怎麼完成的嗎? – JokerMartini
@JokerMartini。我修正了這個例子中的一個錯誤,但我不認爲整體解決方案非常可靠,現在我可能不會推薦它。使用'setData'不會有什麼區別。目前,恐怕我沒有更好的想法,也沒有時間進一步研究。 – ekhumoro
你能幫我解決我的問題嗎?我更新了我的帖子。我有它幾乎工作,但它有一些錯誤http://stackoverflow.com/questions/34133789/controlling-drag-n-drop-disable-enable-of-qtreewidget-items-python?noredirect=1#comment56017728_34133789 – JokerMartini