如何垂直+水平居中一个小部件,同时忽略视图/窗口中的所有其他小部件?
我正在尝试在 QPushButton 的顶部和底部添加相同大小的扩展垫片。我希望这些垫片能够尊重窗户的高度——并且仅窗户的高度。然而,在初始化时,总是会向顶部间隔添加一个额外的约 19px。 19px 似乎是我左上角面包屑的高度。我如何告诉间隔符将按钮小部件仅相对于窗口/视图大小居中,而不是相对于任何小部件居中?
我尝试覆盖
resizeEvent
并手动计算顶部和底部垫片的高度应该是多少,然后使用 QSpacerItem.changeSize()
更改这些高度,但无济于事。我还尝试使用 QSizePolicy.Fixed
而不是 QSizePolicy.Expanding
,但结果不一致。 (当然,使用 QSizePolicy.Fixed
时,垫片也不会膨胀。)
def resizeEvent(self, event):
super().resizeEvent(event)
if self.isResizing:
return
self.isResizing = True
# Calculate the window's total height.
totalHeight = self.size().height()
# Calculate the total height taken by the breadcrumb.
breadcrumbHeight = self.breadcrumb.sizeHint().height()
breadcrumbMargins = self.breadcrumb.contentsMargins()
breadcrumbTotalHeight = breadcrumbHeight + breadcrumbMargins.top() + breadcrumbMargins.bottom()
# Calculate the total available space for the spacers.
topSpacerHeight = (totalHeight // 2) - ((self.button.size().height()) // 2) - breadcrumbTotalHeight
bottomSpacerHeight = (totalHeight // 2) - ((self.button.size().height()) // 2)
print('topSpacerHeight ' + str(topSpacerHeight))
print('bottomSpacerHeight ' + str(bottomSpacerHeight))
# Adjust the top spacer
self.topSpacer.changeSize(20, topSpacerHeight, QSizePolicy.Minimum, QSizePolicy.Expanding)
# Adjust the bottom spacer similarly
self.bottomSpacer.changeSize(20, bottomSpacerHeight, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.isResizing = False
有什么想法吗?这看起来应该是很容易实现的事情。我可以使用
breadcrumb
计算覆盖的 resizeEvent()
函数中 breadcrumbHeight = self.breadcrumb.sizeHint().height()
的大小。我觉得我们应该能够利用它来调整顶部垫片的尺寸,使其等于底部垫片的大小。
这是一个可重复的最小示例:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QSpacerItem, QSizePolicy
from PyQt5.QtCore import Qt, QSize
class MinimalExample(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Minimal Example')
self.setGeometry(100, 100, 800, 450)
self.initUI()
def initUI(self):
self.centralWidget = QWidget(self)
self.setCentralWidget(self.centralWidget)
mainLayout = QVBoxLayout(self.centralWidget)
# Breadcrumb at the top left
breadcrumb = QLabel('<a href="#">Home</a> > Page')
breadcrumb.setAlignment(Qt.AlignLeft | Qt.AlignTop)
mainLayout.addWidget(breadcrumb)
# Spacer to push everything else to the center
topSpacer = QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding)
mainLayout.addSpacerItem(topSpacer)
# Button in the middle
buttonLayout = QHBoxLayout()
mainLayout.addLayout(buttonLayout)
button = QPushButton("Click Me")
button.setFixedSize(QSize(750, 200))
buttonLayout.addWidget(button, 0, Qt.AlignCenter)
# Spacer at the bottom
bottomSpacer = QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding)
mainLayout.addSpacerItem(bottomSpacer)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MinimalExample()
window.show()
sys.exit(app.exec_())
提前感谢您的帮助!我已经尴尬地花了一整天的时间了!
使用布局manager意味着其托管项目的几何形状完全委托给它。
虽然通常最好总是让布局管理器完成他们的工作,但有时需要通过手动设置某些小部件的几何形状来覆盖它,在这种情况下,最好不将这些小部件添加到布局中,但只需确保它们是使用
parent
小部件参数创建的。
这使得有必要手动管理这些小部件的几何形状,这通常发生在父小部件的
resizeEvent()
覆盖中,但是,如果这些小部件应被视为“视觉布局”的一部分,则需要采取一些进一步的预防措施需要。
在这种特定情况下,您可以在布局的末尾添加一个拉伸(就在标签之后),但是您应该通过正确设置其最小尺寸来确保父小部件始终有足够的空间来显示“浮动”小部件:
class MinimalExample(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Minimal Example')
self.setGeometry(100, 100, 800, 450)
self.initUI()
def initUI(self):
centralWidget = QWidget(self)
self.setCentralWidget(centralWidget)
mainLayout = QVBoxLayout(centralWidget)
# Breadcrumb at the top left
breadcrumb = QLabel('<a href="#">Home</a> > Page')
breadcrumb.setAlignment(Qt.AlignLeft | Qt.AlignTop)
mainLayout.addWidget(breadcrumb)
mainLayout.addStretch()
self.button = QPushButton("Click Me", centralWidget)
self.button.setFixedSize(QSize(750, 200))
margins = mainLayout.contentsMargins()
centralWidget.setMinimumSize(
margins.left() + margins.right() + self.button.width(),
mainLayout.spacing()
+ mainLayout.minimumSize().height() + self.button.height()
)
def resizeEvent(self, event):
super().resizeEvent(event)
r = self.button.rect()
r.moveCenter(self.centralWidget().geometry().center())
self.button.setGeometry(r)
但是,您应该意识到,这可能会产生不需要的结果:例如,如果窗口高度更改为最小值,按钮将覆盖在标签上方,这是因为即使我们包含了窗口的最小高度标签和按钮,尝试始终将小部件居中会导致几何形状不一致,这就是为什么通常不鼓励这种尝试,并且您可能应该重新考虑整个 UI,因为特定小部件的“全局”居中,无论其余内容如何,可能是基于错误的 UI 假设。
请注意,
centralWidget()
是QMainWindow的现有函数,最好不要覆盖此类名称。这就是为什么我没有为其分配实例属性,然后在resizeEvent()
中使用真正的函数getter。如果您仍然想要实例属性,请选择另一个名称。