简体   繁体   English

Python-实时绘图

[英]Python - Real Time Plotting

I wrote a simple python script that display my sensor data (read from Serial). 我写了一个简单的python脚本来显示我的传感器数据(从Serial读取)。 I wish to understand why the figure crashes when I'm clicking anywhere... Is there any - simple way, if possible - to avoid the script to crash if I try to do something else on the computer? 我想了解为什么在单击任何地方时图形都会崩溃...是否有-如果可能的话-如果我尝试在计算机上进行其他操作,可以避免脚本崩溃?

Moreover, if I want to develop a simple GUI, has someone an example to learn how to do it? 此外,如果我想开发一个简单的GUI,是否有人举一个例子来学习如何做? Does it will avoid this problem ? 会避免这个问题吗?

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()

which output : script crashing 哪个输出: 脚本崩溃

Thank you ! 谢谢 !

I wrote a Python app that could be useful for you: 我编写了一个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_())

''''''

Just try it out. 尝试一下。 Copy-paste this code in a new python-file, and run it. 将此代码复制粘贴到新的python文件中,然后运行它。 You should get a beautiful, smoothly moving graph: 您应该得到一个漂亮的,平滑移动的图形:

在此处输入图片说明

The actual data is sent in the dataSendLoop(..) function. 实际数据在dataSendLoop(..)函数中发送。 This function runs in a separate thread. 此函数在单独的线程中运行。 Notice that the data gets sent in this codeline within that function: 请注意,数据是通过该函数中的此代码行发送的:

    mySrc.data_signal.emit(...)

Just put the value you want to add between the brackets. 只需将要添加的值放在方括号之间。 That value will get added to the moving graph next time it gets a screen refresh. 下次刷新屏幕时,该值将添加到移动图形中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM