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

Python PyQt5:直接在QMainWindow中放置按钮与使用QGridLayout的区别,python,pyqt,pyqt5,Python,Pyqt,Pyqt5,我正在尝试使用PyQt5构建一个UI,将按钮放置在精确的位置。我相信QGridLayout是一种很好的方法,而不是直接在qMain窗口中绘制它们。我之所以关注QGridLayout,是因为我希望将按钮网格与UI的其他部分分离,例如按钮网格右侧的图像(使用QHBoxLayout) 当我直接在qMain窗口中绘制按钮时,按钮显示在正确的位置。但是,当我使用具有相同坐标的QGridLayout时,按钮没有放置在正确的位置,并且似乎被压扁了 方法1:我使用QPushButton.move()和.resi

我正在尝试使用PyQt5构建一个UI,将按钮放置在精确的位置。我相信QGridLayout是一种很好的方法,而不是直接在qMain窗口中绘制它们。我之所以关注QGridLayout,是因为我希望将按钮网格与UI的其他部分分离,例如按钮网格右侧的图像(使用QHBoxLayout)

当我直接在qMain窗口中绘制按钮时,按钮显示在正确的位置。但是,当我使用具有相同坐标的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()

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

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

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

网格代码的问题在于,您试图使用像素作为引用将小部件添加到网格中,但参数引用的是网格的行和列。
使用布局管理器时,手动设置小部件的位置是无用的,因为布局管理器负责根据添加小部件的行/列(以及行/列跨度)设置它们的位置

由于布局管理器还负责调整小部件的大小(如果有足够的空间,则使其变大,如果没有空间,则使其变小),如果要设置特定的大小,则需要使用小部件
setFixedSize()
setFixedWidth()
setfixedhight()
函数

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

最后,添加边距(将应用于整个布局或小部件),并且可以使用边距

以下是您的代码的修订版本:

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)
请注意,我刚刚使用了从您的示例中选取的三个按钮,因为我真的不明白为什么要使用重叠按钮


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

谢谢您的回答!我遗漏了我的目标的一个重要细节,这就是为什么我的示例中有重叠按钮。此UI旨在根据数据变量中的坐标显示给定网页的HTML元素。例如,重叠按钮将表示一个div和该div内的一个按钮。是否可以使用GridLayout正确地将这些小部件层叠在一起,就像您在回答中概述的那样?如果这个问题应该是另一个stackoverflow问题,我很高兴你的回答,我会接受它并发布一个不同的问题。QGridLayout允许在同一个单元格中放置多个小部件,甚至在多个单元格之间放置重叠的小部件。但是,如果我理解您试图实现的目标,那么使用布局可能不是一个好主意,因为如果要将小部件放置在html元素坐标处,则必须使用固定几何体(这是少数需要固定几何体的情况之一)。无论如何,我建议您创建一个新问题,因为您可能正在寻找一个不同的解决方案。谢谢您的额外输入!你的回答很有帮助,也很清楚。我将发布另一个问题,一旦我审查的文件更多。我感谢你的帮助。
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)