如何正确退出 Pyside2 多线程 GUI 应用程序?

Posted

技术标签:

【中文标题】如何正确退出 Pyside2 多线程 GUI 应用程序?【英文标题】:How to properly exit from Pyside2 multi-thread GUI application? 【发布时间】:2021-01-09 14:28:42 【问题描述】:

我正在使用 pyside2 并编写了一个应用程序,其中有几个线程使用队列在它们之间进行通信。一个线程从串行端口读取数据并将数据传递给另一个线程,该线程解析它们并在绘图之前进行一些计算。从我的 app_main.py 文件中:

import sys
# omissis various import statements

class MainWindow(QMainWindow, Ui_MainWindow):
    serialRxQu = Queue()  # serial FIFO RX Queue
    serialWo = ""

    def __init__(self, *args, **kwargs):
        Ui_MainWindow.__init__(self)
        QMainWindow.__init__(self)
        # omissis, various ui initializations

         # Visualization Worker Thread, started as soon as the thread pool is started.
        self.viewWo = ViewWorker(self.serialRxQu, self.canvas, self.textEdit)
        # serial Worker Thread started later
        self.serialWo = SerialWorker(self.serialRxQu, self.textEdit)

        self.threadpool = QThreadPool()
        self.threadpool.start(self.viewWo)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

我省略了一些对问题无用的部分,如果不包含其他文件,它们将难以解释。

应用程序 exec 是使用 `sys.exit(app.exec_()) 调用的,据我了解,这意味着当我关闭应用程序的主窗口时,事件调度程序将关闭并传递返回代码到 sys.exit 应该终止应用程序的执行。

我尝试使用命令python app_main.py 从 Windows 中的命令行启动应用程序,我看到的是,当我关闭应用程序的主窗口时,进程似乎挂在终端中,我从来没有得到提示返回。 我还尝试将sys.exit()app.exec_() 分开,如下所示:

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    style_man = StyleManager(app)
    window = MainWindow()
    window.show()
    # sys.exit(app.exec_())
    app.exec_()
    print("I pass here!")
    sys.exit(0)

我在终端上看到了 I pass here! 的打印字样,但在这种情况下,我再也没有收到提示。我确实希望sys.exit() 调用会终止进程,但也许有一些东西挂起并阻止应用程序关闭? 可能是事件调度程序仍在运行吗?或者任何与线程或队列相关的东西? 还是我应该使用不同于sys.exit() 的东西?

我希望能正确解释我的情况和我的疑问,否则我会尝试纠正我的问题。

【问题讨论】:

【参考方案1】:

经过一些故障排除后,我认为我发现了我的程序的问题。

对于线程,我使用从 QRunnable 派生的对象,线程是无限循环,一个监听串行,另一个在新数据到达时绘制数据。

作为无限循环,线程继续运行,并且由于它们每个人都有一个计时器对象,因此计时器对象可能会阻止执行调度程序终止。

为了解决这个问题,我在线程循环中添加了一个标志并使用了应用程序的关闭事件

    def closeEvent(self, event):
        self.set_finish_signal()
        app.exit(0)

    def set_finish_signal(self):
        self.viewWo.terminate_thread()
        self.serialWo.terminate_thread()

运行循环条件在启动时始终为 False,terminate_thread 函数将信号更改为 True 并且 Run 终止,释放计时器资源并允许执行正确终止。

【讨论】:

以上是关于如何正确退出 Pyside2 多线程 GUI 应用程序?的主要内容,如果未能解决你的问题,请参考以下文章

如何从具有 PySide2 GUI 的 python 脚本构建一个 mac os 应用程序?

PySide2:不能在不退出 APP 的情况下关闭 QMessageBox

无法使用 QMutex 锁定变量

带有 PySide2 的文件浏览器:获取文件的路径,然后终止 GUI

如何使用pytest正确退出队列和Qthread进行测试?

使用 Pyside2 QThread 线程化时,Scipy curve_fit 崩溃