python – Is it possible to retrieve the content of cells in a PyQt5 QTableWidget object and inject it into other cells?

I would like to modify a QTableWidget table, taking whole rows and moving them in place of other rows (reversing the position of the rows, in a way). All the attempts I made did not work. I give a minimal example, below, to illustrate what I want to do. The idea is to take the contents of each cell in the row and inject it into another row. In this example the interpreter crashes with the message:

Segmentation fault (core dumped)

Here is the minimum example code:

import sys
from PyQt5.QtWidgets import * 
from functools import partial                    
   
#Main Window
class App(QWidget):
    def __init__(self):
        super().__init__()
        self.title="PyQt5 - QTableWidget"
        self.left = 0
        self.top = 0
        self.width = 300
        self.height = 170
   
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
   
        self.createTable()
   
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.tableWidget)
        self.setLayout(self.layout)
   
        #Show window
        self.show()
   
    #Create table
    def createTable(self):
        self.tableWidget = QTableWidget()
  
        #Row count
        self.tableWidget.setRowCount(3) 
  
        #Column count
        self.tableWidget.setColumnCount(2)  

        #cell 0 - 0
        self.tableWidget.setItem(0,0, QTableWidgetItem("0 - 0"))
        
        #cell 0 - 1
        widget = QWidget()
        layout = QVBoxLayout()
        brick_name_button = QPushButton("0 - 1")
        brick_name_button.clicked.connect(partial(self.print_mssg, "0 - 1"))
        layout.addWidget(brick_name_button)
        widget.setLayout(layout)
        self.tableWidget.setCellWidget(0, 1, widget)

        #cell 1 - 0
        self.tableWidget.setItem(1, 0, QTableWidgetItem("1 - 0"))
        
        #cell 1 - 1
        widget = QWidget()
        layout = QVBoxLayout()
        brick_name_button = QPushButton("1 - 1")
        brick_name_button.clicked.connect(partial(self.print_mssg, "1 - 1"))
        layout.addWidget(brick_name_button)
        widget.setLayout(layout)
        self.tableWidget.setCellWidget(1, 1, widget)

        #cell 2 - 1
        widget = QWidget()
        layout = QVBoxLayout()
        brick_name_button = QPushButton("Invert raw 0 and 1")
        brick_name_button.clicked.connect(self.invert)
        layout.addWidget(brick_name_button)
        widget.setLayout(layout)
        self.tableWidget.setCellWidget(2, 1, widget)

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

    def print_mssg(self, mssg):
        print('n' + mssg + 'n')

    def invert(self):
        widget_to_move_0 = self.tableWidget.cellWidget(0, 1)
        item_to_move_0 = self.tableWidget.takeItem(0, 0)
        widget_to_move_1 = self.tableWidget.cellWidget(1, 1)
        item_to_move_1 = self.tableWidget.takeItem(1, 0)
        self.tableWidget.setCellWidget(1, 1, widget_to_move_0)
        self.tableWidget.setItem(1, 0, item_to_move_0)
        self.tableWidget.setCellWidget(0, 1, widget_to_move_1)
        self.tableWidget.setItem(0, 0, item_to_move_1)
        self.show()
   
if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())

EDIT1:
The issue seems to be really just related to the QWidget/QPushButton objects because it works fine if we only change the QTableWidgetItem objects … For example by taking the snipet code above and only commenting the lines with cellWidget() and setCellWidget(), in the invert method, it works fine!:

def invert(self):
    #widget_to_move_0 = self.tableWidget.cellWidget(0, 1)
    item_to_move_0 = self.tableWidget.takeItem(0, 0)
    #widget_to_move_1 = self.tableWidget.cellWidget(1, 1)
    item_to_move_1 = self.tableWidget.takeItem(1, 0)
    #self.tableWidget.setCellWidget(1, 1, widget_to_move_0)
    self.tableWidget.setItem(1, 0, item_to_move_0)
    #self.tableWidget.setCellWidget(0, 1, widget_to_move_1)
    self.tableWidget.setItem(0, 0, item_to_move_1)
    #self.show()

EDIT2:
In fact it seems that it works (for a reason I don’t know), only if we create a new widget object (and not using the previous widget object in the cell). Indeed, the small code, just to illustrate, below works perfectly:

import sys
from PyQt5.QtWidgets import * 
from functools import partial                    
   
#Main Window
class App(QWidget):
    STATE = True
    def __init__(self):
        super().__init__()
        self.title="PyQt5 - QTableWidget"
        self.left = 0
        self.top = 0
        self.width = 300
        self.height = 170
   
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
   
        self.createTable()
   
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.tableWidget)
        self.setLayout(self.layout)
   
        #Show window
        self.show()
   
    #Create table
    def createTable(self):
        self.tableWidget = QTableWidget()
  
        #Row count
        self.tableWidget.setRowCount(3) 
  
        #Column count
        self.tableWidget.setColumnCount(2)  

        #cell 0 - 0
        self.tableWidget.setItem(0, 0, QTableWidgetItem("0 - 0"))
        
        #cell 0 - 1
        widget = QWidget()
        layout = QVBoxLayout()
        brick_name_button = QPushButton("0 - 1")
        brick_name_button.clicked.connect(partial(self.print_mssg, "0 - 1"))
        layout.addWidget(brick_name_button)
        widget.setLayout(layout)
        self.tableWidget.setCellWidget(0, 1, widget)

        #cell 1 - 0
        self.tableWidget.setItem(1, 0, QTableWidgetItem("1 - 0"))
        
        #cell 1 - 1
        widget = QWidget()
        layout = QVBoxLayout()
        brick_name_button = QPushButton("1 - 1")
        brick_name_button.clicked.connect(partial(self.print_mssg, "1 - 1"))
        layout.addWidget(brick_name_button)
        widget.setLayout(layout)
        self.tableWidget.setCellWidget(1, 1, widget)

        #cell 2 - 1
        widget = QWidget()
        layout = QVBoxLayout()
        brick_name_button = QPushButton("Invert raw 0 and 1")
        brick_name_button.clicked.connect(self.invert)
        layout.addWidget(brick_name_button)
        widget.setLayout(layout)
        self.tableWidget.setCellWidget(2, 1, widget)

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

    def print_mssg(self, mssg):
        print('n' + mssg + 'n')

    def invert(self):
        item_to_move_0 = self.tableWidget.takeItem(0, 0)
        item_to_move_1 = self.tableWidget.takeItem(1, 0)

        #change raw 1
        widget = QWidget()
        layout = QVBoxLayout()
        if self.STATE is True:
            push_button_name = "0 - 1"
        else:
            push_button_name = "1 - 1"
        brick_name_button = QPushButton(push_button_name)
        brick_name_button.clicked.connect(partial(self.print_mssg, push_button_name))
        layout.addWidget(brick_name_button)
        widget.setLayout(layout)
        self.tableWidget.setCellWidget(1, 1, widget)
        self.tableWidget.setItem(1, 0, item_to_move_0)

        #change raw 0
        widget = QWidget()
        layout = QVBoxLayout()
        if self.STATE is True:
            push_button_name = "1 - 1"
        else:
            push_button_name = "0 - 1"
        brick_name_button = QPushButton(push_button_name)
        brick_name_button.clicked.connect(partial(self.print_mssg, push_button_name))
        layout.addWidget(brick_name_button)
        widget.setLayout(layout)
        self.tableWidget.setCellWidget(0, 1, widget)
        self.tableWidget.setItem(0, 0, item_to_move_1)
        self.STATE = not self.STATE
if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())

Leave a Comment