【问题标题】:PyQt5 Best way to plot data without freezing GUI [closed]PyQt5 在不冻结 GUI 的情况下绘制数据的最佳方法 [关闭]
【发布时间】:2021-08-30 11:38:20
【问题描述】:

我正在尝试实现类似于this 的目标。具体来说,我有一个充满数据的队列,我想实时绘制这些数据。 我目前有一个使用 QTimer(并且没有线程)的工作版本,以便每半秒调用一次函数。此函数将项目从队列中取出(当它非空时)并绘制它们。虽然这肯定有效,但我想知道是否有更好的方法可以使用(涉及线程等)。我主要担心的是我害怕我的方法虽然有效,但会使事情变得比他们需要的更迟钝。我尝试了上面给出的方法,但是(如果我没有搞砸的话,我可能就是这样):

  1. 终止线程有问题(特别是:“QObject::killTimer: 定时器不能从另一个线程停止”)
  2. 正如最后一条评论所指出的那样,该进程甚至似乎都没有发生在其他线程中(在其他线程中添加睡眠调用会冻结整个 GUI)。

关键是,我想知道是否有比我目前的方法“更好”的方法来做到这一点(例如上面给出的方法,如果我可以让它在我的情况下正常工作,或者有一个检查线程队列不断地,并且只要它非空,就会发出一个值而不是有一个计时器)。

我没有给出一个最小的可重现示例,因为这不一定是特定代码位的特定问题,更多的是关于正确方法的问题。

编辑:基于this,最好一次只处理队列中的一个事件并将计时器的超时设置为 0 毫秒(而不是每半秒循环一次直到队列不为空) ?

【问题讨论】:

    标签: python multithreading plot timer pyqt5


    【解决方案1】:

    这取决于您希望如何绘制数据。队列中的每个项目都是新图还是只需要显示的最新数据?如果您只需要显示最新数据,那么这是一个简单的过程。

    我创建了一个库 qt_thread_updater 来帮助解决这个问题。 qt_thread_updater 使用一个定期运行的计时器来调用主线程中的函数。它是线程安全的。但是,qt_thread_updater 将在稍后的超时时间调用该函数,因此 GUI 更新将延迟几分之一秒。

    import multiprocessing as mp
    import threading
    from qtpy import QtWidgets
    from qt_thread_updater import get_updater
    
    
    app = QtWidgets.QApplication()
    
    fig = Plot()  # Your plotting library
    fig.show()
    
    def plot_data(data):
        # Plot the data here
        fig.plot(data)    
    
    def read_queue(que, alive):
        while alive.is_set():
            for _ in range(que.qsize()):
                data = que.get()
                get_updater().call_latest(plot_data, data)
            time.sleep(0.1)  # Some delay to let the QEvent loop process events.
    
    # Processed data queue and Event to close down processes
    myque = mp.Queue()
    alive = mp.Event()
    alive.set()
    
    # Read processed data thread and display
    pull_data_th = threading.Thread(target=read_queue, args=(myque, alive))
    pull_data_th.start()
    
    # Worker process
    worker = mp.Process(target=do_work, args=(myque, alive)
    worker.start()
    
    app.exec_()
    alive.clear()
    worker.join()
    pull_data_th.join()
    

    如果您需要显示队列中的每个项目,那么您必须注意性能。如果 QEvent 循环没有时间处理事件并已满,则应用程序将崩溃。如果您的应用程序崩溃,那么您需要降低数据速率或获取更快的绘图库(matplotlib 很慢,pyqtgraph 要快得多)。

    除了将call_latest更改为call_in_main之外,您可以使用与以下相同的代码

    def read_queue(que, alive):
        while alive.is_set():
            for _ in range(que.qsize()):
                data = que.get()
                get_updater().call_in_main(plot_data, data)
    
            # Some delay to let the QEvent loop process events.
            if que.empty():
                time.sleep(0.5)
            else:
                time.sleep(0.001)
    

    【讨论】:

    • 我不确定我是否理解 call_latest() 和 call_in_main() 之间的区别,但这可能与您关于我希望如何绘制数据的问题有关。我想基本上增量地绘制一个函数(如 y=x)。也就是说,我想从 x=0 开始绘制 y 值,而不仅仅是最新的数据点。这些中的任何一个都适用于这个用例吗?此外,您会说从 matplotlib 迁移到 pyqtgraph 还是会导致更流畅的动画?我有大约 20~ 个数字,每个数字都有一个新的 (x,y) 对,每 100-500 毫秒左右绘制一次。
    • @CompareTwo 听起来您想使用call_in_main。基本上qt_thread_updater 计时器将运行 30 赫兹。当超时发生时,call_latest 只会使用最新的数据集调用给定的函数一次。 call_in_main 将多次调用该函数,并确保为每个数据集调用该函数。你可以试试matplotlib。如果您发现性能问题,我会切换到 pyqtgraph。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多