OiO.lk Community platform!

Oio.lk is an excellent forum for developers, providing a wide range of resources, discussions, and support for those in the developer community. Join oio.lk today to connect with like-minded professionals, share insights, and stay updated on the latest trends and technologies in the development field.
  You need to log in or register to access the solved answers to this problem.
  • You have reached the maximum number of guest views allowed
  • Please register below to remove this limitation

Odd behaviour dragging and dropping in PyQ6 QTableWidget

  • Thread starter Thread starter backseat
  • Start date Start date
B

backseat

Guest
I'm seeing an odd behaviour with dragging and dropping a row within a QTableWidget. The code below is taken from this question.

When a row is dragged to between two rows, the drop indicator line can either be right at the bottom of the upper row or right at the top of the lower row. Seeing the drop indicator between two rows implies that the dropped row will be between those two rows, but that's not always the case.

See the image below. On the left, if the mouse button is released the dropped row will be above the Red row. On the right, it will be below the Red row.

enter image description here

As the dragged row is moved up and down, the drop indicator moves as described above. The difference is very subtle, maybe just a couple of pixels.

Is there a way to stop this and simply have one position for the drop indicator that represents where the dropped row will appear?

Code:

Code:
import sys

from PyQt6.QtWidgets import (
    QTableWidget,
    QAbstractItemView,
    QTableWidgetItem,
    QWidget,
    QHBoxLayout,
    QApplication,
)


class TableWidgetDragRows(QTableWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.setDragEnabled(True)
        self.setAcceptDrops(True)
        self.viewport().setAcceptDrops(True)
        self.setDragDropOverwriteMode(False)
        self.setDropIndicatorShown(True)

        self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
        self.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
        self.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)

    def dropEvent(self, event):
        if event.source() == self:
            rows = set([mi.row() for mi in self.selectedIndexes()])
            targetRow = self.indexAt(event.position().toPoint()).row()
            rows.discard(targetRow)
            rows = sorted(rows)
            if not rows:
                return
            if targetRow == -1:
                targetRow = self.rowCount()
            for _ in range(len(rows)):
                self.insertRow(targetRow)
            rowMapping = dict()  # Src row to target row.
            for idx, row in enumerate(rows):
                if row < targetRow:
                    rowMapping[row] = targetRow + idx
                else:
                    rowMapping[row + len(rows)] = targetRow + idx
            colCount = self.columnCount()
            for srcRow, tgtRow in sorted(rowMapping.items()):
                for col in range(0, colCount):
                    self.setItem(tgtRow, col, self.takeItem(srcRow, col))
            for row in reversed(sorted(rowMapping.keys())):
                self.removeRow(row)
            event.accept()
            return


class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()

        layout = QHBoxLayout()
        self.setLayout(layout)

        self.table_widget = TableWidgetDragRows()
        layout.addWidget(self.table_widget)

        # setup table widget
        self.table_widget.setColumnCount(2)
        self.table_widget.setHorizontalHeaderLabels(["Type", "Name"])

        items = [
            ("Red", "Toyota"),
            ("Blue", "RV"),
            ("Green", "Beetle"),
            ("Silver", "Chevy"),
            ("Black", "BMW"),
        ]
        self.table_widget.setRowCount(len(items))
        for i, (color, model) in enumerate(items):
            self.table_widget.setItem(i, 0, QTableWidgetItem(color))
            self.table_widget.setItem(i, 1, QTableWidgetItem(model))

        self.resize(400, 400)
        self.show()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Window()
    sys.exit(app.exec())
<p>I'm seeing an odd behaviour with dragging and dropping a row within a QTableWidget. The code below is taken from <a href="https://stackoverflow.com/questions/26227885/drag-and-drop-rows-within-qtablewidget">this question</a>.</p>
<p>When a row is dragged to between two rows, the drop indicator line can either be right at the bottom of the upper row or right at the top of the lower row. Seeing the drop indicator between two rows implies that the dropped row will be between those two rows, but that's not always the case.</p>
<p>See the image below. On the left, if the mouse button is released the dropped row will be above the Red row. On the right, it will be below the Red row.</p>
<p><a href="https://i.sstatic.net/jttdzR1F.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/jttdzR1F.png" alt="enter image description here" /></a></p>
<p>As the dragged row is moved up and down, the drop indicator moves as described above. The difference is very subtle, maybe just a couple of pixels.</p>
<p>Is there a way to stop this and simply have one position for the drop indicator that represents where the dropped row will appear?</p>
<p>Code:</p>
<pre><code>import sys

from PyQt6.QtWidgets import (
QTableWidget,
QAbstractItemView,
QTableWidgetItem,
QWidget,
QHBoxLayout,
QApplication,
)


class TableWidgetDragRows(QTableWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.setDragEnabled(True)
self.setAcceptDrops(True)
self.viewport().setAcceptDrops(True)
self.setDragDropOverwriteMode(False)
self.setDropIndicatorShown(True)

self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
self.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)

def dropEvent(self, event):
if event.source() == self:
rows = set([mi.row() for mi in self.selectedIndexes()])
targetRow = self.indexAt(event.position().toPoint()).row()
rows.discard(targetRow)
rows = sorted(rows)
if not rows:
return
if targetRow == -1:
targetRow = self.rowCount()
for _ in range(len(rows)):
self.insertRow(targetRow)
rowMapping = dict() # Src row to target row.
for idx, row in enumerate(rows):
if row < targetRow:
rowMapping[row] = targetRow + idx
else:
rowMapping[row + len(rows)] = targetRow + idx
colCount = self.columnCount()
for srcRow, tgtRow in sorted(rowMapping.items()):
for col in range(0, colCount):
self.setItem(tgtRow, col, self.takeItem(srcRow, col))
for row in reversed(sorted(rowMapping.keys())):
self.removeRow(row)
event.accept()
return


class Window(QWidget):
def __init__(self):
super(Window, self).__init__()

layout = QHBoxLayout()
self.setLayout(layout)

self.table_widget = TableWidgetDragRows()
layout.addWidget(self.table_widget)

# setup table widget
self.table_widget.setColumnCount(2)
self.table_widget.setHorizontalHeaderLabels(["Type", "Name"])

items = [
("Red", "Toyota"),
("Blue", "RV"),
("Green", "Beetle"),
("Silver", "Chevy"),
("Black", "BMW"),
]
self.table_widget.setRowCount(len(items))
for i, (color, model) in enumerate(items):
self.table_widget.setItem(i, 0, QTableWidgetItem(color))
self.table_widget.setItem(i, 1, QTableWidgetItem(model))

self.resize(400, 400)
self.show()


if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
sys.exit(app.exec())
</code></pre>
 

Latest posts

Top