为啥没有显示绝对定位的小部件?

Posted

技术标签:

【中文标题】为啥没有显示绝对定位的小部件?【英文标题】:Why is an absolute positioned widget not shown?为什么没有显示绝对定位的小部件? 【发布时间】:2020-04-30 06:43:01 【问题描述】:

使用 Qt5 我正在尝试使用绝对定位使小部件工作。下面的代码是我正在尝试做的事情的最小工作示例。快速浏览代码:

CentralWidget 是主窗口的中心小部件,使用绝对定位保存 MyWidget,例如不使用任何布局。 MyWidget 不会立即设置其子小部件,但提供了一个 SetData 方法,该方法首先删除所有当前子小部件,然后将新的子小部件添加到其布局中。 SetData 使用主窗口中的计时器触发。

我注释掉了两行代码。第一个通过向CentralWidget 添加布局来“启用”使用布局的相对定位。这条线显示了我想要实现的目标,但使用绝对定位。第二条注释启用了一些调试信息:

MyWidget
  layout.count: 3
  size: PyQt5.QtCore.QSize(-1, -1)
  sizeHint: PyQt5.QtCore.QSize(200, 100)

CentralWidget
  size: PyQt5.QtCore.QSize(18, 18)
  sizeHint: PyQt5.QtCore.QSize(18, 18)

为了让MyWidget 使用绝对定位可见,我做错了什么?

代码:

from PyQt5 import QtCore, QtWidgets
import sys


class MyWidget(QtWidgets.QWidget):
    z = 0

    def __init__(self, parent):
        super().__init__(parent)

        self._layout = QtWidgets.QVBoxLayout(self)

    def SetData(self):
        while self._layout.count() > 0:
            widget = self._layout.takeAt(0).widget()
            widget.hide()
            widget.deleteLater()

        for i in range(3):
            self._layout.addWidget(QtWidgets.QLabel(str(MyWidget.z * 10 + i)))

        MyWidget.z += 1


class CentralWidget(QtWidgets.QWidget):
    def __init__(self, parent):
        super().__init__(parent)

        self._myWidget = MyWidget(self)

        # QtWidgets.QHBoxLayout(self).addWidget(self._myWidget)

    def SetData(self):
        self._myWidget.SetData()
        # print("MyWidget\n  layout.count: \n  size: \n  sizeHint: \n\nCentralWidget\n  size: \n  sizeHint: \n\n".format(self._myWidget.layout().count(), self.sizeHint(), self.size(), self._myWidget.sizeHint(), self._myWidget.size()))


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        centralWidget = CentralWidget(self)
        self.setCentralWidget(centralWidget)

        self._timer = QtCore.QTimer(self)
        self._timer.timeout.connect(centralWidget.SetData)
        self._timer.start(500)


def main():
    app = QtWidgets.QApplication(sys.argv)

    mainWindow = MainWindow()
    mainWindow.show()

    app.exec_()


if __name__ == "__main__":
    main()

【问题讨论】:

尝试用self._layout = QtWidgets.QVBoxLayout(parent)替换行self._layout = QtWidgets.QVBoxLayout(self) @S.Nick 您的建议确实显示了标签,但这不是我所追求的。您的建议将布局添加到 CentralWidget,因此不使用标签的绝对定位。 如果我没记错的话,如果你不使用布局,你应该在创建 self._myWidget.show() 之后显式调用它。 @titusjan 不,调用self._myWidget.show() 不会将小部件绘制到窗口中:/。 【参考方案1】:

这种行为的原因与小部件没有添加到布局中直接相关它的内容是在显示后添加的。

事实上,如果你在初始化时和mainWindow.show()之前调用centralWidget.SetData(),它会按预期工作。

当您将子小部件添加到布局时会发生很多事情,这通常涉及对子大小提示的多次调用,允许父级调整自己的大小提示,然后,,调整它的大小和它的孩子的大小。

如果该“容器小部件”本身包含在另一个布局中,则该小部件将在下一个事件周期中自动调整大小(基于其提示),但在您的情况下不会发生这种情况,因为您的是“免费”小部件。

您要查找的函数是QWidget.adjustSize(),但是由于上述原因,您不能在添加子小部件后立即调用它。 为了解决您的问题,您可以在adjustSize() 之前调用QApplication.processEvents(),或者最终使用从0 开始的单次QTimer:

    def SetData(self):
        while self._layout.count() > 0:
            widget = self._layout.takeAt(0).widget()
            widget.hide()
            widget.deleteLater()

        for i in range(3):
            self._layout.addWidget(QtWidgets.QLabel(str(MyWidget.z * 10 + i)))

        MyWidget.z += 1

        QtCore.QTimer.singleShot(0, self.adjustSize)

【讨论】:

感谢您的回答。我已经尝试过adjustSize,但得到了奇怪的结果,因为我没有将它放入计时器或调用processEvents。您是否参考了一份文档,其中更详细地描述了“将子小部件添加到布局时会发生很多事情” @Woltan 不幸的是,没有。我只有通过经验和研究小部件和布局的源代码才知道这一点。

以上是关于为啥没有显示绝对定位的小部件?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Internet Explorer 7 上绝对定位的父元素中的百分比宽度子元素的宽度会塌陷?

我的父级元素div用了相对定位,儿子元素div用了相对定位,为啥孙子元素div用了绝对定位不行了?

绝对定位的元素盖住下方的元素时,为啥把下方的position设置为relative时,会不被盖住呢?

绝对定位的元素盖住下方的元素时,为啥把下方的position设置为relative时,会不被盖住呢?

CSS过渡混合绝对和相对定位

如何让绝对定位的盒子进行水平居中