Search code examples
pythondrag-and-dropqtablewidgetpyside6

PySide6 - QTableWidget - Drag and Drop - CellWidget - lost anywhere --> Is something wrong with my question?


I need to drag and drop (move) CellWidget in a QtableWidget.

I can move them, but if i drop a CellWidget on a cell occupied by an other one, i deleted the previous. Example : (row, col) B(0, 1) move to (1, 2)
C(0, 2) move to (0, 1) B is deleted.

More : A cellWidget can't be put on an old position.

Here is my minimal code.

from PySide6.QtWidgets import QApplication, QHBoxLayout, QWidget, QPushButton, QTableWidget
from PySide6.QtCore import Qt, QMimeData
from PySide6.QtGui import QDrag


class DragButton(QPushButton):
    def mouseMoveEvent(self, e):
        if e.buttons() == Qt.LeftButton:
            drag = QDrag(self)
            mime = QMimeData()
            drag.setMimeData(mime)
            drag.exec(Qt.MoveAction)


class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setAcceptDrops(True)
        self.nb_col = 5
        self.nb_row = 5
        self.tbl = QTableWidget(self.nb_row, self.nb_col)
        self.tbl.setHorizontalHeaderLabels([str(x) for x in range(self.nb_col)])
        self.tbl.setVerticalHeaderLabels([str(x) for x in range(self.nb_row)])

        self.layout = QHBoxLayout()
        self.layout.addWidget(self.tbl)

        nom = ['A', 'B', 'C', 'D']
        for l in range(len(nom)):
            new_btn = DragButton(nom[l])
            new_btn2 = DragButton(nom[l] + 'j')
            self.tbl.setCellWidget(0, l, new_btn)
            self.tbl.setCellWidget(l, 1, new_btn2)
        self.setLayout(self.layout)

    def dragEnterEvent(self, e):
        e.accept()

    def dropEvent(self, e):
        position = e.position()
        widget = e.source()
        row = self.tbl.rowAt(position.y() - self.tbl.horizontalHeader().height() - self.tbl.y())
        column = self.tbl.columnAt(position.x() - self.tbl.x())
        self.tbl.setCellWidget(row, column, widget)
        e.accept()


if __name__ == "__main__":
    app = QApplication([])
    w = Window()
    w.showMaximized()
    app.exec()

Thanks for your help. Christophe


Solution

  • I might be a little late to the party ^^ but I just had the same problem on a QTableWidget, the trick is to create a temporary QWidget and QHBoxLayout in which you place your buttons or whatever. That way, those temporary widgets will get destroyed instead.

    from PySide6.QtWidgets import QApplication, QHBoxLayout, QWidget, QPushButton, QTableWidget
    from PySide6.QtCore import Qt, QMimeData
    from PySide6.QtGui import QDrag
    
    
    class DragButton(QPushButton):
        def mouseMoveEvent(self, e):
            if e.buttons() == Qt.LeftButton:
                drag = QDrag(self)
                mime = QMimeData()
                drag.setMimeData(mime)
                drag.exec(Qt.MoveAction)
    
    
    class Window(QWidget):
        def __init__(self):
            super().__init__()
            self.setAcceptDrops(True)
            self.nb_col = 5
            self.nb_row = 5
            self.tbl = QTableWidget(self.nb_row, self.nb_col)
            self.tbl.setHorizontalHeaderLabels([str(x) for x in range(self.nb_col)])
            self.tbl.setVerticalHeaderLabels([str(x) for x in range(self.nb_row)])
    
            self.layout = QHBoxLayout()
            self.layout.addWidget(self.tbl)
    
            nom = ['A', 'B', 'C', 'D']
            for l in range(len(nom)):
                new_btn = DragButton(nom[l])
                new_btn2 = DragButton(nom[l] + 'j')
                self.set_cell_widget(0, l, new_btn)
                self.set_cell_widget(l, 1, new_btn2)
            self.setLayout(self.layout)
    
        def set_cell_widget(self, row, column, widget):
            container_widget = QWidget()
            container_layout = QHBoxLayout()
            container_layout.setContentsMargins(0, 0, 0, 0)
            container_layout.setSpacing(0)
            container_widget.setLayout(container_layout)
            container_layout.addWidget(widget)
    
            self.tbl.setCellWidget(row, column, container_widget)
    
        def dragEnterEvent(self, e):
            e.accept()
    
        def dropEvent(self, e):
            position = e.position()
            widget = e.source()
            row = self.tbl.rowAt(position.y() - self.tbl.horizontalHeader().height() - self.tbl.y())
            column = self.tbl.columnAt(position.x() - self.tbl.x())
            self.set_cell_widget(row, column, widget)
            e.accept()
    
    
    if __name__ == "__main__":
        app = QApplication([])
        w = Window()
        w.showMaximized()
        app.exec()
    

    Hope this helps!