首页 > 解决方案 > pyqtgraph 动态绘图:添加行以打开 GUI

问题描述

我正在尝试将新行添加到现有且打开的绘图中。

我写了一个看门狗来监视一个包含测量数据的文件夹。每隔几秒就会有一个新的数据文件。我尝试生成的应用程序应该在看门狗触发时读取文件并将数据添加到绘图中。

更新现有数据时,使用 QTimer 和其他东西的动态绘图很容易,但我没有得到新行的钩子。

另外,当我想在 _exec() 上进行绘图时运行脚本时,是否必须使用多线程?

from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pq
import sys
import numpy as np
import time


class Plot2d(object):
    def __init__(self):
        self.traces = dict()
        self.num = 0
        pq.setConfigOptions(antialias=True)
        self.app = QtGui.QApplication(sys.argv)
        self.win = pq.GraphicsWindow(title='examples')
        self.win.resize(1000, 600)
        self.win.setWindowTitle('Windowtitle')
        self.canvas = self.win.addPlot(title='Plot')


    def starter(self):
        if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
            QtGui.QApplication.instance().exec_()

    def trace(self, name, dataset_x, dataset_y):
        if name in self.traces:
            self.traces[name].setData(dataset_x, dataset_y)
        else:
            self.traces[name] = self.canvas.plot(pen='y')

    def update(self, i):
        x_data = np.arange(0, 3.0, 0.01)
        y_data = x_data*i
        self.trace(str(i), x_data, y_data)
        self.trace(str(i), x_data, y_data)


if __name__ == '__main__':
    p = Plot2d()
    p.update(1)
    p.starter()
    time.sleep(1)
    p.update(2)

这是我尝试过的。当目录中有新数据可用时,应该由看门狗调用更新函数。

import time as time
from watchdog.observers import Observer
from watchdog.events import PatternMatchingEventHandler

if __name__ == "__main__":
    patterns = "*"
    ignore_patterns = ""
    ignore_directories = False
    case_sensitive = True
    go_recursively = False
    my_event_handler = PatternMatchingEventHandler(patterns, ignore_patterns, ignore_directories, case_sensitive)

    def on_created(event):
        print(f" {event.src_path} has been created!") #this function should call the plot update

    my_event_handler.on_created = on_created
    path = (r'C:\Users\...') #path from GUI at some point

    my_observer = Observer()
    my_observer.schedule(my_event_handler, path, recursive=go_recursively)
    my_observer.start()

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        my_observer.stop()
        my_observer.join()

标签: python-3.xuser-interfaceplotpyqtgraph

解决方案


exec_() 方法是阻塞的,所以 starter() 也会阻塞,并且会在关闭窗口时解锁,这意味着 starter 之后的所有代码只有在关闭窗口后才会执行。另一方面,您不应该在 GUI 线程中使用 time.sleep,因为它会阻止 GUI 事件循环的执行。

根据所使用的技术,提供了更新元素的方法,在 Qt 的情况下,适当的方法是使用信号,以便将看门狗的所有逻辑封装在 QObject 中,该 QObject 将通过信号将信息传输到 GUI , 然后 GUI 会使用该信息进行绘图。

import sys

from watchdog.observers import Observer
from watchdog.events import PatternMatchingEventHandler

from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pq
import numpy as np


class QObserver(QtCore.QObject):
    dataChanged = QtCore.pyqtSignal(object)

    def __init__(self, parent=None):
        super(QObserver, self).__init__(parent)
        patterns = "*"
        ignore_patterns = ""
        ignore_directories = False
        case_sensitive = True
        go_recursively = False

        path = r"C:\Users\..."  # path from GUI at some point

        event_handler = PatternMatchingEventHandler(
            patterns, ignore_patterns, ignore_directories, case_sensitive
        )
        event_handler.on_created = self.on_created
        self.observer = Observer()
        self.observer.schedule(event_handler, path, recursive=go_recursively)
        self.observer.start()

    def on_created(self, event):
        print(
            f" {event.src_path} has been created!"
        )  # this function should call the plot update
        self.dataChanged.emit(np.random.randint(1, 5))


class Plot2d(QtCore.QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.traces = dict()
        self.num = 0
        pq.setConfigOptions(antialias=True)
        self.app = QtGui.QApplication(sys.argv)
        self.win = pq.GraphicsWindow(title="examples")
        self.win.resize(1000, 600)
        self.win.setWindowTitle("Windowtitle")
        self.canvas = self.win.addPlot(title="Plot")

    def starter(self):
        if (sys.flags.interactive != 1) or not hasattr(QtCore, "PYQT_VERSION"):
            QtGui.QApplication.instance().exec_()

    def trace(self, name, dataset_x, dataset_y):
        if name in self.traces:
            self.traces[name].setData(dataset_x, dataset_y)
        else:
            self.traces[name] = self.canvas.plot(pen="y")

    @QtCore.pyqtSlot(object)
    def update(self, i):
        x_data = np.arange(0, 3.0, 0.01)
        y_data = x_data * i
        self.trace(str(i), x_data, y_data)
        self.trace(str(i), x_data, y_data)


if __name__ == "__main__":
    p = Plot2d()
    qobserver = QObserver()
    qobserver.dataChanged.connect(p.update)
    p.starter()

推荐阅读