Issue
I'm trying to add a constraint to the QTreeWidget drag and drop function to prevent the branches from entering another branch in another root.
Here's an example to make things more clear:
I have 4 objects. Lets call them apple, banana, carrot, durian.
The tree looks like this:
isDelicious (Root)
|-- BackgroundObjects (Branch)
|-- Durian
|-- ForgroundObjects (Branch)
|-- Apple
|-- Banana
|-- Carrot
isSmelly (Root)
|-- BackgroundObjects (Branch)
|-- Apple
|-- Carrot
|-- ForgroundObjects (Branch)
|-- Banana
|-- Durian
So, the objects are allowed to be dragged and dropped from BackgroundObjects to ForgroundObjects, and visa versa on the same root, but they are not allowed to be dragged and dropped onto a branch on a different root.
I have tried reimplementing and subclassing dragMoveEvent, dragEnterEvent, and dropEvent, and if I call accept on the event in dragEnterEvent, it'll call dragMoveEvent after (which I expect). However, dropEvent is only called when I drop outside of the QTreeWidget.
What I want to do is check the grandparent of the selected objects before they are moved, and the proposed new grandparent to see if they are the same. If so, then accept the move. Otherwise ignore the move.
I have searched to see if there is any answers, and so far I haven't seen any for what I'm trying to do. Probably the closest would be these two questions from 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)
Solution
Qt does not seem to make this sort of thing very easy.
The best I could come up with was to temporarily reset the item flags during the drag-enter and drag-move events. The example below calculates the current top-level item dynamically in order to contrain drag and drop. But it could also be done by using setData()
to add an identifier to each item.
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, event):
self._drag_event(event, 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, 300)
window.show()
sys.exit(app.exec_())
Answered By - ekhumoro
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.