简体   繁体   English

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

[英]How to auto-close PyQt/PySide window using thread?

How can I make a PyQt5 window automatically close after 30 seconds and still keep the window respond to interaction? 如何在30秒后自动关闭PyQt5窗口,并仍然保持窗口对交互的响应?

I'm creating a thread which sleeps for 30 seconds, and then it calls the close() function of the window. 我正在创建一个睡眠30秒的线程,然后该线程调用窗口的close()函数。 Right now, the code hangs at self.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.

I also tried moving the threading out of the window object but then I'm still experiencing a hang on window.close() . 我也尝试将线程移出window对象,但随后仍遇到window.close()的挂起。

What am I doing wrong? 我究竟做错了什么?

The code must work with Python 2.7 and 3.5. 该代码必须适用于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_())

Please note that the Qt binding imports are just done like that in order to make this code run easier on your end ;) 请注意,Qt绑定的导入就是这样完成的,以使此代码在您的一端更容易运行;)

Try following code instead of my_win.execute_function_threaded call: 尝试使用以下代码代替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)

The reason the original code doesn't work is because you are forbidden to call Qt GUI methods from secondary threads (Qt methods are not thread safe). 原始代码不起作用的原因是,禁止您从辅助线程调用Qt GUI方法(Qt方法不是线程安全的)。 There are several solutions to this, such as: 有几种解决方案,例如:

  • using a QTimer (since QTimer doesn't use threads, and just queues up a future event in the Qt event loop that exists in the main thread, this solution should solve the segfaults - see the answer by @ingvar ) 使用QTimer (因为QTimer不使用线程,而只是在主线程中存在的Qt事件循环中排队将来的事件,所以此解决方案应解决segfaults-请参阅@ingvar的答案)

  • using a QThread and a custom Qt signal. 使用QThread和自定义Qt信号。 Qt signals are thread safe, so you can connect a signal in a secondary thread to the close slot in the primary thread. Qt信号是线程安全的,因此您可以将辅助线程中的信号连接到主线程中的close插槽。 Then you can emit the signal from within the secondary thread after 30 seconds to trigger the close. 然后,您可以在30秒后从辅助线程内发出信号以触发关闭。

The approach you should take depends on your end goal (and how much of a 'toy' example this is), but for now I see no reason to have a secondary thread. 您应该采用的方法取决于最终目标(以及这个“玩具”示例的数量),但是到目前为止,我认为没有理由拥有辅助线程。 You should just go with @ingvar's answer :) 您应该只使用@ingvar的答案:)

PS Either way, using python threads with Qt is a bad idea. PS无论哪种方式,在Qt中使用python线程都是一个坏主意。 Stick to QThread unless the thread is completely independent of the GUI. 除非线程完全独​​立于GUI,否则请坚持使用QThread

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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