pyqtgraph 滚动图:以块为单位绘制,在当前窗口中仅显示最近的 10 个样本 [英] pyqtgraph scrolling plots: plot in chunks, show only latest 10s samples in current window

查看:70
本文介绍了pyqtgraph 滚动图:以块为单位绘制,在当前窗口中仅显示最近的 10 个样本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在使用 pygtgraph 滚动图时遇到问题

预期结果

预期结果与

I have trouble using pygtgraph scrolling plots

Expected Results

The expected results are quite similar to the pyqtgraph-examples-scrolling plots-plot5

X-values are times, which can be generated by a simple function. Y-Values are random values.

Each 10 seconds samples as one chunk and each plot can have max. 30 seconds samples, which means 3 chunks. The current plot window only shows the latest 10 seconds samples

For example, now there are total 60 seconds samples:

  • Data between 50s-60s will be viewed in the current window
  • Data between 30s-50s could be viewed by using the mouse to drag the x-axis backward
  • Data between 0-30s will not be displayed

My Code

My current code is below, it can only show latest 30s data.

import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
import random


win = pg.GraphicsLayoutWidget(show=True)
win.setWindowTitle('Scrolling Plots')

p1 = win.addPlot()
p1.setYRange(0,10)

xVal = [0]
yVal = [0]

def genTime():  # used to generate time
    t = 0
    while True:
        t += np.random.random_sample()
        yield t
        t = np.ceil(t)

xTime = genTime() 

#=====================================================

viewSize = 10   # current window show only latest 10s data
plotSize = 30   # plot 30s data -> 3 chunk
lstCurves = []  # List for Curves

def update():
    global p1, xVal, yVal, lstCurves

    #for c in lstCurves:
    #    c.setPos(xVal[-1], 0)

    i = np.ceil(xVal[-1]) % viewSize  # e.g. when time is 9.2s -> one 10s view size is full, append to curves list as one chunk
    if i == 0:
        curve = p1.plot()
        lstCurves.append(curve)
        xValLast = xVal[-1]
        yValLast = yVal[-1]

        xVal = [xValLast]
        yVal = [yValLast]

        while len(lstCurves) > 3:  # max 3 chunk (30 s)
            p1.removeItem(lstCurves.pop(0))  # remove the oldest 10s
        
    else:
        curve = lstCurves[-1]    # latest 10s curve
        
    xVal.append(next(xTime))
    yVal.append(random.randint(0,9))
    curve.setData(xVal, yVal)
    print(len(lstCurves))

    
#======================================================

timer = pg.QtCore.QTimer()
timer.timeout.connect(update)
timer.start(1000)


## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
    import sys

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

Problem

I have tried using curve.setPos(xx, 0), It looks like the whole curve is moving along the x-axis, but the mapping relationship between X-value and Y-value is broken

I have also tried using setXRange() to dynamically change x-axis display-range in update() func. But in this case, I can't use the mouse to drag the x-axis back to view the old data any more.

My English is not good, I hope you can understand my question. Any suggestions would be sincerely appreciated!

解决方案

Problem

The reasons your code don't do what you want are:

  • When you drag to view the other chunks, you disable the automatic auto-range of the plot, after that, you will have to manually drag the plot every time you want to see the new data. Also, by default, the auto-range of the plot will cover all the data that you are plotting.
  • When you use the setRange() method inside the update function, it will force that range every time you add another value to the data. Then the drag will not work as you want

What can you do

Well, from my perspective using the mouse drag to visualize the other data is not very convenient, I suggest to use an external widget to control the range of the data you want to view, like, a slider, scroll bar, spin box, ... A QScrollBar can do the job and it will look esthetic in a GUI.

Before my Alternative Solution, I have a suggestion for you:

  • Use objects to create your widget, generate a class and use the variables as attributes, with this you avoid the use of the keyword global, and you could reuse the widget for other purposes.

Alternative Solution

Try this:

import sys
import random
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui

class MyApp(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        ## Creating the Widgets and Layouts
        self.plot_widget = pg.PlotWidget()
        self.layout = QtGui.QVBoxLayout()
        self.sbutton = QtGui.QPushButton("Start / Continue")
        self.ebutton = QtGui.QPushButton("Stop")
        self.timer = pg.QtCore.QTimer()
        self.scroll = QtGui.QScrollBar(QtCore.Qt.Horizontal)
        ## Creating the variables and constants
        self.data = [[0], [random.randint(0,9)]]  ## [xVal, yVal] to create less variables
        self.plot_item = self.plot_widget.plot(*self.data)
        self.plot_widget.setYRange(0, 10)
        self.xTime = self.genTime()
        self.vsize = 10
        self.psize = 30
        ## Building the Widget
        self.setLayout(self.layout)
        self.layout.addWidget(self.sbutton)
        self.layout.addWidget(self.ebutton)
        self.layout.addWidget(self.plot_widget)
        self.layout.addWidget(self.scroll)
        ## Changing some properties of the widgets
        self.plot_widget.setMouseEnabled(x=False, y=False)
        self.ebutton.setEnabled(False)
        self.scroll.setEnabled(False)
        self.scroll.setMaximum(self.psize-self.vsize)
        self.scroll.setValue(self.psize-self.vsize)
        ## Coneccting the signals
        self.sbutton.clicked.connect(self.start)
        self.ebutton.clicked.connect(self.stop)
        self.timer.timeout.connect(self.update)
        self.scroll.valueChanged.connect(self.upd_scroll)

    def genTime(self):  # used to generate time
        t = 0
        while True:
            t += np.random.random_sample()
            yield t
            t = np.ceil(t)

    def upd_scroll(self):
        val = self.scroll.value()
        xmax = np.ceil(self.data[0][-1+self.vsize-self.psize+val])-1
        xmin = xmax-self.vsize
        self.plot_widget.setXRange(xmin, xmax)

    def update(self):
        num = len(self.data[0])
        if num <= self.psize:
            self.plot_item.setData(*self.data)
        else:
            self.plot_item.setData(
                self.data[0][-self.psize:],
                self.data[1][-self.psize:]
            )

        if num == self.vsize:
            self.scroll.setEnabled(True)
        self.data[0].append(next(self.xTime))
        self.data[1].append(random.randint(0,9))
        if num > self.vsize :
            self.upd_scroll()
     
    def start(self):
        self.sbutton.setEnabled(False)
        self.ebutton.setEnabled(True)
        self.timer.start(100)

    def stop(self):
        self.sbutton.setEnabled(True)
        self.ebutton.setEnabled(False)
        self.timer.stop()
        self.upd_scroll()
        
    def closeEvent(self, event):
        self.timer.stop()
        event.accept()

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

It may look like this:

这篇关于pyqtgraph 滚动图:以块为单位绘制,在当前窗口中仅显示最近的 10 个样本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆