PyQt5:直接在 QMainWindow 中放置按钮与使用 QGridLayout 的区别

PyQt5: Difference in placing buttons directly in QMainWindow versus using QGridLayout

我正在尝试使用 PyQt5 构建一个 UI 来将按钮放置在准确的位置。我相信 QGridLayout 是执行此操作的好方法,而不是直接在 QMainWindow 中绘制它们。我查看 QGridLayout 的原因是因为我希望将按钮网格与 UI 的其他部分分开,例如按钮网格右侧的图像(使用 QHBoxLayout)。

当我直接在 QMainWindow 中绘制按钮时,按钮出现在正确的位置。但是,当我使用具有相同坐标的 QGridLayout 时,按钮没有放置在正确的位置并且看起来被压扁了。

方法 1:我使用 QPushButton .move() 和 .resize() 方法直接在 QMainWindow 中绘制按钮。在这种情况下,按钮被绘制在正确的位置。

# below imports, data variable, and main() are used for both examples
import sys
from PyQt5.QtWidgets import (QApplication, QPushButton, QMainWindow, 
                            QGridLayout, QWidget)

data = [{'xmin': 2, 'ymin': 4,  'width': 205, 'height': 54, 'label': 'Go to Page 1'}, 
{'xmin': 34, 'ymin': 22, 'width': 141, 'height': 17, 'label': 'Go to Page 2'}, 
{'xmin': 2, 'ymin': 90, 'width': 187, 'height': 54, 'label': 'Go to Page 3'}, 
{'xmin': 34, 'ymin': 108, 'width': 122, 'height': 17, 'label': 'Go to Page 4'}, 
{'xmin': 2, 'ymin': 176, 'width': 231, 'height': 54, 'label': 'Go to Page 5'}]

class VisualizerWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        for rect in data:
            button = QPushButton(rect['label'], parent=self)
            button.move(rect['xmin'], rect['ymin'])
            button.resize(rect['width'], rect['height']+10)

def main():
    app = QApplication(sys.argv)
    viz_window = VisualizerWindow()
    viz_window.showMaximized()
    sys.exit(app.exec())

if __name__ == '__main__':
    main()

方法二:当我采用QMainWindow、QGridLayout的方法,然后通过layout.addWidget()添加QPushButtons时,按钮没有放置在屏幕上的正确位置,即使我在 .addWidget() 方法中提供了准确的坐标。

class VisualizerWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        layout = QGridLayout()
        
        for rect in data:
            button = QPushButton(rect['label'], parent=self)
            layout.addWidget(button, rect['xmin'], 
                                rect['ymin'], rect['width'], 
                                rect['height']+10)
        widget = QWidget(self)
        widget.setLayout(layout)
        self.setCentralWidget(widget)

方法 2 中的网格布局似乎在主要 Window 中被压扁了。 为什么我在这两种方法之间看到不同的绘图行为?如何正确使用 QGridLayout 在正确的位置绘制按钮?

前提:使用固定的几何形状(您称之为“绘制按钮”)很少是个好主意。布局管理器,如 QGridLayout,确保您的界面始终针对任何 window 分辨率进行优化,方法是考虑所有小部件用于计算其大小的字体和样式,并保证它们始终正确显示,而不会相互重叠,如果 window 太小则隐藏它们,或者如果有更多 space 可用则将它们留在一个小区域。

您的网格代码的问题是您尝试使用像素作为参考向网格添加小部件,但是 addWidget() 参数被引用到 并且网格的
使用布局管理器时,手动设置小部件的位置是没有用的,因为布局管理器有责任根据添加小部件的 row/column(和 row/column 跨度)设置它们的位置。

由于布局管理器还负责调整小部件的大小(如果有足够的 space,则将它们变大;如果没有,则将其变小),如果您想设置特定的大小,则需要使用小部件 setFixedSize()setFixedWidth()setFixedHeight() 函数。

布局管理器也始终尝试使用所有可用 space,因此在设置固定或最大小部件大小时,布局管理器会将这些小部件平均放置在可用 space 中。如果您需要将固定大小的小部件放在其网格单元格的一侧,请在 addWidget().
中使用 alignment 参数 此外,在您的情况下,结果将是按钮将在垂直方向上均匀分布,因此您需要添加一个“spacer”。

最后,要添加边距(将应用于整个布局或小部件),可以使用 QWidget.setContentsMargins() and QLayout.setContentsMargins()

这是您的代码的修订版:

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QApplication, QPushButton, QMainWindow, 
                            QGridLayout, QWidget, QSpacerItem, QSizePolicy)

leftMargin = 2
topMargin = 4
data = [
    {'row': 0, 'column': 0, 'width': 205, 'height': 54, 'label': 'Go to Page 1'}, 
    {'row': 1, 'column': 0, 'width': 141, 'height': 54, 'label': 'Go to Page 2'}, 
    {'row': 2, 'column': 0, 'width': 187, 'height': 54, 'label': 'Go to Page 3'}, 
]

class VisualizerWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        layout = QGridLayout()

        layout.setContentsMargins(leftMargin, topMargin, 0, 0)

        for rect in data:
            button = QPushButton(rect['label'], parent=self)
            layout.addWidget(button, rect['row'], rect['column'], alignment=Qt.AlignLeft)
            button.setFixedSize(rect['width'], rect['height'])

        # add a spacer item at the bottom of the layout, so that it will occupy all
        # the remaining available space, and will "put" the buttons on top
        spacer = QSpacerItem(20, 20, QSizePolicy.Expanding, QSizePolicy.Expanding)
        layout.addItem(spacer, layout.rowCount(), 0)

        widget = QWidget(self)
        widget.setLayout(layout)
        self.setCentralWidget(widget)

请注意,我只使用了您示例中的三个按钮,因为我真的不明白您为什么要使用重叠按钮。

我强烈建议您阅读有关 layout managers 的文档并使用 Designer 中的布局进行一些实验,这可能会澄清您的大部分疑虑。