问题
I'm having a problem with pyqt5. I have this piece of code to instantiate my class and open my window without closing after the show method (because gc).
def user_crud():
global crud
crud = TCrudUsuario()
crud.show()
it works well, but on the TCrudUsuario class, I have some buttons that connects to a method with arguments, so I'm using a lambda (tried with partial, but the same problem happens), something like this:
self.btnNovo.clicked.connect(lambda: self.manage_user(1))
the problem is: if I use a lambda or partial to connect to my method manage_user(), it allows me to open multiple instances of TCrudUsuario class, opening two or more windows. but, if I remove the connect method, it only opens 1 window. My goal is to allow only one instance/one window. does anyone know why this happens?
reproducible code:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_test(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setObjectName("test")
self.resize(380, 250)
self.btntest = QtWidgets.QPushButton(self)
self.btntest.setGeometry(QtCore.QRect(70, 190, 100, 40))
self.btntest.setText('open other window')
self.btntest.clicked.connect(open_otherwindow)
class OtherWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setObjectName("otherwindow")
self.resize(250, 250)
self.button = QtWidgets.QPushButton(self)
self.button.setGeometry(QtCore.QRect(70, 100, 100, 40))
self.button.setText('hello')
self.button.clicked.connect(lambda: self.nothing(1))
def nothing(self, arg):
pass
def open_otherwindow():
global w
w = OtherWindow()
w.show()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
app.setStyle('Fusion')
test = Ui_test()
test.show()
sys.exit(app.exec_())
回答1:
Explanation:
To understand the problem, the following 2 codes and their outputs must be analyzed:
Example1
from PyQt5 import QtCore
if __name__ == "__main__":
app = QtCore.QCoreApplication([])
o = QtCore.QObject()
o.destroyed.connect(lambda: print("destroyed o"))
o = QtCore.QObject()
def on_timeout():
print("quit")
QtCore.QCoreApplication.quit()
QtCore.QTimer.singleShot(1000, on_timeout)
app.exec_()
destroyed o
quit
Example2
from PyQt5 import QtCore
if __name__ == "__main__":
app = QtCore.QCoreApplication([])
o = QtCore.QObject()
o.destroyed.connect(lambda: print("destroyed o"))
p = o
o = QtCore.QObject()
def on_timeout():
print("quit")
QtCore.QCoreApplication.quit()
QtCore.QTimer.singleShot(1000, on_timeout)
app.exec_()
quit
destroyed o
In the first example, the variable "o" is assigned a QObject and when another QObject is assigned, the initial QObject is deleted, so "destroyed" is printed before "quit".
In the second example, it has the difference "p = o" where reference is made to the QObject, that is, in that line "p" and "o" represent the same object, so by assigning "or" a new QObject the initial QObject is not it destroys, and it is only destroyed when the loop ends and the GC does its job.
That is what happens in your case in a subtle way, the logic of "p = o" is that the QObject is stored in another "place", and in your example that "place" is the lambda that has its own scope ( similarly with partial). Specifically, in your example, a new window was created destroying the previous one, causing a single window to be displayed at all times.
Solution:
One possible solution is to prevent the first window from being removed and a new window created using a flag:
from PyQt5 import QtCore, QtGui, QtWidgets
flag = False
# ...
def open_otherwindow():
global w, flag
if not flag:
w = OtherWindow()
w.show()
flag = True
# ...
来源:https://stackoverflow.com/questions/60625437/pyqt5-multiple-instances-of-same-window