[英]How to auto-close PyQt/PySide window using thread?
如何在30秒后自动关闭PyQt5窗口,并仍然保持窗口对交互的响应?
我正在创建一个睡眠30秒的线程,然后该线程调用窗口的close()
函数。 现在,代码挂在self.close()
:
Exception in thread Thread-1:
Traceback (most recent call last):
File "C:\Users\fredrik\.conda\envs\pysidedev_py27\lib\threading.py", line 801, in __bootstrap_inner
self.run()
File "C:\Users\fredrik\.conda\envs\pysidedev_py27\lib\threading.py", line 754, in run
self.__target(*self.__args, **self.__kwargs)
File "C:\Users\fredrik\Desktop\timer.py", line 47, in <lambda>
my_win.execute_function_threaded(func=lambda: my_win.auto_close(n=3))
File "C:\Users\fredrik\Desktop\timer.py", line 36, in auto_close
self.close() # hangs
RuntimeError: Internal C++ object (MyWindow) already deleted.
我也尝试将线程移出window对象,但随后仍遇到window.close()
的挂起。
我究竟做错了什么?
该代码必须适用于Python 2.7和3.5。
import sys
import time
from threading import Thread
try:
from PyQt5 import QtWidgets
except ImportError:
try:
from PySide2 import QtWidgets
except ImportError:
try:
from PyQt4 import QtGui as QtWidgets
except ImportError:
try:
from PySide import QtGui as QtWidgets
except ImportError:
print('giving up!')
class MyWindow(QtWidgets.QMainWindow):
"""Auto-closing window"""
def __init__(self, parent=None):
super(MyWindow, self).__init__(parent)
def closeEvent(self, event):
"""Delete object when closed"""
self.deleteLater()
def auto_close(self, n):
"""Close self in n seconds"""
print('going to sleep')
for i in range(n):
print('sleeping...')
time.sleep(1)
print('done sleeping!')
self.close() # hangs
def execute_function_threaded(self, func):
"""Run given function in thread"""
self.t = Thread(target=func)
self.t.start()
print('thread started')
app = QtWidgets.QApplication(sys.argv)
my_win = MyWindow()
my_win.show()
my_win.execute_function_threaded(func=lambda: my_win.auto_close(n=3))
sys.exit(app.exec_())
请注意,Qt绑定的导入就是这样完成的,以使此代码在您的一端更容易运行;)
尝试使用以下代码代替my_win.execute_function_threaded
调用:
class MyWindow(QtWidgets.QMainWindow):
"""Auto-closing window"""
def __init__(self, parent=None):
super(MyWindow, self).__init__(parent)
QtCore.QTimer.singleShot(30000, self.close)
原始代码不起作用的原因是,禁止您从辅助线程调用Qt GUI方法(Qt方法不是线程安全的)。 有几种解决方案,例如:
使用QTimer
(因为QTimer
不使用线程,而只是在主线程中存在的Qt事件循环中排队将来的事件,所以此解决方案应解决segfaults-请参阅@ingvar的答案)
使用QThread
和自定义Qt信号。 Qt信号是线程安全的,因此您可以将辅助线程中的信号连接到主线程中的close
插槽。 然后,您可以在30秒后从辅助线程内发出信号以触发关闭。
您应该采用的方法取决于最终目标(以及这个“玩具”示例的数量),但是到目前为止,我认为没有理由拥有辅助线程。 您应该只使用@ingvar的答案:)
PS无论哪种方式,在Qt中使用python线程都是一个坏主意。 除非线程完全独立于GUI,否则请坚持使用QThread
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.