pyqt5qtablewidgetqtablewidgetitem

QTableWidget Drag/Drop rows does not change their location when accessing by index


I have a QTableWidget, that allows moving of rows by dragging and dropping.

The initial table looks as follows:

enter image description here

If clicking on the button, it prints all the values as they are in the table.

Now after dragging row 2 to the the position of row 4, the output does not change.

enter image description here

Output:

Item in row 0, col 0 has value Name
Item in row 0, col 1 has value City
Item in row 1, col 0 has value Aloysius
Item in row 1, col 1 has value Indore
Item in row 2, col 0 has value Alan
Item in row 2, col 1 has value Bhopal
Item in row 3, col 0 has value Arnavi
Item in row 3, col 1 has value Mandsaur

The expected output:

Item in row 0, col 0 has value Name
Item in row 0, col 1 has value City
Item in row 1, col 0 has value Alan
Item in row 1, col 1 has value Bhopal
Item in row 2, col 0 has value Arnavi
Item in row 2, col 1 has value Mandsaur
Item in row 3, col 0 has value Aloysius
Item in row 3, col 1 has value Indore

Why is this the case and how can I print all the rows by their actual, new location?

Example:

import sys
import PyQt5

from PyQt5.QtCore import *#QPointF, QRectF
from PyQt5.QtGui import *#QPainterPath, QPolygonF, QBrush,QPen,QFont,QColor, QTransform
from PyQt5.QtWidgets import *#QApplication, QGraphicsScene, QGraphicsView, QGraphicsSimpleTextIt


class App(QWidget):
    def __init__(self):
        super().__init__()
        self.title = 'PyQt5 - QTableWidget'
        self.left = 0
        self.top = 0
        self.width = 300
        self.height = 200

        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        self.tableWidget = QTableWidget()
        self.tableWidget.verticalHeader().setSectionsMovable(True)
        self.tableWidget.verticalHeader().setDragEnabled(True)
        self.tableWidget.verticalHeader().setDragDropMode(QAbstractItemView.InternalMove)

        # Row count
        self.tableWidget.setRowCount(4)

        # Column count
        self.tableWidget.setColumnCount(2)

        self.tableWidget.setItem(0, 0, QTableWidgetItem("Name"))
        self.tableWidget.setItem(0, 1, QTableWidgetItem("City"))
        self.tableWidget.setItem(1, 0, QTableWidgetItem("Aloysius"))
        self.tableWidget.setItem(1, 1, QTableWidgetItem("Indore"))
        self.tableWidget.setItem(2, 0, QTableWidgetItem("Alan"))
        self.tableWidget.setItem(2, 1, QTableWidgetItem("Bhopal"))
        self.tableWidget.setItem(3, 0, QTableWidgetItem("Arnavi"))
        self.tableWidget.setItem(3, 1, QTableWidgetItem("Mandsaur"))

        # Table will fit the screen horizontally
        self.tableWidget.horizontalHeader().setStretchLastSection(True)
        self.tableWidget.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch)

        self.layout = QVBoxLayout()
        self.layout.addWidget(self.tableWidget)

        self.button = QPushButton("Print all the values in the table, in the right order!")

        def get_all_the_values_in_the_table():
            #row_counter = 0
            print("  ")
            print("  ")
            for row in range(self.tableWidget.rowCount()):

                for col in range(self.tableWidget.columnCount()):
                    print("Item in row "+str(row)+", col "+str(col)+" has value "+str(self.tableWidget.item(row,col).text()))

        self.button.clicked.connect(lambda: get_all_the_values_in_the_table())
        self.layout.addWidget(self.button)

        self.setLayout(self.layout)

        # Show window
        self.show()






if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())

Edit 1:

Thought that perhaps the item has the same location in the table, but a different index. Yet trying to solve this by getting the index does not work neither. Where's the trick?

print("Item in row "+str(row_)+", col "+str(col_)+" has value "+str(self.tableWidget.itemFromIndex(self.tableWidget.indexFromItem(self.tableWidget.item(row_,col_))).text()))

Solution

  • Moving header sections does not change the model layout, only the visual representation: if you move the first header to another place, the first row in the model will still be the previous one. In fact, if you closely look at the vertical header, the labels (which by default indicate the model index) are moved also.

    If you want to print the model based on the header layout, you need to call logicalIndex():

            def get_all_the_values_in_the_table():
                print()
                print()
                header = self.tableWidget.verticalHeader()
                for i in range(self.tableWidget.rowCount()):
                    # get the logical index based on the visual index
                    row = header.logicalIndex(i)
                    for col in range(self.tableWidget.columnCount()):
                        print("Item in row {}, col {} has value {}".format(
                            i, col, self.tableWidget.item(row,col).text()))
    
            self.button.clicked.connect(get_all_the_values_in_the_table)
    

    Note that: