如何在QGroupBox,PyQt5中添加表

How to add a table in a QGroupBox, PyQt5

提问人:Souleater 提问时间:7/26/2023 最后编辑:musicamanteSouleater 更新时间:7/31/2023 访问量:114

问:

我是 PyQt5 的新手,仍然很挣扎。我尝试了一整天在QGroupBox中添加行和列。我发现的唯一解决方案是添加一个 QTableWidget,问题在于小部件大小。

现在我只想拥有此表的标题,然后用户可以向此表添加行。 [ID、名称]

我想要细胞,但不是随之而来的“窗口”。如果我尝试将它对齐在QGroupBox的中心,它会将整个东西对齐在中心,而不是列。(列位于小部件的左上角,见图)

How it currently looks

这是创建表的代码片段:

创建框

    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)

创建一个 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)

我唯一发现的是 .resizeColumnsToContents 和 .setSectionResizeMode,它们只是根据其中的内容调整列。

通过设置表格小部件的最大大小,我使它变小了,但这不是我想要的。如果向表中添加了更多数据,则用户必须滚动,因为小部件不够大。 难道没有任何东西可以自动调整吗?

还是有其他解决方案?用户可以选择要显示的数据(将添加哪一行)的按钮应位于中心行的正下方,如果删除/添加行,则自动向上/向下移动。如果我必须有固定大小的 QTableWidget,这也是不可能的。

希望我解释得足够好。我感谢每一个能让我更接近解决方案的答案。:) (是的,我已经看过 Stack Overflow 上的所有相关帖子,可能每隔一页,不幸的是没有解决我的问题)

python-3.x pyqt5 qtablewidget qgroupbox

评论

0赞 musicamante 7/26/2023
迟早,表格将需要滚动。有可能使表格只显示标题并动态改变其高度,但大多数情况下,这种方法会导致很难正确实现,而且如果它改变了整个布局,往往会不鼓励(“添加”按钮不断移动的事实实际上对用户来说很烦人)。问题是:你打算在这些牢房里放什么?因为看起来您正在尝试将 QTableWidget 用于布局目的,但在极少数情况下,这真的很有意义。
0赞 Souleater 7/26/2023
用户最多可以添加 5 个选项卡。我有一个实例列表。从这些实例中,我获得了 ID 和名称(将显示在表中)。单击该按钮时,用户会看到所有可用数据,选择一个,然后它应该作为新行显示在表中。[instance.id,instance.name]
0赞 musicamante 7/26/2023
对不起,您的评论不清楚;与上述内容有什么关系?
0赞 Souleater 7/26/2023
对不起,我的意思是当然是行,而不是制表符

答:

-1赞 Dev. Francesca Mazzeo 7/26/2023 #1
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QGroupBox, QVBoxLayout, QTableWidget, QPushButton, QTableWidgetItem, QHeaderView, QMessageBox


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 = QTableWidget()
        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)
        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_())

尝试这个并使其适应您的项目。

这是输出:

enter image description here

版本 2023-07-26 第一个代码:

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QGroupBox, QVBoxLayout, QTableWidget, QPushButton, QTableWidgetItem, QHeaderView, QMessageBox


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 = QTableWidget()
        self.table_widget.setColumnCount(2)
        self.table_widget.setHorizontalHeaderLabels(['ID', 'Name'])
        self.table_widget.verticalHeader().setVisible(False)
        self.table_widget.setShowGrid(False)

        # Set a fixed width for the 'ID' column
        self.table_widget.setColumnWidth(0, 50)
        #Set a fixed width for the 'NAME' column
        self.table_widget.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
        #Block widht fro the 'ID' column
        self.table_widget.horizontalHeader().setSectionResizeMode(0, QHeaderView.Fixed)

        # 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)
        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(''))

        # Resize the 'Name' column to fit the contents
        self.table_widget.resizeColumnToContents(1)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = TestApp()
    window.show()
    sys.exit(app.exec_())

试试这个,我已经调整了 ID 项链和 NAME 列的自动调整大小并阻止了它自动调整大小

这是输出:

enter image description here

版本 2023-07-26 第二个代码:

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QGroupBox, QVBoxLayout, QTableWidget, QPushButton, QTableWidgetItem, QHeaderView, QScrollArea, QSizePolicy
from PyQt5.QtCore import Qt

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

        # Create a QScrollArea and set it as the main widget
        self.scroll_area = QScrollArea(self)
        self.layout.addWidget(self.scroll_area)

        # Create a QWidget to hold the content
        self.main_widget = QWidget()
        self.main_layout = QVBoxLayout(self.main_widget)

        # Create a QGroupBox to display the table
        self.groupbox1 = QGroupBox('Data')
        self.groupbox_layout = QVBoxLayout()
        self.groupbox1.setLayout(self.groupbox_layout)
        self.groupbox1.setWindowFlag(Qt.WindowContextHelpButtonHint, False)
        self.groupbox1.hide()  # Hide the group box initially

        # Create a QTableWidget
        self.table_widget = QTableWidget()
        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)

        # Add the QTableWidget to the QGroupBox layout
        self.groupbox_layout.addWidget(self.table_widget)

        # Add the QGroupBox to the main_layout of the main_widget
        self.main_layout.addWidget(self.groupbox1)

        # 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 QPushButton to the main_layout of the main_widget
        self.main_layout.addWidget(self.add_row_button)

        # Set the main_widget as the widget for the QScrollArea
        self.scroll_area.setWidget(self.main_widget)

        # Set the QScrollArea to expand the widget vertically
        self.scroll_area.setWidgetResizable(True)
        self.main_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)

        # Set the number of rows to display (2 rows in this case)
        self.displayed_rows = 1

        # Calculate and set the initial height of the QScrollArea based on the QTableWidget height
        table_height = self.table_widget.rowHeight(0) * self.displayed_rows + self.groupbox_layout.spacing()
        self.scroll_area.setMinimumHeight(table_height)

        # Set the maximum height for the QGroupBox
        self.groupbox1.setMaximumHeight(table_height)

    def add_row(self):
        if not self.groupbox1.isVisible():  # Show the group box when the first row is added
            self.groupbox1.show()

        # 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(''))
 
        # Recalculate the new height of the QGroupBox based on the updated QTableWidget height
        table_height = self.table_widget.rowHeight(
            0) * (self.displayed_rows + 1.5) + self.groupbox_layout.spacing()
        self.groupbox1.setFixedHeight(table_height)
        self.table_widget.setMinimumHeight(table_height)

        if (self.displayed_rows < 5):
            self.displayed_rows += 1
        else:
            # Enable scrolling when the displayed rows reach 5
            self.update_scroll_area_height()

    def update_scroll_area_height(self):
        table_height = self.table_widget.rowHeight(
            0) * (self.displayed_rows + 2) + self.groupbox_layout.spacing()
        self.groupbox1.setFixedHeight(table_height)
        self.scroll_area.setMinimumHeight(table_height)

        # Enable vertical scrolling
        self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = TestApp()
    window.show()
    sys.exit(app.exec_())

尝试这个并使其适应您的项目。

这是输出:enter image description here

评论

0赞 Souleater 7/26/2023
太棒了,非常感谢!是否可以调整小部件,使其在添加行时“增长”?我的意思是只看到标题,添加一行时,小部件会变大。不过,我已经对这个解决方案感到满意,可以在此基础上进行:)
0赞 Dev. Francesca Mazzeo 7/26/2023
我想我已经开发了你需要的代码,但代码非常适合评论部分,如果你给我发一封电子邮件,我会把代码发给你......或者您可以通过我的联系表格(联系部分)或我的社交网络(页脚)与我联系,您可以在我的网站上找到 devfrancescamazzeo.com
0赞 musicamante 7/26/2023
@FrancescaMazzeo 请注意,如果解决方案可以尊重问题的请求,您应该考虑编辑您的帖子以改进您的答案(甚至创建一个新答案)。私人交流并不受欢迎,但如果对现有帖子的任何添加符合 OP 的要求,最好公开进行,因为它可能对可能面临类似问题的其他人有所帮助(这是 SO 的主要目的)。
0赞 Dev. Francesca Mazzeo 7/26/2023
已编辑,对不起,我不知道可以编辑带有绿色复选标记的帖子
0赞 Souleater 7/26/2023
感谢您的编辑,但我实际上不需要滚动,无论如何用户最多只能添加 5 行。QGroupBox中的空间应该没问题。它应该在视觉上“有吸引力”,而这个窗口则不是这样。我可以设置一个固定的大小,然后设置.setStyleSheet(CSS),这样我就看不到小部件的边框了吗?
0赞 mahkitah 7/26/2023 #2

这是Francesca Mazzeo答案的修改版本。 它使用具有重新实现方法的子类来获取“随着添加的行而增长”行为。QTableWidgetresizeEvent()

有一件奇怪的事情我不明白:添加第一行不会触发。后续行会这样做。resizeEvent()

我以任何方式发布此内容,希望有人知道如何解决这个问题。

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())

编辑:
使用 musicamente 的评论的新 QTablewidget 子类:

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)

评论

0赞 musicamante 7/26/2023
在调整大小事件中更改大小应始终小心谨慎,并且并不总是可靠的(如本例所示)。更合适的实现应该依赖于实际更改视口大小的函数和信号。例如,在本例中,通过连接到模型的 and(后者确保即使只设置了列,但不存在行,也会显示标题)。另外:无需遍历部分大小,只需使用 标题 .最后,不要对边距使用任意值,而是 .rowsInsertedcolumnsInsertedlength()frameWidth()
0赞 Souleater 7/26/2023
会看一看,谢谢!@musicamante您建议何时更改事件或覆盖内容?我仍然没有弄清楚它的用途/目的,因为到目前为止没有任何效果,而且看起来很复杂。
0赞 Souleater 7/26/2023
我只需要它自动调整,以便按钮可以直接位于最后一行的下方
0赞 mahkitah 7/26/2023
@musicamente,请参阅编辑。设置视口大小未获得预期结果。它只是改变了框架内的“白色区域”。你的其余评论一如既往地准确无误。
0赞 mahkitah 7/26/2023
@Souleater,如果您希望按钮直接位于 tablewidget 下方,只需在按钮之后而不是之前添加拉伸即可。