QLayout.replace 不替换

Posted

技术标签:

【中文标题】QLayout.replace 不替换【英文标题】:QLayout.replace not replacing 【发布时间】:2020-07-25 22:30:15 【问题描述】:

每次单击按钮 (self.btn) 时,我都有以下代码来替换小部件 (self.lbl):

import sys
from PySide2.QtCore import Slot
from PySide2.QtWidgets import QApplication, QLabel, QVBoxLayout, QWidget, \
        QPushButton


class Workshop(QWidget):

    def __init__(self):
        super().__init__()
        self.n = 0
        self.btn = QPushButton('Push me')
        self.lbl = QLabel(str(self.n))

        self.main_layout = QVBoxLayout()
        self.sub_layout = QVBoxLayout()
        self.sub_layout.addWidget(self.lbl)
        self.sub_layout.addWidget(self.btn)
        self.main_layout.addLayout(self.sub_layout)

        self.btn.clicked.connect(self.change_label)
        self.setLayout(self.main_layout)
        self.show()

    @Slot()
    def change_label(self):
        new_label = QLabel(str(self.n + 1))
        self.main_layout.replaceWidget(self.lbl, new_label)
        self.n += 1
        self.lbl = new_label


if __name__ == '__main__':
    app = QApplication()

    w = Workshop()

    sys.exit(app.exec_())

在初始化之后,对象w 看起来像这样:

当我点击“Push me”按钮 (self.btn) 时,数字会根据需要递增,但初始的“0”仍保留在后台:

但是其他数字并没有保留在后台;只有“0”可以。例如,这里是“22”(我点击“Push me”22次后的结果):

注意: 知道我可以使用 setText 方法实现我想要的结果,但这段代码只是我的一个 sn-p将适应一个我没有像setText 这样的方法的类。

谢谢!

【问题讨论】:

self.main_layout.replaceWidget(self.lbl, new_label)行之前添加行self.lbl.hide() 【参考方案1】:

当您替换布局中的小部件时,前一个小部件仍保留在那里。 来自replaceWidget()

widget from 的父级保持不变。

问题在于,当从布局中删除小部件时,它仍然保留其父级(在您的情况下,Workshop 实例),因此您仍然可以查看它。如果为您创建的每个新 QLabel 将对齐方式设置为 AlignCenter,这一点会更清楚:您会看到,如果您添加一个新标签并调整窗口大小,前一个标签将保持其先前的位置:

class Workshop(QWidget):

    def __init__(self):
        # ...
        self.lbl = QLabel(str(self.n), alignment=QtCore.Qt.AlignCenter)
        # ...

    def change_label(self):
        new_label = QLabel(str(self.n + 1), alignment=QtCore.Qt.AlignCenter)
        # ...

你有两种可能,实际上非常相似:

将“已移除”小部件的父级设置为None:一旦您覆盖self.lbl,垃圾收集器就会移除该小部件:
    self.lbl.setParent(None)
通过调用 deleteLater() 来删除小部件,这是在将小部件重新设置为 None 时发生的情况,如果它没有其他持久引用,则会被垃圾收集:
    self.lbl.deleteLater()

对于您的 pourposes,我建议您使用 deleteLater(),因为调用 setParent()(这是 QObject 的 setParent 的重新实现)实际上做了很多其他事情(最重要的是,检查焦点链并重置小部件的窗口标志),并且由于小部件无论如何都会被删除,所有这些实际上都是不必要的,并且无论如何都会调用 QObject 的 setParent(None) 实现。

您面临的图形“故障”可能取决于底层的低级绘画功能,在某些情况下,它在 MacOS 上会出现一些(已知的)意外行为。

【讨论】:

非常感谢@musicamante!您的回答在代码方面很有帮助,而且很有见地。我应该将self.lbl.deleteLater() 放在self.main_layout.replaceWidget(self.lbl, new_label) 之前还是之后? 顺便问一下,知道为什么这个错误只针对 0 而不是其他数字吗? 顺序没那么重要(因为deleteLater,顾名思义,删除对象later),但我建议你把它放在after 替换布局中的小部件,只是为了逻辑和连贯性问题。如前所述,故障可能取决于MacOS下的那些错误,我不知道更多。

以上是关于QLayout.replace 不替换的主要内容,如果未能解决你的问题,请参考以下文章

为啥 HyperLinkField 替换某些 URL 中的字段而不替换其他 URL?

批量替换url,指定内容不替换

PHP 用 <br/> 替换文字 \r\n (不替换新行)

新手用bash的sed这样替换怎么不成功呢?

如果没有替换,Python字符串替换文件而不触及文件

在不使用任何嵌套替换函数的情况下替换字符串中的多个字符