【问题标题】:Python - Real Time PlottingPython - 实时绘图
【发布时间】:2016-02-26 10:05:48
【问题描述】:

我编写了一个简单的 python 脚本来显示我的传感器数据(从串行读取)。 我想了解为什么当我单击任何地方时图形会崩溃... 如果我尝试在计算机上执行其他操作,是否有任何简单的方法来避免脚本崩溃?

此外,如果我想开发一个简单的 GUI,有没有人可以学习如何做的例子?它会避免这个问题吗?

import serial
import re
import numpy as np
import time
from matplotlib import pyplot as plt

SERIAL_PORT = 'COM3'
BAUDRATE = 9600
BUFFER_SIZE = 30



print("initialisation ...")
ser = serial.Serial(SERIAL_PORT, BAUDRATE)
buffer = []

plt.ion()
fig = plt.figure()
ydata = [0]*BUFFER_SIZE*2
prevBuf = [0]*BUFFER_SIZE*10
ax1=plt.axes()

line, = plt.plot(prevBuf)
plt.ylim([2000,10000])


def clean_serial():
    i=0
    while i<=5:
        data = ser.readline()
        print data
        i+=1
    ser.flush()


def extract_number(rawdata):
    return float(re.sub("[^0-9.]", " ", rawdata))

def process_buffer(buf):

    prevBuf[len(prevBuf)-BUFFER_SIZE:] = buf

    line.set_xdata(np.arange(len(prevBuf)))
    line.set_ydata(prevBuf)

    ydataMax = max(prevBuf)+10
    ydataMin = min(prevBuf)-100
    plt.ylim([ydataMin,ydataMax])
    plt.draw()


    prevBuf[:] = prevBuf[BUFFER_SIZE:]+buf

print("cleaning serial")
clean_serial()

print("reading value from serial")
while True:
    data = extract_number(ser.readline())
    buffer.append(data)
    if len(buffer)>= BUFFER_SIZE:
        process_buffer(buffer)
        buffer=[]


ser.close()

哪个输出:script crashing

谢谢!

【问题讨论】:

  • 你能发布你的错误信息吗?
  • 我不能 100% 确定它会弹出错误消息,它只是冻结。我必须按 ctrl+c(键盘中断)来停止它,然后重新启动
  • 嗨@CrH,你关于实时绘图的问题解决了吗?

标签: python real-time


【解决方案1】:

我写了一个对你有用的 Python 应用:

###################################################################
#                                                                 #
#                     PLOTTING A LIVE GRAPH                       #
#                  ----------------------------                   #
#            EMBED A MATPLOTLIB ANIMATION INSIDE YOUR             #
#            OWN GUI!                                             #
#                                                                 #
###################################################################


import sys
import os
from PyQt4 import QtGui
from PyQt4 import QtCore
import functools
import numpy as np
import random as rd
import matplotlib
matplotlib.use("Qt4Agg")
from matplotlib.figure import Figure
from matplotlib.animation import TimedAnimation
from matplotlib.lines import Line2D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
import time
import threading



def setCustomSize(x, width, height):
    sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
    sizePolicy.setHorizontalStretch(0)
    sizePolicy.setVerticalStretch(0)
    sizePolicy.setHeightForWidth(x.sizePolicy().hasHeightForWidth())
    x.setSizePolicy(sizePolicy)
    x.setMinimumSize(QtCore.QSize(width, height))
    x.setMaximumSize(QtCore.QSize(width, height))

''''''

class CustomMainWindow(QtGui.QMainWindow):

    def __init__(self):

        super(CustomMainWindow, self).__init__()

        # Define the geometry of the main window
        self.setGeometry(300, 300, 800, 400)
        self.setWindowTitle("my first window")

        # Create FRAME_A
        self.FRAME_A = QtGui.QFrame(self)
        self.FRAME_A.setStyleSheet("QWidget { background-color: %s }" % QtGui.QColor(210,210,235,255).name())
        self.LAYOUT_A = QtGui.QGridLayout()
        self.FRAME_A.setLayout(self.LAYOUT_A)
        self.setCentralWidget(self.FRAME_A)

        # Place the zoom button
        self.zoomBtn = QtGui.QPushButton(text = 'zoom')
        setCustomSize(self.zoomBtn, 100, 50)
        self.zoomBtn.clicked.connect(self.zoomBtnAction)
        self.LAYOUT_A.addWidget(self.zoomBtn, *(0,0))

        # Place the matplotlib figure
        self.myFig = CustomFigCanvas()
        self.LAYOUT_A.addWidget(self.myFig, *(0,1))

        # Add the callbackfunc to ..
        myDataLoop = threading.Thread(name = 'myDataLoop', target = dataSendLoop, args = (self.addData_callbackFunc,))
        myDataLoop.start()

        self.show()

    ''''''


    def zoomBtnAction(self):
        print("zoom in")
        self.myFig.zoomIn(5)

    ''''''

    def addData_callbackFunc(self, value):
        # print("Add data: " + str(value))
        self.myFig.addData(value)



''' End Class '''


class CustomFigCanvas(FigureCanvas, TimedAnimation):

    def __init__(self):

        self.addedData = []
        print(matplotlib.__version__)

        # The data
        self.xlim = 200
        self.n = np.linspace(0, self.xlim - 1, self.xlim)
        a = []
        b = []
        a.append(2.0)
        a.append(4.0)
        a.append(2.0)
        b.append(4.0)
        b.append(3.0)
        b.append(4.0)
        self.y = (self.n * 0.0) + 50

        # The window
        self.fig = Figure(figsize=(5,5), dpi=100)
        self.ax1 = self.fig.add_subplot(111)


        # self.ax1 settings
        self.ax1.set_xlabel('time')
        self.ax1.set_ylabel('raw data')
        self.line1 = Line2D([], [], color='blue')
        self.line1_tail = Line2D([], [], color='red', linewidth=2)
        self.line1_head = Line2D([], [], color='red', marker='o', markeredgecolor='r')
        self.ax1.add_line(self.line1)
        self.ax1.add_line(self.line1_tail)
        self.ax1.add_line(self.line1_head)
        self.ax1.set_xlim(0, self.xlim - 1)
        self.ax1.set_ylim(0, 100)


        FigureCanvas.__init__(self, self.fig)
        TimedAnimation.__init__(self, self.fig, interval = 50, blit = True)

    def new_frame_seq(self):
        return iter(range(self.n.size))

    def _init_draw(self):
        lines = [self.line1, self.line1_tail, self.line1_head]
        for l in lines:
            l.set_data([], [])

    def addData(self, value):
        self.addedData.append(value)

    def zoomIn(self, value):
        bottom = self.ax1.get_ylim()[0]
        top = self.ax1.get_ylim()[1]
        bottom += value
        top -= value
        self.ax1.set_ylim(bottom,top)
        self.draw()


    def _step(self, *args):
        # Extends the _step() method for the TimedAnimation class.
        try:
            TimedAnimation._step(self, *args)
        except Exception as e:
            self.abc += 1
            print(str(self.abc))
            TimedAnimation._stop(self)
            pass

    def _draw_frame(self, framedata):
        margin = 2
        while(len(self.addedData) > 0):
            self.y = np.roll(self.y, -1)
            self.y[-1] = self.addedData[0]
            del(self.addedData[0])


        self.line1.set_data(self.n[ 0 : self.n.size - margin ], self.y[ 0 : self.n.size - margin ])
        self.line1_tail.set_data(np.append(self.n[-10:-1 - margin], self.n[-1 - margin]), np.append(self.y[-10:-1 - margin], self.y[-1 - margin]))
        self.line1_head.set_data(self.n[-1 - margin], self.y[-1 - margin])
        self._drawn_artists = [self.line1, self.line1_tail, self.line1_head]



''' End Class '''


# You need to setup a signal slot mechanism, to 
# send data to your GUI in a thread-safe way.
# Believe me, if you don't do this right, things
# go very very wrong..
class Communicate(QtCore.QObject):
    data_signal = QtCore.pyqtSignal(float)

''' End Class '''



def dataSendLoop(addData_callbackFunc):
    # Setup the signal-slot mechanism.
    mySrc = Communicate()
    mySrc.data_signal.connect(addData_callbackFunc)

    # Simulate some data
    n = np.linspace(0, 499, 500)
    y = 50 + 25*(np.sin(n / 8.3)) + 10*(np.sin(n / 7.5)) - 5*(np.sin(n / 1.5))
    i = 0

    while(True):
        if(i > 499):
            i = 0
        time.sleep(0.1)
        mySrc.data_signal.emit(y[i]) # <- Here you emit a signal!
        i += 1
    ###
###




if __name__== '__main__':
    app = QtGui.QApplication(sys.argv)
    QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Plastique'))
    myGUI = CustomMainWindow()


    sys.exit(app.exec_())

''''''

试试看吧。将此代码复制粘贴到一个新的 python 文件中,然后运行它。你应该得到一个漂亮的、平滑移动的图形:

实际数据在 dataSendLoop(..) 函数中发送。此函数在单独的线程中运行。请注意,数据是在该函数内的此代码行中发送的:

    mySrc.data_signal.emit(...)

只需将要添加的值放在括号内即可。该值将在下次刷新屏幕时添加到移动图表中。

【讨论】:

    猜你喜欢
    • 2014-09-07
    • 2015-05-26
    • 2014-10-12
    • 2013-03-22
    • 2019-06-30
    • 2012-03-13
    • 2017-12-16
    • 2012-09-22
    • 2015-11-04
    相关资源
    最近更新 更多