非阻塞 PyQT 作为一个额外的 GUI 可视化主进程的结果?

Posted

技术标签:

【中文标题】非阻塞 PyQT 作为一个额外的 GUI 可视化主进程的结果?【英文标题】:Non-blocking PyQT as an additional GUI that visualizes the result from a main process? 【发布时间】:2021-05-18 15:14:49 【问题描述】:

我有一个主进程来做一些事情(例如分析数据),它单独运行就很好。我还想制作一个简单的 GUI,使用 PyQT5 显示主要任务的结果。这个想法是 GUI 不应该干扰主进程,也就是说,如果我删除 GUI,它不应该对主进程造成任何问题。

主流程的代码很简单:

if __name__ == '__main__':
    # initialize the object that performs the main task
    tasker = Task()

    # the graphical interface to visualize the result of tasker
    gui = GUI(task)  # GUI is a separate class that keeps a reference to tasker

    # read the input data and do stuff on each new data instance
    for f in listdir(inrepo):
        data = read_new_data(f)  # an utility function that reads new data from file
        result = tasker.process(data)  # tasker processes the new data and return some results
        gui.update(data, result)  # pass the data and result in the GUI to update it

GUI类的代码比较长,这里只贴了几行,但是我跳过的那几行只是创建小部件,没什么花哨的(我还没有连接任何事件)

class GUI(QApplication):

    def __init__(self, tasker):
        """Initialize the application"""

        super().__init__([])

        self.tasker = tasker

        # define the main window
        self.window = QWidget()
        self.window.setWindowTitle('GUI')

        ...  # layout and components etc.

        # show stuff
        self.window.show()
        self.exec()

所以我希望 GUI 完全独立于我的主进程。例如,如果我不再需要 GUI,我可以将 gui = GUI(task)gui.update(data, result) 这两行注释掉。

但是,问题是启动 GUI 会阻塞整个过程(我认为是因为 GUI.__init__ 中的 self.exec(),所以我的主进程无法继续循环数据。请告诉我如何制作PyQT 非阻塞?甚至可行吗?

我考虑过的一些选项:

    线程:对于我的用例来说,它似乎比必要的复杂,它可能会使从线程引用task 实例变得困难。 task 的所有新更新都应反映在 GUI 中。如果我没记错的话,PyQT 的应用程序已经在一个线程上运行。所以多级线程可能会很麻烦。

    从另一个 Python 进程运行 GUI,通过共享文件夹进行通信:可能会导致高延迟。来自task 的任何新数据和结果都应立即反映在 GUI 中。写入文件然后从文件读取然后更新 GUI 会导致一些延迟。

    GUI 中执行任务:我可以使用一些超时事件来定期读取新数据并在它们上运行task,但是一切都在很大程度上取决于 GUI,我可以'如果我不再需要 GUI,请不要将其注释掉。

非常感谢任何建议!非常感谢!

【问题讨论】:

【参考方案1】:

在 GUI 模式/控制台模式之间切换程序通常不像注释掉一些行那么简单。 PyQt 尤其不允许您从主线程以外的任何地方运行 GUI 循环。不过,并不是所有的希望都落空了——这只是意味着您应该尽早决定您的程序是作为控制台应用程序还是作为 GUI 运行。

您可以在代码中创建一个“开关”来告诉您的代码如何执行,而不是依赖注释掉代码。一种方法是在执行代码时检查命令行参数,例如:

import sys

if "--headless" in sys.argv[1:]:  # checking the command-line arguments
    run_code_without_gui()
else:
    run_code_with_gui()

这样,以python mycode.py --headless 执行代码将在没有GUI 的情况下执行它(通过run_code_without_gui 函数),而执行python mycode.py 将作为GUI 运行它(通过run_code_with_gui 函数)。 (虽然如果你真的要解析命令行参数,我推荐使用argparse library)。

您甚至可以使分析代码与 GUI 代码完全分离,这样您只需在没有 GUI 的情况下从 analysis.py 运行一个函数,并让 GUI 调用完全相同的函数,例如用户点击“分析”按钮。

另外需要注意的是,如果您的分析代码需要很长时间才能执行,它可能会无意中阻塞 GUI。在这种情况下,您应该在一个单独的“工作”线程中运行分析代码,该线程在单击“分析”按钮时产生,在执行时保持 GUI 响应。如果您希望分析无限期地与 GUI 一起运行,这可能是您要走的路 - 在向用户显示 GUI 的同时创建用于分析的工作线程。

【讨论】:

我明白了。非常感谢您的全面回答!我真的很感激!

以上是关于非阻塞 PyQT 作为一个额外的 GUI 可视化主进程的结果?的主要内容,如果未能解决你的问题,请参考以下文章

在我的 PyQt5 GUI(非基于 Web 的应用程序)中嵌入图形(数据可视化)的最佳方式

基于multiprocessing和threading实现非阻塞的GUI界面显示

当阻塞任务与 GUI 相关时,如何保持 PyQt GUI 响应?

PyQt界面编程应用与实践

PyQt5应用与实践

Matplotlib在PyQt4的应用