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 中的布局进行一些实验,这可能会澄清您的大部分疑虑。
我正在尝试使用 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 中的布局进行一些实验,这可能会澄清您的大部分疑虑。