如何使用pyqtgraph TimeAxisItem使X轴时间动态刷新

Posted

技术标签:

【中文标题】如何使用pyqtgraph TimeAxisItem使X轴时间动态刷新【英文标题】:How to make the X-axis time dynamically refresh by using pyqtgraph TimeAxisItem 【发布时间】:2019-09-17 01:24:57 【问题描述】:

我将根据一系列数据制作实时曲线。首先,我建立了一个数量字典,里面有 3 组数据。当前程序可以动态绘制曲线。 X轴也可以显示时间,也是实时更新的。但是,X 轴上不同点的时间始终是相同的值。

UNIX_EPOCH_naive = datetime.datetime(1970, 1, 1, 0, 0) #offset-naive datetime
UNIX_EPOCH_offset_aware = datetime.datetime(1970, 1, 1, 0, 0, tzinfo = pytz.utc) #offset-aware datetime
UNIX_EPOCH = UNIX_EPOCH_naive

TS_MULT_us = 1e6

def now_timestamp(ts_mult=TS_MULT_us, epoch=UNIX_EPOCH):
    return(int((datetime.datetime.utcnow() - epoch).total_seconds()*ts_mult))

def int2dt(ts, ts_mult=TS_MULT_us):
    tz = pytz.timezone('Asia/Shanghai')
    user_ts = int(time.time())
    d1 = datetime.datetime.fromtimestamp(float(user_ts))
    d1x = tz.localize(d1)
    return(d1x)

def dt2int(dt, ts_mult=TS_MULT_us, epoch=UNIX_EPOCH):
    delta = dt - epoch
    return(int(delta.total_seconds()*ts_mult))

def td2int(td, ts_mult=TS_MULT_us):
    return(int(td.total_seconds()*ts_mult))

def int2td(ts, ts_mult=TS_MULT_us):
    return(datetime.timedelta(seconds=float(ts)/ts_mult))

class TimeAxisItem(pg.AxisItem):
    def __init__(self, *args, **kwargs):
        super(TimeAxisItem, self).__init__(*args, **kwargs)
    def tickStrings(self, values, scale, spacing):
        return [int2dt(value).strftime("%H:%M:%S") for value in values]

p = win.addPlot(title="Data-Time Graph", axisItems='bottom': TimeAxisItem(orientation='bottom'))
data_dict = 
p.addLegend() 
data_x=[]


def getDate():
    ......
    .....
    curve = p.plot(pen = color[len(data_dict)],name=name)
    data_dict[name] = [curve] # dictionary: key:[curve,[dadta1,data2,...]]
    data_dict[name].append([val])

def addToDisplay():
    p.plot()
    for i in data_dict.items():
        data = i[1][1] #
        curve = i[1][0] #
        if(len(data) > data_frequency):#
             data_y=data[- data_frequency:]
        else:
             data_y = data[:]
        curve.setData(data_y)#

if __name__ == "__main__":
    th= threading.Thread(target=getDate)# 
    th.start()
    timer = pg.QtCore.QTimer()
    timer.timeout.connect(addToDisplay) 
    timer.start(10)

我希望X轴是动态刷新的,右边是最近的时间,左边是过去的时间。

【问题讨论】:

你能帮我吗Help Please in PyQt4 【参考方案1】:

我不完全确定您想要实现什么,因为您的代码没有运行,但您似乎正在尝试创建时间戳图。这是一个使用 TimeAxisItem 来跟踪 X 轴上经过的时间的小部件。

PyQt5

from PyQt5 import QtCore, QtGui, QtWidgets
from threading import Thread
from collections import deque
import pyqtgraph as pg
import numpy as np
import random
import sys
import time

"""Scrolling Timestamp Plot Widget Example"""

class TimeAxisItem(pg.AxisItem):
    """Internal timestamp for x-axis"""
    def __init__(self, *args, **kwargs):
        super(TimeAxisItem, self).__init__(*args, **kwargs)

    def tickStrings(self, values, scale, spacing):
        """Function overloading the weak default version to provide timestamp"""

        return [QtCore.QTime().currentTime().addMSecs(value).toString('mm:ss') for value in values]

class ScrollingTimestampPlot(QtGui.QWidget):
    """Scrolling plot widget with timestamp on x-axis and dynamic y-axis"""

    def __init__(self, parent=None):
        super(ScrollingTimestampPlot, self).__init__(parent)

        # Internal timestamp for x-axis
        self.timestamp = QtCore.QTime()
        self.timestamp.start()

        # Desired Frequency (Hz) = 1 / self.FREQUENCY
        # USE FOR TIME.SLEEP (s)
        self.FREQUENCY = 0.025

        # Screen refresh rate to update plot (ms)
        # self.SCROLLING_TIMESTAMP_PLOT_REFRESH_RATE = 1 / Desired Frequency (Hz) * 1000
        # USE FOR TIMER.TIMER (ms)
        self.SCROLLING_TIMESTAMP_PLOT_REFRESH_RATE = self.FREQUENCY * 1000

        self.DATA_POINTS_TO_DISPLAY = 200

        # Automatically pops from left if length is full
        self.data = deque(maxlen=self.DATA_POINTS_TO_DISPLAY)

        # Create Plot Widget 
        self.scrolling_timestamp_plot_widget = pg.PlotWidget(axisItems='bottom': TimeAxisItem(orientation='bottom'))

        # Enable/disable plot squeeze (Fixed axis movement)
        self.scrolling_timestamp_plot_widget.plotItem.setMouseEnabled(x=False, y=False)
        self.scrolling_timestamp_plot_widget.setTitle('Scrolling Timestamp Plot Example')
        self.scrolling_timestamp_plot_widget.setLabel('left', 'Value')
        self.scrolling_timestamp_plot_widget.setLabel('bottom', 'Time (s)')

        self.scrolling_timestamp_plot = self.scrolling_timestamp_plot_widget.plot()
        self.scrolling_timestamp_plot.setPen(246,212,255)

        self.layout = QtGui.QGridLayout()
        self.layout.addWidget(self.scrolling_timestamp_plot_widget)

        self.read_position_thread()
        self.start()

    def start(self):
        """Update plot"""

        self.position_update_timer = QtCore.QTimer()
        self.position_update_timer.timeout.connect(self.plot_updater)
        self.position_update_timer.start(self.get_scrolling_timestamp_plot_refresh_rate())

    def read_position_thread(self):
        """Read in data using a thread"""

        self.current_position_value = 0
        self.position_update_thread = Thread(target=self.read_position, args=())
        self.position_update_thread.daemon = True
        self.position_update_thread.start()

    def read_position(self):
        frequency = self.get_scrolling_timestamp_plot_frequency()
        while True:
            self.current_position_value = random.randint(1,101) 
            time.sleep(frequency)

    def plot_updater(self):
        self.data_point = float(self.current_position_value)

        self.data.append('x': self.timestamp.elapsed(), 'y': self.data_point)
        self.scrolling_timestamp_plot.setData(x=[item['x'] for item in self.data], y=[item['y'] for item in self.data])

    def clear_scrolling_timestamp_plot(self):
        self.data.clear()

    def get_scrolling_timestamp_plot_frequency(self):
        return self.FREQUENCY

    def get_scrolling_timestamp_plot_refresh_rate(self):
        return self.SCROLLING_TIMESTAMP_PLOT_REFRESH_RATE

    def get_scrolling_timestamp_plot_layout(self):
        return self.layout

    def get_current_position_value(self):
        return self.current_position_value

    def get_scrolling_timestamp_plot_widget(self):
        return self.scrolling_timestamp_plot_widget

# Start Qt event loop unless running in interactive mode or using pyside
if __name__ == '__main__':
    # Create main application window
    app = QtWidgets.QApplication([])
    app.setStyle(QtGui.QStyleFactory.create("Cleanlooks"))
    mw = QtGui.QMainWindow()
    mw.setWindowTitle('Scrolling Plot Example')

    # Create scrolling plot
    scrolling_timestamp_plot_widget = ScrollingTimestampPlot()

    # Create and set widget layout
    # Main widget container
    cw = QtGui.QWidget()
    ml = QtGui.QGridLayout()
    cw.setLayout(ml)
    mw.setCentralWidget(cw)

    # Can use either to add plot to main layout
    #ml.addWidget(scrolling_timestamp_plot_widget.get_scrolling_timestamp_plot_widget(),0,0)
    ml.addLayout(scrolling_timestamp_plot_widget.get_scrolling_timestamp_plot_layout(),0,0)
    mw.show()

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

PyQt4

from PyQt4 import QtCore, QtGui
from threading import Thread
from collections import deque
import pyqtgraph as pg
import numpy as np
import random
import sys
import time

"""Scrolling Timestamp Plot Widget Example"""

class TimeAxisItem(pg.AxisItem):
    """Internal timestamp for x-axis"""
    def __init__(self, *args, **kwargs):
        super(TimeAxisItem, self).__init__(*args, **kwargs)

    def tickStrings(self, values, scale, spacing):
        """Function overloading the weak default version to provide timestamp"""

        return [QtCore.QTime().addMSecs(value).toString('mm:ss') for value in values]

class ScrollingTimestampPlot(QtGui.QWidget):
    """Scrolling plot widget with timestamp on x-axis and dynamic y-axis"""

    def __init__(self, parent=None):
        super(ScrollingTimestampPlot, self).__init__(parent)

        # Internal timestamp for x-axis
        self.timestamp = QtCore.QTime()
        self.timestamp.start()

        # Desired Frequency (Hz) = 1 / self.FREQUENCY
        # USE FOR TIME.SLEEP (s)
        self.FREQUENCY = 0.025

        # Screen refresh rate to update plot (ms)
        # self.SCROLLING_TIMESTAMP_PLOT_REFRESH_RATE = 1 / Desired Frequency (Hz) * 1000
        # USE FOR TIMER.TIMER (ms)
        self.SCROLLING_TIMESTAMP_PLOT_REFRESH_RATE = self.FREQUENCY * 1000

        self.DATA_POINTS_TO_DISPLAY = 200

        # Automatically pops from left if length is full
        self.data = deque(maxlen=self.DATA_POINTS_TO_DISPLAY)

        # Create Plot Widget 
        self.scrolling_timestamp_plot_widget = pg.PlotWidget(axisItems='bottom': TimeAxisItem(orientation='bottom'))

        # Enable/disable plot squeeze (Fixed axis movement)
        self.scrolling_timestamp_plot_widget.plotItem.setMouseEnabled(x=False, y=False)
        self.scrolling_timestamp_plot_widget.setTitle('Scrolling Timestamp Plot Example')
        self.scrolling_timestamp_plot_widget.setLabel('left', 'Value')
        self.scrolling_timestamp_plot_widget.setLabel('bottom', 'Time (s)')

        self.scrolling_timestamp_plot = self.scrolling_timestamp_plot_widget.plot()
        self.scrolling_timestamp_plot.setPen(246,212,255)

        self.layout = QtGui.QGridLayout()
        self.layout.addWidget(self.scrolling_timestamp_plot_widget)

        self.read_position_thread()
        self.start()

    def start(self):
        """Update plot"""

        self.position_update_timer = QtCore.QTimer()
        self.position_update_timer.timeout.connect(self.plot_updater)
        self.position_update_timer.start(self.get_scrolling_timestamp_plot_refresh_rate())

    def read_position_thread(self):
        """Read in data using a thread"""

        self.current_position_value = 0
        self.position_update_thread = Thread(target=self.read_position, args=())
        self.position_update_thread.daemon = True
        self.position_update_thread.start()

    def read_position(self):
        frequency = self.get_scrolling_timestamp_plot_frequency()
        while True:
            self.current_position_value = random.randint(1,101) 
            time.sleep(frequency)

    def plot_updater(self):
        self.data_point = float(self.current_position_value)

        self.data.append('x': self.timestamp.elapsed(), 'y': self.data_point)
        self.scrolling_timestamp_plot.setData(x=[item['x'] for item in self.data], y=[item['y'] for item in self.data])

    def clear_scrolling_timestamp_plot(self):
        self.data.clear()

    def get_scrolling_timestamp_plot_frequency(self):
        return self.FREQUENCY

    def get_scrolling_timestamp_plot_refresh_rate(self):
        return self.SCROLLING_TIMESTAMP_PLOT_REFRESH_RATE

    def get_scrolling_timestamp_plot_layout(self):
        return self.layout

    def get_current_position_value(self):
        return self.current_position_value

    def get_scrolling_timestamp_plot_widget(self):
        return self.scrolling_timestamp_plot_widget

# Start Qt event loop unless running in interactive mode or using pyside
if __name__ == '__main__':
    # Create main application window
    app = QtGui.QApplication([])
    app.setStyle(QtGui.QStyleFactory.create("Cleanlooks"))
    mw = QtGui.QMainWindow()
    mw.setWindowTitle('Scrolling Plot Example')

    # Create scrolling plot
    scrolling_timestamp_plot_widget = ScrollingTimestampPlot()

    # Create and set widget layout
    # Main widget container
    cw = QtGui.QWidget()
    ml = QtGui.QGridLayout()
    cw.setLayout(ml)
    mw.setCentralWidget(cw)

    # Can use either to add plot to main layout
    #ml.addWidget(scrolling_timestamp_plot_widget.get_scrolling_timestamp_plot_widget(),0,0)
    ml.addLayout(scrolling_timestamp_plot_widget.get_scrolling_timestamp_plot_layout(),0,0)
    mw.show()

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

【讨论】:

这是一个很好的例子,谢谢,Nathancy。我运行这段代码,它可以绘制曲线,但是,X 轴没有显示时间标签。 时间标签是什么意思?它应该将minute:seconds 显示为 .gif 中的 X 轴标签,还是您正在寻找不同的时间戳格式? X 轴只是一条带刻度的线,但没有像你的 gif 显示的任何数字字符,“mm:ss”,我安装了 PyQT5,python 3.6。谢谢,内森西。 当我运行 PyQt5 版本时,它滚动正常,但时间戳轴标签不断变化。这是example。我正在使用 Python 3.6、macOS 11.3.2。 对于PyQt5版本,我认为“QtCore.QTime().currentTime()”应该是“QtCore.QTime(0, 0)”

以上是关于如何使用pyqtgraph TimeAxisItem使X轴时间动态刷新的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 PyQtGraph 获取堆栈图?

如何使用 PyQtgraph 更改绘图的刻度字体大小

如何使用 pyqtgraph 和 QSerialPort 创建实时图?

如何在 pyqtgraph 中写 \latex 字符?

Pyqtgraph - 如何添加轴日期时间?

如何使用 PyQtGraph 绘制图像上两点之间的水平距离