简体   繁体   English

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

[英]PyQt5: Difference in placing buttons directly in QMainWindow versus using QGridLayout

I am trying to build a UI using PyQt5 that places buttons in exact positions.我正在尝试使用 PyQt5 构建一个将按钮放置在精确位置的 UI。 I believe that QGridLayout is a good way to do this, versus plotting them directly in the QMainWindow.我相信 QGridLayout 是一个很好的方法来做到这一点,而不是直接在 QMainWindow 中绘制它们。 The reason I am looking at QGridLayout is because I would like to have a separation of the grid of buttons from other parts of the UI, such as an image to the right of the buttons grid (using QHBoxLayout).我查看 QGridLayout 的原因是因为我希望将按钮网格与 UI 的其他部分分开,例如按钮网格右侧的图像(使用 QHBoxLayout)。

When I plot the buttons directly in the QMainWindow, the buttons appear in the correct locations.当我直接在 QMainWindow 中绘制按钮时,按钮出现在正确的位置。 However, when I use QGridLayout with the same coordinates, the buttons are not placed in the correct locations and appear to be squished.但是,当我使用具有相同坐标的 QGridLayout 时,按钮未放置在正确的位置并且似乎被压扁了。

Approach 1: I plot the buttons directly in the QMainWindow using the QPushButton .move() and .resize() methods.方法 1:我使用 QPushButton .move() 和 .resize() 方法直接在 QMainWindow 中绘制按钮。 In this case the buttons are plotted in the correct locations.在这种情况下,按钮绘制在正确的位置。

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

方法 1 窗口截图 - 正确位置

Approach 2: When I take the approach of using QMainWindow, QGridLayout, and then add QPushButtons through layout.addWidget(), the buttons are not placed in the correct locations on the screen, even though I am supplying the exact coordinates in the .addWidget() method.方法 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 窗口截图 - 位置不正确

The Grid Layout in Approach 2 appears to be squished in the Main Window.方法 2 中的网格布局似乎在主窗口中被压扁了。 Why am I seeing different plotting behavior between these two approaches?为什么我看到这两种方法之间的绘图行为不同? How can I use QGridLayout correctly to plot the buttons in the correct locations?如何正确使用 QGridLayout 在正确的位置绘制按钮?

Premise: using fixed geometries (what you call "plot the buttons") is rarely a good idea.前提:使用固定几何图形(您称之为“绘制按钮”)很少是一个好主意。 Layout managers, like QGridLayout, ensure that your interface is always optimized for any window resolution by taking into account font and styles that all widget use to compute their size and guarantee that they are always correctly shown, without overlapping one each other, hiding them if the window is too small, or leaving them in a small area if there is more space available.布局管理器,如 QGridLayout,通过考虑所有小部件用于计算其大小的字体和样式,确保您的界面始终针对任何窗口分辨率进行优化,并确保它们始终正确显示,彼此不重叠,如果窗户太小,或者如果有更多可用空间,将它们留在一个小区域。

The problem with your grid code is that you're trying to add widgets to the grid using pixel as references, but addWidget() arguments are referred to rows and columns of the grid.您的网格代码的问题在于您尝试使用像素作为引用向网格添加小部件,但addWidget()参数被引用到网格的
When using layout managers, manually setting the position of widget is useless, as it's the layout manager responsibility to set their position, based on the row/column (and row/column spans) on which the widget was added.使用布局管理器时,手动设置小部件的位置是没有用的,因为布局管理器负责根据添加小部件的行/列(和行/列跨度)设置它们的位置。

Since layout managers also take care of resizing widgets (making them bigger if there's enough space, or smaller if there's not), if you want to set specific sizes you'll need to use the widget setFixedSize() , setFixedWidth() or setFixedHeight() functions.由于布局管理器还负责调整小部件的大小(如果有足够的空间,则将它们变大,如果没有,则将它们变小),如果您想设置特定大小,则需要使用小部件setFixedSize()setFixedWidth()setFixedHeight()函数。

The layout manager also always tries to use all the available space, so when setting fixed or maximum widgets sizes, the layout manager will place those widgets equally in the available space.布局管理器也总是尝试使用所有可用空间,因此在设置固定或最大小部件尺寸时,布局管理器会将这些小部件平均放置在可用空间中。 If you need to put a widget with a fixed size on a side of its grid cell, use the alignment argument in addWidget() .如果您需要在其网格单元的一侧放置一个固定大小的小部件,请使用addWidget()alignment参数。
Also, in your case, the result would be that the buttons will be equally distributed vertically, so you'll need to add a "spacer".此外,在您的情况下,结果将是按钮将垂直均匀分布,因此您需要添加一个“间隔”。

Finally, to add margins (that will be applied to the whole layout or the widget), QWidget.setContentsMargins() and QLayout.setContentsMargins() can be used.最后,要添加边距(将应用于整个布局或小部件),可以使用QWidget.setContentsMargins()QLayout.setContentsMargins()

Here's a revised version of your code:这是您的代码的修订版本:

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)

Note that I just used three buttons taken from your example, as I really don't understand why you're using overlapping buttons.请注意,我只使用了从您的示例中提取的三个按钮,因为我真的不明白您为什么使用重叠按钮。

I strongly suggest you to read the documentation about layout managers and do some experiments using layouts in Designer, which will probably clarify most of your doubts.我强烈建议您阅读有关布局管理器的文档并在 Designer 中使用布局进行一些实验,这可能会澄清您的大部分疑问。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM