【问题标题】:QTableWidget with relative-width, stretchable columns具有相对宽度、可拉伸列的 QTableWidget
【发布时间】:2020-09-26 08:44:54
【问题描述】:

我正在尝试实现一个类似于以下的pyqt5表:

首先:我什至不确定 QTableWidget 是不是最合适的小部件,因为我对 Qt 没有太多经验。 QGridLayout 可能更适合。我先尝试过,但不知如何使其符合要求,任何建议都将不胜感激。

表格要求是:

  • 一些单元格包含文本、一些 QCheckBoxes 和一些 QRadioButtons
  • 在调整大小时,表格宽度必须始终填充其父级宽度的 100%(我目前使用的是setSectionResizeMode(QHeaderView.Stretch)
  • 表格应该可以垂直滚动
  • 它的一些列有不同的相对宽度,必须保持这种关系(这里开始我的问题)
  • 每个列的居中都不相同(在文档中找不到如何实现这一点)

我目前的问题是setSectionResizeMode 的使用与resizeSection 冲突。我可以有不同的列宽或可拉伸的表格宽度,但我不知道如何同时实现。

示例代码如下所示:

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

data = {'col1':['1','2','3','4'],
        'col2':['1','2','1','3'],
        'col3':['1','1','2','1']}


class MainWindow(QWidget):

    def __init__(self):

        super().__init__()

        self.table = QTableWidget()
        self.table.setRowCount(4)
        self.table.setColumnCount(3)
        self.table.verticalHeader().hide()
        self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        # self.table.horizontalHeader().resizeSection(1, 200) <-- no effect if stretch is activated

        self.table.setHorizontalHeaderLabels([colName for colName in data])
        self.fillTable()

        self.layout = QVBoxLayout()
        self.layout.addWidget(self.table)
        self.setLayout(self.layout)


    def fillTable(self):
        for colNumber, colName in enumerate(data):
            for rowNumber, value in enumerate(data[colName]):
                self.table.setItem(rowNumber, colNumber, QTableWidgetItem(value))


if __name__=="__main__":

    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec_()

这是我的另一个尝试,使用 QGridLayout。这里的问题是调整窗口大小时列不展开。

import sys
from PyQt5.QtWidgets import (QWidget, QGridLayout, QLabel, QPushButton, QApplication, QScrollArea, QVBoxLayout)
from PyQt5.QtCore import Qt

class MainWindow(QWidget):

    def __init__(self):

        super().__init__()

        # Label
        self.label = QLabel('Test')

        # Grid
        self.grid = QWidget()
        self.gridLayout = QGridLayout()
        self.grid.setLayout(self.gridLayout)
        self.grid.setMinimumWidth(600) # Without this, the width relations are not kept

        # Grid elements
        for i in range(20):
            self.gridLayout.addWidget(QPushButton('A'), i, 0, 1, 1)
            self.gridLayout.addWidget(QPushButton('B'), i, 1, 1, 3)
            self.gridLayout.addWidget(QPushButton('C'), i, 4, 1, 1)

        # Scroll area
        self.scrollArea = QScrollArea()
        self.scrollArea.setWidget(self.grid)
        self.scrollArea.setMinimumWidth(self.grid.sizeHint().width())
        self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        # Window layout
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.label)
        self.layout.addWidget(self.scrollArea)
        self.setLayout(self.layout)


if __name__ == '__main__':

    app = QApplication(sys.argv)
    windowExample = MainWindow()
    windowExample.show()
    sys.exit(app.exec_())

【问题讨论】:

  • 你确定你真的需要一张桌子吗?如果您不需要项目视图的功能,但只想要一种将标签和复选框/单选框对齐的方法,那么您不能在 QScrollArea 内使用带有 QGridLayout 集的普通 QWidget 吗? (顺便说一句,自 Qt4 以来,QGridView 不再存在)。列是否可以调整大小?表可以调整大小吗? “项目”是否应该始终占据整个可用空间?您是否考虑过为单个列使用setSectionResizeMode,而不是为所有列设置它?
  • @musicamante 好的,这是一个错字(我指的是 QGridLayout,而不是 QGridView)。 GridLayout 是我的第一次尝试(找到在我的问题的新版本中添加的示例),但是当窗口大小发生变化时,我无法调整列的大小。是的,列应该可以调整大小,并且网格或表格应该占据所有宽度。
  • 我建议您编辑您的帖子并提供您对 QGridLayout 的尝试,因为它可能是更实际的选择。
  • @musicamante 这就是我刚刚所做的。
  • 对不起,我没有得到编辑。我刚刚使用网格布局方法发布​​了一个答案。

标签: python pyqt pyqt5 qtablewidget


【解决方案1】:

使用网格布局可能是最佳选择,因为项目视图更适合显示项目模型而不包含 GUI 元素。

您缺少的是 widgetResizable 属性:

如果此属性设置为 false(默认值),则滚动区域遵循其小部件的大小。

[...] 如果此属性设置为 true,则滚动区域将自动调整小部件的大小,以避免滚动条可以避免,或者利用额外的空间。

那么,您不应该使用列跨度来确保小部件自行扩展,而应使用setColumnStretch

最后,为确保滚动区域始终显示内容而不“剪切”右侧,不应禁用水平滚动条,而是根据区域内容适当设置最小宽度,垂直滚动条宽度和滚动区域框架宽度。

考虑到所有这些,您显然不需要再设置网格最小宽度了。

    # Grid elements
    for i in range(20):
        self.gridLayout.addWidget(QPushButton('A'), i, 0)
        self.gridLayout.addWidget(QPushButton('B'), i, 1)
        self.gridLayout.addWidget(QPushButton('C'), i, 2)
    # ensure that the second column uses a stretch factor bigger than the
    # default 0 value (which will only honor the size hints and policies of
    # each widget
    self.gridLayout.setColumnStretch(1, 1)

    # Scroll area
    self.scrollArea = QScrollArea()
    self.scrollArea.setWidget(self.grid)
    self.scrollArea.setWidgetResizable(True)
    # compute the correct minimum width
    width = (self.grid.sizeHint().width() + 
        self.scrollArea.verticalScrollBar().sizeHint().width() + 
        self.scrollArea.frameWidth() * 2)
    self.scrollArea.setMinimumWidth(width)

【讨论】:

  • 感谢您的回答。我看到现在至少内容没有被剪裁并填充父母的宽度。但是仍然存在两个问题:第一列和第三列没有扩展,但宽度是固定的,如果我在所有它们中都使用 setColumnStretch,那么它们都是相同的宽度。我看到的第二个问题是没有 grid.setMinimumWidth,在小窗口宽度下,三列也设置为相等宽度。
  • 嗯,我可以使用setColumnStretch和不同的因素来保持列宽比来解决第一个问题。
  • 根据小部件的不同,它们总是会尽可能小,所以如果它们是相同的类型并且具有相似的内容,它们将始终具有相同的最小宽度。您可以通过使用setMinimumColumnWidth() 设置(可能是任意的)最小宽度来避免这种情况。
猜你喜欢
  • 1970-01-01
  • 2013-01-27
  • 1970-01-01
  • 2010-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-30
相关资源
最近更新 更多