In PySide6, if a `QMainWindow` has a pushbutton connected to it's own partial function, it could be opened many times, why?

huangapple go评论58阅读模式
英文:

In PySide6, if a `QMainWindow` has a pushbutton connected to it's own partial function, it could be opened many times, why?

问题

以下是您的代码的翻译部分:

import sys
from functools import partial

from PySide6.QtWidgets import (QApplication, QGridLayout, QHBoxLayout,
                               QMainWindow, QPushButton, QStatusBar, QWidget)


class UiTest(QMainWindow):
    def __init__(self):
        self.app = QApplication.instance()
        if self.app is None:
            self.app = QApplication(sys.argv)
        super().__init__()

        self.setObjectName(u"test_partial")
        self.centralwidget = QWidget(self)
        self.centralwidget.setObjectName(u"centralwidget")
        self.setWindowTitle("Test partial")

        self.horizontalLayout = QHBoxLayout(self.centralwidget)

        self.PB_no_partial = QPushButton(self.centralwidget)
        self.PB_no_partial.setObjectName(u"PB_no_partial")
        self.PB_no_partial.setText("no partial")
        self.PB_no_partial.clicked.connect(self.PB_no_partial_clicked)

        self.PB_own_partial = QPushButton(self.centralwidget)
        self.PB_own_partial.setObjectName(u"PB_own_partial")
        self.PB_own_partial.setText("own partial")
        self.PB_own_partial.clicked.connect(self.PB_own_partial_clicked)

        self.PB_others_partial = QPushButton(self.centralwidget)
        self.PB_others_partial.setObjectName(u"PB_others_partial")
        self.PB_others_partial.setText("other's partial")
        self.PB_others_partial.clicked.connect(self.PB_others_partial_clicked)

        self.horizontalLayout.addWidget(self.PB_no_partial)
        self.horizontalLayout.addWidget(self.PB_own_partial)
        self.horizontalLayout.addWidget(self.PB_others_partial)

        self.gridLayout = QGridLayout()
        self.gridLayout.setObjectName(u"gridLayout")
        self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1)
        self.statusbar = QStatusBar(self)
        self.statusbar.setObjectName(u"statusbar")
        self.setStatusBar(self.statusbar)

        self.setCentralWidget(self.centralwidget)
        self.statusbar = QStatusBar(self)

    def PB_no_partial_clicked(self):
        self.no_partial = UiNoPartial()
        self.no_partial.show()

    def PB_own_partial_clicked(self):
        self.own_partial = UiOwnPartial()
        self.own_partial.show()

    def PB_others_partial_clicked(self):
        if not hasattr(self, "own_partial"):
            self.own_partial = UiOwnPartial()
        self.others_partial = UiOthersPartial(self.own_partial)
        self.others_partial.show()

    def show(self):
        super().show()
        sys.exit(self.app.exec())

class UiNoPartial(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("No partial")

        self.setObjectName(u"no_partial")
        self.centralwidget = QWidget(self)
        self.centralwidget.setObjectName(u"centralwidget")

        self.PB_no_partial = QPushButton(self.centralwidget)
        self.PB_no_partial.setObjectName(u"PB_no_partial")
        self.PB_no_partial.clicked.connect(self.PB_no_partial_clicked)

    def PB_no_partial_clicked(self):
        print("PB_no_partial_clicked")

class UiOwnPartial(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Own partial")

        self.setObjectName(u"own_partial")
        self.centralwidget = QWidget(self)
        self.centralwidget.setObjectName(u"centralwidget")

        self.PB_own_partial = QPushButton(self.centralwidget)
        self.PB_own_partial.setObjectName(u"PB_own_partial")
        self.PB_own_partial.clicked.connect(partial(self.PB_own_partial_clicked, "aaa"))

    def PB_own_partial_clicked(self, text:str):
        if text == "aaa":
            print("aaa")
        else:
            print("bbb")

class UiOthersPartial(QMainWindow):
    def __init__(self, others):
        super().__init__()
        self.setWindowTitle("Other's partial")

        self.setObjectName(u"others_partial")
        self.centralwidget = QWidget(self)
        self.centralwidget.setObjectName(u"centralwidget")

        self.others = others

        self.PB_others_partial = QPushButton(self.centralwidget)
        self.PB_others_partial.setObjectName(u"PB_others_partial")
        self.PB_others_partial.clicked.connect(partial(self.others.PB_own_partial_clicked, "bbb"))

if __name__ == "__main__":
    test = UiTest()
    test.show()

至于您的问题,PySide6中的行为是受到连接信号和槽函数的方式的影响的。在PB_own_partial_clicked函数中,使用了functools.partial来连接槽函数,这允许将参数传递给槽函数。这样,每次点击"own partial"按钮时,都会创建一个新的UiOwnPartial对象,因此您会看到多个窗口打开。

而对于"other's partial"按钮,它与已创建的UiOwnPartial对象的槽函数连接,因此只会打开一个窗口。这是因为当按钮点击时,它会检查是否已经创建了UiOwnPartial对象,如果没有,则创建一个新的,否则使用已经存在的对象。这就解释了为什么"own partial"和"other's partial"的行为不同。

这种行为与PyQt5/6或PySide2中的情况可能会有所不同,因为不同的版本可能有不同的信号和槽连接机制。如果需要更多关于特定版本的信息,建议查阅相关文档或进行实际测试。

英文:

Here is my code:

import sys
from functools import partial

from PySide6.QtWidgets import (QApplication, QGridLayout, QHBoxLayout,
                               QMainWindow, QPushButton, QStatusBar, QWidget)


class UiTest(QMainWindow):
    def __init__(self):
        self.app = QApplication.instance()
        if self.app is None:
            self.app = QApplication(sys.argv)
        super().__init__()

        self.setObjectName(u"test_partial")
        self.centralwidget = QWidget(self)
        self.centralwidget.setObjectName(u"centralwidget")
        self.setWindowTitle("Test partial")

        self.horizontalLayout = QHBoxLayout(self.centralwidget)

        self.PB_no_partial = QPushButton(self.centralwidget)
        self.PB_no_partial.setObjectName(u"PB_no_partial")
        self.PB_no_partial.setText("no partial")
        self.PB_no_partial.clicked.connect(self.PB_no_partial_clicked)

        self.PB_own_partial = QPushButton(self.centralwidget)
        self.PB_own_partial.setObjectName(u"PB_own_partial")
        self.PB_own_partial.setText("own partial")
        self.PB_own_partial.clicked.connect(self.PB_own_partial_clicked)

        self.PB_others_partial = QPushButton(self.centralwidget)
        self.PB_others_partial.setObjectName(u"PB_others_partial")
        self.PB_others_partial.setText("other's partial")
        self.PB_others_partial.clicked.connect(self.PB_others_partial_clicked)

        self.horizontalLayout.addWidget(self.PB_no_partial)
        self.horizontalLayout.addWidget(self.PB_own_partial)
        self.horizontalLayout.addWidget(self.PB_others_partial)

        self.gridLayout = QGridLayout()
        self.gridLayout.setObjectName(u"gridLayout")
        self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1)
        self.statusbar = QStatusBar(self)
        self.statusbar.setObjectName(u"statusbar")
        self.setStatusBar(self.statusbar)

        self.setCentralWidget(self.centralwidget)
        self.statusbar = QStatusBar(self)

    def PB_no_partial_clicked(self):
        self.no_partial = UiNoPartial()
        self.no_partial.show()
    
    def PB_own_partial_clicked(self):
        self.own_partial = UiOwnPartial()
        self.own_partial.show()
    
    def PB_others_partial_clicked(self):
        if not hasattr(self, "own_partial"):
            self.own_partial = UiOwnPartial()
        self.others_partial = UiOthersPartial(self.own_partial)
        self.others_partial.show()
    
    def show(self):
        super().show()
        sys.exit(self.app.exec())

class UiNoPartial(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("No partial")

        self.setObjectName(u"no_partial")
        self.centralwidget = QWidget(self)
        self.centralwidget.setObjectName(u"centralwidget")

        self.PB_no_partial = QPushButton(self.centralwidget)
        self.PB_no_partial.setObjectName(u"PB_no_partial")
        self.PB_no_partial.clicked.connect(self.PB_no_partial_clicked)
    
    def PB_no_partial_clicked(self):
        print("PB_no_partial_clicked")

class UiOwnPartial(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Own partial")

        self.setObjectName(u"own_partial")
        self.centralwidget = QWidget(self)
        self.centralwidget.setObjectName(u"centralwidget")

        self.PB_own_partial = QPushButton(self.centralwidget)
        self.PB_own_partial.setObjectName(u"PB_own_partial")
        self.PB_own_partial.clicked.connect(partial(self.PB_own_partial_clicked, "aaa"))
    
    def PB_own_partial_clicked(self, text:str):
        if text == "aaa":
            print("aaa")
        else:
            print("bbb")

class UiOthersPartial(QMainWindow):
    def __init__(self, others):
        super().__init__()
        self.setWindowTitle("Other's partial")

        self.setObjectName(u"others_partial")
        self.centralwidget = QWidget(self)
        self.centralwidget.setObjectName(u"centralwidget")

        self.others = others

        self.PB_others_partial = QPushButton(self.centralwidget)
        self.PB_others_partial.setObjectName(u"PB_others_partial")
        self.PB_others_partial.clicked.connect(partial(self.others.PB_own_partial_clicked, "bbb"))


if __name__ == "__main__":
    test = UiTest()
    test.show()

Now, run this code, and interesting things happen:

  • when I click "no partial" pushbutton for many times, only one "no partial" window would be opened;
  • when I click "own partial" pushbutton for many times, each time one new "own partial" window would be opened;
  • when I click "other's partial" pushbutton for many times, only one "other's partial" window would be opened.

So we can conclude: in PySide6, if one QMainWindow object has a pushbutton connected to it's own partial function, it could be opened many times, or it could be opened only once event though it be opened by user many times.

I wonder why this could happen? what makes it different? Why "own partial" is different from "othere's partial"?

I didn't test it on other python QT versions like PyQt5/6 or PySide2, but I guess same thing would happen. Anyone has PyQt5/6 or PySide2 could have a try.

My environment:<br>
windows 10<br>
python 3.10.1<br>
PySide6 6.2.2.1<br>

答案1

得分: 1

当你调用 partial 时,它会创建对每个被调用参数的引用,这些引用会一直存在,直到新的 partial 对象被取消引用。

因此,当你运行 partial(self.PB_own_partial_clicked, "aaa") 时,你实际上创建了对 self 的引用,这导致了 UiOwnPartial 实例最终持有对 partial 的引用,而 partial 又持有对自身的引用。

因此,即使你在 UiTest 窗口中覆盖了 self.own_partial 属性,UiOwnPartial 窗口仍然可以继续存在,因为Python检测到在 partial 对象中仍然存在对窗口的引用。

英文:

My guess is that when you call partial, it creates a reference to each of its called arguments which will stay around until the new partial object is dereferenced.

Thus, when you run partial(self.PB_own_partial_clicked, &quot;aaa&quot;), you end up creating a reference to self which then results in a situation where the UiOwnPartial instance ends up with a reference to partial which holds a reference to itself.

Because of that, even when you overwrite the self.own_partial property in the UiTest window, the UiOwnPartial window can continue to exist since Python detects that a reference to the window still exists in the partial object.

huangapple
  • 本文由 发表于 2023年5月10日 18:08:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76217173.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定