首页 > 解决方案 > 非阻塞 PyQT 作为一个额外的 GUI 可视化主进程的结果?

问题描述

我有一个主要过程来做一些事情(例如分析数据),它单独运行就好了。我还想制作一个简单的 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,我可以将 2 行gui = GUI(task)gui.update(data, result).

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

我考虑过的一些选项:

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

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

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

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

标签: pythonuser-interfacepyqt5

解决方案


在 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 库)。

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

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


推荐阅读