如何使用线程自动关闭 PyQt/PySide 窗口?
Posted
技术标签:
【中文标题】如何使用线程自动关闭 PyQt/PySide 窗口?【英文标题】:How to auto-close PyQt/PySide window using thread? 【发布时间】:2017-05-30 15:55:20 【问题描述】:如何让 PyQt5 窗口在 30 秒后自动关闭,并且仍然保持窗口响应交互?
我正在创建一个休眠 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.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 绑定导入就是这样完成的,以便让这段代码更容易运行;)
【问题讨论】:
我已经测试了代码,它不会阻塞小部件,你可以评论一下你有,PyQt 的版本,Python 的版本 @eyllanesc 不确定您的意思,无论我正在测试的 Python 版本或 Qt 绑定/版本如何,窗口都会在我这边崩溃。例如,我尝试了 Python 2.7/PySide、Python 3.5/PyQt5,其中窗口崩溃。 【参考方案1】:尝试以下代码而不是 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)
【讨论】:
【参考方案2】:原始代码不起作用的原因是因为您被禁止从辅助线程调用 Qt GUI 方法(Qt 方法不是线程安全的)。有几种解决方案,例如:
使用QTimer
(因为QTimer
不使用线程,只是将主线程中存在的Qt 事件循环中的未来事件排队,此解决方案应该解决segfaults - 请参阅由@ingvar回答)
使用 QThread
和自定义 Qt 信号。 Qt 信号是线程安全的,因此您可以将辅助线程中的信号连接到主线程中的close
插槽。然后您可以在 30 秒后从辅助线程内发出信号以触发关闭。
您应该采取的方法取决于您的最终目标(以及这是多少“玩具”示例),但目前我认为没有理由拥有辅助线程。你应该选择@ingvar 的回答:)
附:无论哪种方式,在 Qt 中使用 python 线程都是一个坏主意。除非线程完全独立于 GUI,否则请坚持使用 QThread
。
【讨论】:
以上是关于如何使用线程自动关闭 PyQt/PySide 窗口?的主要内容,如果未能解决你的问题,请参考以下文章
关闭 PyQt 对话框会终止父进程? (PyQt4 / Pyside) 带有示例代码
Qt/PyQt4/PySide QDateEdit 日历弹窗掉屏
如何使用 PyQt/PySide 获取与特定文件类型关联的图标?