python-3.xpyqt5qtablewidgetqgroupbox

How to add a table in a QGroupBox, PyQt5


I'm pretty new to PyQt5 and still struggle. I tried the whole day adding rows and columns in a QGroupBox. The only solution I found was adding a QTableWidget, the problem with that is the widget size.

For now I only wanna have the header of this table, the user can then add rows to this table. [ID, Name]

I want to have the cells, but not the 'window' that comes with it. If I try to align it in the center of the QGroupBox it aligns the whole thing in the center, not the columns. (the columns are in the top left corner of the widget, see picture)

How it currently looks

That's the code snippet which creates the table:

Create the box

    self.groupbox1 = QGroupBox('Data')
    self.groupbox1.setMaximumWidth(350)
    self.groupbox1.setMaximumHeight(400)
    self.layout = QVBoxLayout()
    self.groupbox1.setLayout(self.layout)
    self.layout.addWidget(self.groupbox1)

Create a QTableWidget

    self.table_widget = QTableWidget()
    self.table_widget.setColumnCount(2)
    self.table_widget.setHorizontalHeaderLabels(['ID', 'Data'])
    self.table_widget.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
    self.table_widget.resizeColumnsToContents()
    self.table_widget.setMaximumSize(200, 200)
    self.upper_layout.setAlignment(Qt.AlignCenter)

    self.upper_layout.addWidget(self.table_widget)

The only thing I found was .resizeColumnsToContents and .setSectionResizeMode which is only adjusting the columns depending on what will be in there.

By setting the maximum size of the table widget I made it smaller but that's not quite what I want. If there is more data added to the table the user has to scroll because the widget isn't big enough. Isn't there anything that is adjusting that automatically?

Or is there another solution to the problem? The button where the user can choose which data to be displayed (which row will be added) should be directly under the rows in the center and move automatically up/down if rows are deleted/added. This is also not possible if I have to have a fixed size of the QTableWidget.

Hope I explained it well enough. I'm thankful for every answer that could bring me closer to the solution. :) (Yes, I had a look at all related posts on Stack Overflow already and probably every other page, didn't solve my problem unfortunately)


Solution

  • This is a modified version of Francesca Mazzeo answer. It uses a subclassed QTableWidget with a reimplemented resizeEvent() method to get the "grow with added rows" behaviour.

    There's one weird thing that I don't understand: Adding the first row doesn't trigger resizeEvent(). Subsequent rows do.

    I'm posting this any way in the hope someone knows how to fix this.

    import sys
    from PyQt5.QtWidgets import QApplication, QWidget, QGroupBox, QVBoxLayout, QTableWidget, QPushButton, QTableWidgetItem, QHeaderView, QMessageBox
    
    
    # class ExpandingTableWidget(QTableWidget):
    #    def resizeEvent(self, event):
    #        super().resizeEvent(event)
    #        height = self.horizontalHeader().height()
    #        row_count = self.verticalHeader().count()
    #        for i in range(row_count):
    #            height += self.verticalHeader().sectionSize(i)
    #
    #        self.setMaximumHeight(height + 2)
    
    
    class TestApp(QWidget):
        def __init__(self):
            super().__init__()
            self.init_ui()
    
        def init_ui(self):
            self.setWindowTitle('Test Application')
            self.setGeometry(200, 200, 400, 400)
    
            # Create a QVBoxLayout for the main window
            self.layout = QVBoxLayout(self)  # 'self' refers to the main window
            self.groupbox1 = QGroupBox('Data')
            self.groupbox_layout = QVBoxLayout()
            self.groupbox1.setLayout(self.groupbox_layout)
    
            # Create a QTableWidget
            self.table_widget = ExpandingTableWidget()
            self.table_widget.setColumnCount(2)
            self.table_widget.setHorizontalHeaderLabels(['ID', 'Name'])
            self.table_widget.verticalHeader().setVisible(False)
            self.table_widget.setShowGrid(False)
            self.table_widget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
    
            # Create a QPushButton to add rows to the QTableWidget
            self.add_row_button = QPushButton('Add Row')
            self.add_row_button.clicked.connect(self.add_row)
    
            # Add the QTableWidget and QPushButton to the QGroupBox layout
            self.groupbox_layout.addWidget(self.table_widget, stretch=1)
            self.groupbox_layout.addStretch(0)
            self.groupbox_layout.addWidget(self.add_row_button)
    
            # Add the QGroupBox to the main QVBoxLayout of the main window
            self.layout.addWidget(self.groupbox1)
    
        def add_row(self):
            # Add a new row to the QTableWidget when the button is clicked
            row_position = self.table_widget.rowCount()
            self.table_widget.insertRow(row_position)
            self.table_widget.setItem(
                row_position, 0, QTableWidgetItem(str(row_position + 1)))
            self.table_widget.setItem(row_position, 1, QTableWidgetItem(''))
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        window = TestApp()
        window.show()
        sys.exit(app.exec())
    

    Edit:
    New QTablewidget subclass using musicamente's comments:

    class ExpandingTableWidget(QTableWidget):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.model().rowsInserted.connect(self.set_height)
            self.model().columnsInserted.connect(self.set_height)
    
        def set_height(self):
            height = self.horizontalHeader().height() + self.verticalHeader().length() + self.frameWidth() * 2
            self.setMaximumHeight(height)