我正在尝试向QTreeWidget拖放功能添加约束,以防止分支进入另一个根中的另一个分支.
这是一个让事情更清晰的例子:
我有4个物体.让我们称他们为苹果,香蕉,胡萝卜,榴莲.
树看起来像这样:
isDelicIoUs (Root)
|-- BackgroundObjects (Branch)
|-- Durian
|-- ForgroundObjects (Branch)
|-- Apple
|-- Banana
|-- Carrot
isSmelly (Root)
|-- BackgroundObjects (Branch)
|-- Apple
|-- Carrot
|-- ForgroundObjects (Branch)
|-- Banana
|-- Durian
因此,允许将对象从BackgroundObjects拖放到ForgroundObjects,反之亦然,但是不允许将它们拖放到不同根目录上的分支上.
我已经尝试重新实现和子类化dragMoveEvent,dragEnterEvent和dropEvent,如果我在dragEnterEvent中对事件调用accept,它将调用dragMoveEvent(我期望).但是,只有当我退出QTreeWidget时才会调用dropEvent.
我想要做的是在移动之前检查所选对象的祖父母,以及建议的新祖父母以查看它们是否相同.如果是,那么接受此举.否则忽略此举.
我已经搜索过是否有任何答案,到目前为止我还没有看到任何我正在尝试做的事情.可能最接近的是来自Stack Overflow的这两个问题:
https://stackoverflow.com/questions/17134289/managing-drag-and-drop-within-qtreewidgets-in-pyside
qt: QTreeView – limit drag and drop to only happen within a particlar grandparent (ancestor)
最佳答案
Qt似乎没有让这种事情变得非常容易.
我能想到的最好的方法是在拖动输入和拖动移动事件期间暂时重置项目标志.下面的示例动态计算当前顶级项目以禁止拖放.但也可以通过使用setData()为每个项添加标识符来完成.
from PyQt4 import QtCore,QtGui
class TreeWidget(QtGui.QTreeWidget):
def __init__(self,parent=None):
QtGui.QTreeWidget.__init__(self,parent)
self.setDragDropMode(self.InternalMove)
self.setDragEnabled(True)
self.setDropIndicatorShown(True)
self._dragroot = self.itemRootIndex()
def itemRootIndex(self,item=None):
root = self.invisibleRootItem()
while item is not None:
item = item.parent()
if item is not None:
root = item
return QtCore.QPersistentModelIndex(
self.indexFromItem(root))
def startDrag(self,actions):
items = self.selectedItems()
self._dragroot = self.itemRootIndex(items and items[0])
QtGui.QTreeWidget.startDrag(self,actions)
def dragEnterEvent(self,event):
self._drag_event(event,True)
def dragMoveEvent(self,False)
def _drag_event(self,event,enter=True):
items = []
disable = False
item = self.itemAt(event.pos())
if item is not None:
disable = self._dragroot != self.itemRootIndex(item)
if not disable:
rect = self.visualItemRect(item)
if event.pos().x() < rect.x():
disable = True
if disable:
for item in item,item.parent():
if item is not None:
flags = item.flags()
item.setFlags(flags & ~QtCore.Qt.ItemIsDropEnabled)
items.append((item,flags))
if enter:
QtGui.QTreeWidget.dragEnterEvent(self,event)
else:
QtGui.QTreeWidget.dragMoveEvent(self,event)
for item,flags in items:
item.setFlags(flags)
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.tree = TreeWidget(self)
self.tree.header().hide()
def add(root,*labels):
item = QtGui.QTreeWidgetItem(self.tree,[root])
item.setFlags(item.flags() &
~(QtCore.Qt.ItemIsDragEnabled |
QtCore.Qt.ItemIsDropEnabled))
for index,title in enumerate(
('BackgroundObjects','ForegroundObjects')):
subitem = QtGui.QTreeWidgetItem(item,[title])
subitem.setFlags(
subitem.flags() & ~QtCore.Qt.ItemIsDragEnabled)
for text in labels[index].split():
child = QtGui.QTreeWidgetItem(subitem,[text])
child.setFlags(
child.flags() & ~QtCore.Qt.ItemIsDropEnabled)
add('isDelicIoUs','Durian','Apple Banana Carrot')
add('isSmelly','Apple Carrot','Banana Durian')
root = self.tree.invisibleRootItem()
root.setFlags(root.flags() & ~QtCore.Qt.ItemIsDropEnabled)
self.tree.expandAll()
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.tree)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500,300,300)
window.show()
sys.exit(app.exec_())