1

如何让 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 绑定导入就是这样完成的,以使此代码在您的端更容易运行;)

4

2 回答 2

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)
于 2017-06-04T17:06:37.623 回答
0

原始代码不起作用的原因是因为您被禁止从辅助线程调用 Qt GUI 方法(Qt 方法不是线程安全的)。有几种解决方案,例如:

  • 使用 a QTimer(因为QTimer不使用线程,只是将主线程中存在的 Qt 事件循环中的未来事件排队,这个解决方案应该解决 segfaults - 请参阅@ingvar的答案)

  • 使用一个QThread和一个自定义的 Qt 信号。Qt 信号是线程安全的,因此您可以将辅助线程中的信号连接到close主线程中的插槽。然后您可以在 30 秒后从辅助线程内发出信号以触发关闭。

您应该采取的方法取决于您的最终目标(以及这是一个“玩具”示例的多少),但现在我认为没有理由拥有辅助线程。你应该只用@ingvar的回答:)

PS 无论哪种方式,在 Qt 中使用 python 线程都是一个坏主意。坚持使用,QThread除非线程完全独​​立于 GUI。

于 2017-06-14T06:00:01.910 回答