繁体   English   中英

如何使用线程自动关闭PyQt / PySide窗口?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM