【问题标题】:PyQt5: Difference in placing buttons directly in QMainWindow versus using QGridLayoutPyQt5:直接在 QMainWindow 中放置按钮与使用 QGridLayout 的区别
【发布时间】:2020-12-29 05:17:53
【问题描述】:

我正在尝试使用 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()

方法 2:当我采用使用 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 中的网格布局似乎在主窗口中被挤压。 为什么我看到这两种方法之间的绘图行为不同?如何正确使用 QGridLayout 将按钮绘制在正确的位置?

【问题讨论】:

    标签: python pyqt pyqt5


    【解决方案1】:

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

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

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

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

    最后,要添加边距(将应用于整个布局或小部件),可以使用QWidget.setContentsMargins()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 中使用布局做一些实验,这可能会澄清您的大部分疑问。

    【讨论】:

    • 感谢您的回答!我遗漏了我的目标的一个重要细节,这就是我在示例中使用重叠按钮的原因。此 UI 旨在根据数据变量中的坐标显示给定网页的 HTML 元素。例如,重叠的按钮将代表一个 div 和该 div 内的一个按钮。是否可以像您在答案中概述的那样正确使用 GridLayout 将这些小部件层叠在一起?如果这个问题应该是另一个 stackoverflow 问题,我很高兴您对此的回答,并将接受它并发布一个不同的问题。
    • QGridLayout 允许将多个小部件放置在同一个单元格中,甚至可以在多个单元格之间放置多个小部件。但是,如果我了解您要实现的目标,则使用布局可能不是一个好主意,因为如果要将小部件放置在 html 元素坐标处,则必须使用固定的几何图形(这是您确实需要固定几何形状的少数情况之一)。无论如何,我建议您创建一个新问题,因为您似乎正在寻找不同的解决方案。
    • 感谢您的补充!你的回答真的很有帮助而且很清楚。一旦我更多地查看文档,我将发布另一个问题。感谢您的帮助。
    猜你喜欢
    • 2018-01-05
    • 2014-07-16
    • 2023-03-18
    • 2020-10-12
    • 1970-01-01
    • 2015-09-30
    • 1970-01-01
    • 1970-01-01
    • 2019-03-08
    相关资源
    最近更新 更多