Shadow
Shadow

Reputation: 65

Drag and Drop TreeWidget

I'm trying to make a treewidget that can change children between parents with the drag and drop method, but a children can never be turned in a parent. Right now this is my class:

class Tree(QTreeWidget):
def __init__(self, parent=QWidget):
    QTreeWidget.__init__(self,parent)

    
    self.setAcceptDrops(True)

    self.setDragEnabled(True)
    self.setDropIndicatorShown(True)
    self.setEditTriggers(QAbstractItemView.AllEditTriggers)
    self.setDragDropMode(QAbstractItemView.InternalMove)
    self.setSelectionMode(QAbstractItemView.ExtendedSelection)

def dragMoveEvent(self, event):
    event.setDropAction(QtCore.Qt.CopyAction)
    event.accept()
    
        
def dropEvent(self, event):
    event.setDropAction(QtCore.Qt.MoveAction)
    event.accept()

    target_item= self.itemAt(event.pos())
    if target_item:
        object_name= target_item.text(0)
      
        if self.currentItem().parent():
            parent_item= self.currentItem().parent()
            parent_name = parent_item.text(0)
            print("Número de Serie:", self.currentItem().text(0))
            print("Solto no:",object_name)
            print("Codigo de barras:",parent_name)
        else:
            print("Objeto de destino: ", object_name)
    else:
        print("Solto fora de qualquer")

So i upgrade my code and i tried to print were the child is droped and its all working but the same problem still stand´s the child when is dragged it always disapear from de treewidget and i dont know why.

Every help is welcome, thanks

Upvotes: 1

Views: 136

Answers (1)

Hoodlum
Hoodlum

Reputation: 1003

Assuming we have the following tree structure, with only two levels of data:

root
├─ A 
│  ├─ A1
│  └─ A2
├─ B
│  └─ B1
└─ C

Our task is as follows :

  1. move children between parents
  2. children may never become parents

Furthermore assuming :

  • parents cannot become children
  • parents do not need to be moved

We can say :

  1. parents must allow drops, but cannot be dragged
  2. children can be dragged, but must not accept drops
  3. the root cannot be dragged and must not accept drops

This is actually possible to achieve without modifying the default QTreeWidget's behavior - it's a question of setting the correct flags on the QTreeWidgetItems.

from PyQt5 import QtCore, QtWidgets

class Tree(QtWidgets.QTreeWidget):
    def __init__(self, parent: QtWidgets.QWidget | None = None):
        super().__init__(parent)

        self.setHeaderHidden(True)
        self.setAcceptDrops(True)
        self.setDragEnabled(True)
        self.setDropIndicatorShown(True)

        self.setEditTriggers(
            QtWidgets.QAbstractItemView.EditTrigger.AllEditTriggers)
        self.setDragDropMode(
            QtWidgets.QAbstractItemView.DragDropMode.InternalMove)
        self.setSelectionMode(
            QtWidgets.QAbstractItemView.SelectionMode.ExtendedSelection)
        
        # create the `root` item, which is "fixed" and cannot be modified by user
        self._root = QtWidgets.QTreeWidgetItem(self, ['.'])
        self._root.setFlags(
            # the root cannot be dragged and must not accept drops
            ~(QtCore.Qt.ItemFlag.ItemIsDropEnabled | QtCore.Qt.ItemFlag.ItemIsDragEnabled)
            )
        self.addTopLevelItem(self._root)
        # hide the root from user
        self.setRootIndex(self.indexFromItem(self._root, 0))

    def add_parent(self, parent_name:str, children_names:list[str]) : 
        '''takes a `parent` string and it's `child` strings to populate the widget'''
        # create the parent item
        parent_item = QtWidgets.QTreeWidgetItem(self._root, [parent_name])
        parent_item.setFlags(
            # parents must allow drops, but cannot be dragged
            (parent_item.flags() | QtCore.Qt.ItemFlag.ItemIsDropEnabled) & ~QtCore.Qt.ItemFlag.ItemIsDragEnabled
            )
        self._root.addChild(parent_item)
        
        # create the children items
        for child_name in children_names:
            child_item = QtWidgets.QTreeWidgetItem(parent_item, [child_name])
            child_item.setFlags(
                # children can be dragged, but must not accept drops
                (child_item.flags() | QtCore.Qt.ItemFlag.ItemIsDragEnabled) & ~QtCore.Qt.ItemFlag.ItemIsDropEnabled
                )
            parent_item.addChild(child_item)

    def populate(self, data):
        '''populate the widget from an initial dataset'''
        for parent_name, children_names in data:
            self.add_parent(parent_name, children_names)

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)

    # create tree widget
    tw = Tree()

    # populate with fake data :
    fakedata = [
        ('A', [ 'A1', 'A2' ]),
        ('B', [ 'B1' ]),
        ('C', []),
    ]
    tw.populate(fakedata)

    # run app
    tw.show()
    app.exec()

Upvotes: 2

Related Questions