我想让框架或小部件在背景(蓝色)中实时绘制数据,该数据覆盖整个窗口,并在顶部带有按钮和/或其他对象的浮动“面板”。这些面板必须卡在侧面。在上图中,我希望将面板1捕捉到程序的左侧,将面板2捕捉到程序的底部,将面板3捕捉到程序的右侧。另外,面板2必须居中,但我想我已经涵盖了那个。
我将如何去做?我可以将网格放置在网格顶部还是将框架放置在框架顶部?
不幸的是,这些小部件将始终占据放置它们的一侧的整个区域。
虽然有一个“ hack”允许您执行此操作,但我不能保证它可以无缝运行。
它通过调整坞站小部件
和中央小部件的大小来工作,因此坞站将仅使用所需的空间并相应地移动,同时将中央小部件的几何形状设置为整个可用区域。
考虑到,由于resizeEvent可能在某个时间点发生[[之前,窗口被“映射”了(当窗口实际上对用户变为[[可见时)),我们需要一个小技巧来确保应用了几何正确地。这是在paint事件中完成的(我要警告您,这是not一个非常干净的方法),并且每当它检测到其中一个底座没有正确放置时,它将尝试再次调整它们的大小。 。 重要检查和调整大小必须在绘画事件中发生(这就是为什么使用QTimer延迟它们的原因)。
请注意,为确保坞站小部件的大小正确,您可能需要为坞站或其某些子项设置最小(或最大)大小。另外,所有底座都将autoFillBackground
属性设置为True,因此其背景将不会显示中央小部件。class DockTest(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
uic.loadUi('docktest.ui', self)
def checkGeometries(self):
central = self.centralWidget().geometry()
for dock in self.findChildren(QtWidgets.QDockWidget):
if dock.parent() != self:
continue
area = self.dockWidgetArea(dock)
if ((area == QtCore.Qt.LeftDockWidgetArea and central.x() > dock.geometry().x()) or
(area == QtCore.Qt.TopDockWidgetArea and central.y() > dock.geometry().y()) or
(area == QtCore.Qt.RightDockWidgetArea and central.right() < dock.geometry().right()) or
(area == QtCore.Qt.BottomDockWidgetArea and central.bottom() > dock.geometry().bottom())):
self.resizeWidgets()
return
def resizeWidgets(self):
# give Qt some time for layout adjustments
QtWidgets.QApplication.processEvents()
geometry = self.centralWidget().geometry()
left = top = right = bottom = None
for dock in self.findChildren(QtWidgets.QDockWidget):
if dock.parent() != self:
continue
area = self.dockWidgetArea(dock)
if area == QtCore.Qt.LeftDockWidgetArea:
left = dock
geometry.setLeft(left.geometry().x())
elif area == QtCore.Qt.TopDockWidgetArea:
top = dock
geometry.setTop(top.geometry().y())
elif area == QtCore.Qt.RightDockWidgetArea:
right = dock
geometry.setRight(right.geometry().right())
elif area == QtCore.Qt.BottomDockWidgetArea:
bottom = dock
geometry.setBottom(bottom.geometry().bottom())
if not dock.titleBarWidget():
# set a fake title bar widget to avoid drawing margins
titleWidget = QtWidgets.QWidget()
# the following could change according to the current style
margin = dock.layout().contentsMargins().top()
titleWidget.sizeHint = titleWidget.minimumSizeHint = lambda: QtCore.QSize(margin, margin)
dock.setTitleBarWidget(titleWidget)
# ensure that the dock is not movable, floatable or closable
if dock.features() & (dock.DockWidgetMovable | dock.DockWidgetFloatable | dock.DockWidgetClosable):
dock.setFeatures(dock.features() & ~(dock.DockWidgetMovable | dock.DockWidgetFloatable) | dock.DockWidgetClosable)
# set the central widget geometry to the full available area
self.centralWidget().setGeometry(geometry)
# update the geometry for each dock widget, according to its position
if left:
dockGeo = left.geometry()
dockGeo.setHeight(left.sizeHint().height())
dockGeo.moveTop(geometry.center().y() - dockGeo.height() / 2)
left.setGeometry(dockGeo)
if top:
dockGeo = top.geometry()
dockGeo.setWidth(top.minimumSizeHint().width())
dockGeo.moveLeft(geometry.center().x() - dockGeo.width() / 2)
top.setGeometry(dockGeo)
if right:
dockGeo = right.geometry()
dockGeo.setHeight(right.minimumSizeHint().height())
dockGeo.moveTop(geometry.center().y() - dockGeo.height() / 2)
right.setGeometry(dockGeo)
if bottom:
dockGeo = bottom.geometry()
dockGeo.setWidth(bottom.minimumSizeHint().width())
dockGeo.moveLeft(geometry.center().x() - dockGeo.width() / 2)
bottom.setGeometry(dockGeo)
def resizeEvent(self, event):
super().resizeEvent(event)
self.resizeWidgets()
def showEvent(self, event):
super().showEvent(event)
self.resizeWidgets()
def paintEvent(self, event):
super().paintEvent(event)
# IMPORTANT! the checking (and eventual resizing) mus NOT
# happen within a paintEvent, so we delay that
QtCore.QTimer.singleShot(0, self.checkGeometries)
不过,更好(或更简单)的方法是只创建自己的“停靠小部件”(它们是简单的QWidget),并确保将主窗口设置为其父窗口。这种方法的缺点是您不能使用设计器将这些小部件添加到主窗口中(但是您可以使用它来单独创建它们),而必须通过代码将其添加。在下面的示例中,我用代码创建了所有内容,但是您可以使用QWidget子类来创建它们,并使用self.leftWidget = MyLeftDock(self)
之类的东西进行添加。
class SimpleDock(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setCentralWidget(QtWidgets.QLabel('Child test', alignment=QtCore.Qt.AlignCenter))
self.centralWidget().setStyleSheet('background: rgb(153, 217, 234);')
self.leftWidget = QtWidgets.QWidget(self)
leftLayout = QtWidgets.QVBoxLayout(self.leftWidget)
for i in range(4):
leftLayout.addWidget(QtWidgets.QPushButton('button'))
self.leftWidget.setAutoFillBackground(True)
self.bottomWidget = QtWidgets.QWidget(self)
bottomLayout = QtWidgets.QGridLayout(self.bottomWidget)
for r in range(2):
slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
slider.setMinimumWidth(300)
bottomLayout.addWidget(slider, r, 0)
bottomLayout.addWidget(QtWidgets.QToolButton(), r, 1)
self.bottomWidget.setAutoFillBackground(True)
self.rightWidget = QtWidgets.QWidget(self)
rightLayout = QtWidgets.QVBoxLayout(self.rightWidget)
frame = QtWidgets.QFrame()
frame.setMinimumSize(48, 48)
frame.setFrameStyle(frame.StyledPanel|frame.Raised)
rightLayout.addWidget(frame)
rightLayout.addWidget(QtWidgets.QLabel(
'Some text'))
self.rightWidget.setAutoFillBackground(True)
def resizeWidgets(self):
QtWidgets.QApplication.processEvents()
geometry = self.centralWidget().geometry()
leftGeo = self.leftWidget.geometry()
leftGeo.setSize(self.leftWidget.sizeHint())
leftGeo.moveTop(geometry.center().y() - leftGeo.height() / 2)
self.leftWidget.setGeometry(leftGeo)
bottomGeo = self.bottomWidget.geometry()
bottomGeo.setSize(self.bottomWidget.sizeHint())
bottomGeo.moveCenter(geometry.center())
bottomGeo.moveBottom(geometry.bottom())
self.bottomWidget.setGeometry(bottomGeo)
rightGeo = self.rightWidget.geometry()
rightGeo.setSize(self.rightWidget.sizeHint())
rightGeo.moveCenter(geometry.center())
rightGeo.moveRight(geometry.right())
self.rightWidget.setGeometry(rightGeo)
def resizeEvent(self, event):
super().resizeEvent(event)
self.resizeWidgets()
这是一个比较(两者之间当然会有一些细微的差异,因为两者都是使用不同的方法创建的,但这仅是示例):