【问题标题】:pyQt and threading application crashpyQt 和线程应用程序崩溃
【发布时间】:2016-07-01 15:20:15
【问题描述】:

我写了一个简单的程序,它有 pyQt 界面和 2 个按钮(开始和取消)。开始按钮在后台运行一些计算(通过启动更新功能),并且由于线程,我仍然可以使用 UI。 但应用程序在 10 秒 - 2 分钟后崩溃。 UI 消失,程序关闭。

当我使用 pythonw 运行应用程序时,控制台线程在约 25 秒后没有崩溃,但 gui 仍然有效。

#!/usr/bin/python
import threading
import sys
from PyQt4 import QtGui, QtCore
import time
import os


class Class(QtGui.QWidget):

    def __init__(self):
        #Some init variables
        self.initUI()

    def initUI(self):
        #some UI
        self.show()

    def update(self,stop_event):
            while True and not stop_event.isSet():
                self.updateSpeed()
                self.updateDistance()
                self.printLogs()
                self.saveCSV()
                self.guiUpdate()
                time.sleep(1)

    #gui button function
    def initiate(self):
        self.stop_event = threading.Event()
        self.c_thread = threading.Thread(target = self.update, args=(self.stop_event,))
        self.c_thread.start()

    #Also gui button function
    def cancelTracking(self):
        self.stop_event.set()
        self.close()

def main():

    app = QtGui.QApplication(sys.argv)
    ex = Class()
    sys.exit(app.exec_())
    ex.update()

if __name__ == '__main__':
    main()

我不知道我的线程是否正确。我在堆栈上找到了这样的例子。我对python很陌生,我是第一次使用线程。

【问题讨论】:

    标签: multithreading python-2.7 pyqt


    【解决方案1】:

    这很可能是由于在您的单独线程中调用了 GUI 函数。 PyQt GUI 调用,如 setText() 上的 QLineEdit 不允许来自线程。任何在主线程之外绘制 PyQt 的东西都不起作用。解决此问题的一种方法是让您的线程在数据准备好时发出信号以更新 GUI。另一种方法是让计时器定期检查新数据并在一定时间后更新paintEvent。

    ========== 编辑 ===========

    为了解决这个问题,我创建了一个名为 qt_thread_updater 的库。 https://github.com/justengel/qt_thread_updater 这通过连续运行 QTimer 来工作。当您调用call_latest 时,QTimer 将在主线程中运行该函数。

    from qt_thread_updater import get_updater
    
    lbl = QtWidgets.QLabel('Value: 1')
    
    counter = {'a': 1}
    
    def run_thread():
        while True:
            text = 'Value: {}'.format(counter['a'])
            get_updater().call_latest(lbl.setText, text)
            counter['a'] += 1
            time.sleep(0.1)
    
    th = threading.Thread(target=run_thread)
    th.start()
    

    ========== 结束编辑 ==========

    #!/usr/bin/python
    import threading
    import sys
    from PyQt4 import QtGui, QtCore
    import time
    import os
    
    
    class Class(QtGui.QWidget):
    
        display_update = QtCore.pyqtSignal() # ADDED
    
        def __init__(self):
            #Some init variables
            self.initUI()
    
        def initUI(self):
            #some UI
            self.display_update.connect(self.guiUpdate) # ADDED
            self.show()
    
        def update(self):
            while True and not self.stop_event.isSet():
                self.updateSpeed()
                self.updateDistance()
                self.printLogs()
                self.saveCSV()
    
                # self.guiUpdate()
                self.display_update.emit() # ADDED
    
                time.sleep(1)
    
        #gui button function
        def initiate(self):
            self.stop_event = threading.Event()
            self.c_thread = threading.Thread(target = self.update)
            self.c_thread.start()
    
        #Also gui button function
        def cancelTracking(self):
            self.stop_event.set()
            self.close()
    
    def main():
    
        app = QtGui.QApplication(sys.argv)
        ex = Class()
        sys.exit(app.exec_())
        # ex.update() # - this does nothing
    
    if __name__ == '__main__':
        main()
    

    可能发生的另一件事是由于两个线程试图访问同一个变量而导致死锁。我读过这在 python 中不应该是可能的,但是我从 PySide 和其他 Python C 扩展库的组合中体验到了它。

    可能还想在关闭时加入线程或在程序关闭之前使用QtGui.QApplication.aboutToQuit 信号加入线程。

    【讨论】:

    • 谢谢!它现在可以正常工作了;)不再崩溃
    • 伟大的洞察力!有同样的问题:D
    • 我最终创建了一个库来解决这个问题。 github.com/justengel/qt_thread_updater
    【解决方案2】:

    Qt documentation for QThreads 提供了两种流行的线程使用模式。您可以继承 QThread(旧方法),也可以使用 Worker 模型,在其中创建自定义 QObject 与您的工作函数并在单独的 QThread 中运行它们。

    在任何一种情况下,您都无法从后台线程直接更新 GUI,因此在您的 update 函数中,如果尝试更改任何 GUI 元素,guiUpdate 调用很可能会使 Qt 崩溃。

    运行后台进程的正确方法是使用两种QThread 模式之一并通过Signals 和Slots 与主GUI 线程通信。

    另外,在下面的代码中,

    app = QtGui.QApplication(sys.argv)
    ex = Class()
    sys.exit(app.exec_())
    ex.update()
    

    app.exec_ 启动事件循环并阻塞直到 Qt 退出。在 Qt 退出并且 ex 窗口已被删除之前,Python 不会运行 ex.update() 命令,因此您应该删除该命令。

    【讨论】:

    • 你,是的。我忘记了 ex.update()。我现在从 GUI 按钮使用此功能,因此我将其删除。也感谢您的提示!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-12
    • 2018-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-09
    相关资源
    最近更新 更多