PyQt5:相同 window 的多个实例
PyQt5: multiple instances of same window
我在使用 pyqt5 时遇到问题。我有这段代码来实例化我的 class 并在 show 方法之后打开我的 window 而无需关闭(因为 gc)。
def user_crud():
global crud
crud = TCrudUsuario()
crud.show()
它运行良好,但在 TCrudUsuario class 上,我有一些按钮连接到带参数的方法,所以我使用的是 lambda(尝试部分,但同样的问题发生),像这样:
self.btnNovo.clicked.connect(lambda: self.manage_user(1))
问题是:如果我使用 lambda 或部分连接到我的方法 manage_user(),它允许我打开 TCrudUsuario 的多个实例 class,开两个以上windows。但是,如果我删除连接方法,它只会打开 1 window。我的目标是只允许一个 instance/one window。有谁知道为什么会这样?
可重现代码:
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_())
解释:
要理解问题,必须分析以下 2 个代码及其输出:
示例 1
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
示例 2
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
在第一个例子中,变量"o"被分配了一个QObject,当另一个QObject被分配时,初始的QObject被删除,所以"destroyed"在"quit"之前被打印出来。
在第二个例子中,它有区别"p = o",其中引用了QObject,也就是说,在那一行"p"和"o"代表同一个对象,所以通过分配 "or" 一个新的 QObject,初始 QObject 不会被销毁,它只会在循环结束并且 GC 完成其工作时被销毁。
这就是您的情况以微妙的方式发生的情况,"p = o" 的逻辑是 QObject 存储在另一个 "place" 中,而在您的示例中 "place" 是具有自己范围的 lambda(与部分类似)。具体来说,在您的示例中,创建了一个新的 window 来破坏前一个,导致始终显示单个 window。
解决方案:
一个可能的解决方案是防止删除第一个 window 并使用标志创建新的 window:
from PyQt5 import QtCore, QtGui, QtWidgets
<b>flag = False</b>
# ...
def open_otherwindow():
<b>global w, flag
if not flag:
w = OtherWindow()
w.show()
flag = True</b>
# ...
我在使用 pyqt5 时遇到问题。我有这段代码来实例化我的 class 并在 show 方法之后打开我的 window 而无需关闭(因为 gc)。
def user_crud():
global crud
crud = TCrudUsuario()
crud.show()
它运行良好,但在 TCrudUsuario class 上,我有一些按钮连接到带参数的方法,所以我使用的是 lambda(尝试部分,但同样的问题发生),像这样:
self.btnNovo.clicked.connect(lambda: self.manage_user(1))
问题是:如果我使用 lambda 或部分连接到我的方法 manage_user(),它允许我打开 TCrudUsuario 的多个实例 class,开两个以上windows。但是,如果我删除连接方法,它只会打开 1 window。我的目标是只允许一个 instance/one window。有谁知道为什么会这样?
可重现代码:
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_())
解释:
要理解问题,必须分析以下 2 个代码及其输出:
示例 1
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
示例 2
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
在第一个例子中,变量"o"被分配了一个QObject,当另一个QObject被分配时,初始的QObject被删除,所以"destroyed"在"quit"之前被打印出来。
在第二个例子中,它有区别"p = o",其中引用了QObject,也就是说,在那一行"p"和"o"代表同一个对象,所以通过分配 "or" 一个新的 QObject,初始 QObject 不会被销毁,它只会在循环结束并且 GC 完成其工作时被销毁。
这就是您的情况以微妙的方式发生的情况,"p = o" 的逻辑是 QObject 存储在另一个 "place" 中,而在您的示例中 "place" 是具有自己范围的 lambda(与部分类似)。具体来说,在您的示例中,创建了一个新的 window 来破坏前一个,导致始终显示单个 window。
解决方案:
一个可能的解决方案是防止删除第一个 window 并使用标志创建新的 window:
from PyQt5 import QtCore, QtGui, QtWidgets
<b>flag = False</b>
# ...
def open_otherwindow():
<b>global w, flag
if not flag:
w = OtherWindow()
w.show()
flag = True</b>
# ...