Python Forum
[Tkinter] Matplotlib clear x,y data
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tkinter] Matplotlib clear x,y data
#1
Hi,

How do I clear a Matplotlib line chart's data? I have the following lines when a user clicks a button:

def on_btn_reset(self):
    self.ax.clear()
    self.plotcanvas.draw()

def on_btn_start(self):
    self.anim = animation.FuncAnimation(self.fig, self.animate, interval=1000)
    self.plotcanvas.draw()
The x- and y-axes are cleared when Reset button is clicked. But when I click the Start button after clicking the Resert button, the charts draws with the previous data included and the graph is a mess. I'm including the codes here to reproduce the result. But I'm using a CO2 sensor device hooked up to my raspberry pi 4, so I don't know if this is going to help. The line chart is a real-time line chart that graphs actual CO2 data from the sensor.

import serial, sys, glob, select
import time
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import tkinter as tk
import pandas as pd

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from tkinter import *
from tkinter import messagebox
from tkinter import ttk
from tkinter import font
from itertools import count
from pandas import DataFrame
from datetime import datetime
from datetime import timedelta

offBulb = "/home/pi/projects/SoilCO2Logger/images/off_bulb.png"
redBulb = "/home/pi/projects/SoilCO2Logger/images/red_bulb.png"
greenBulb = "/home/pi/projects/SoilCO2Logger/images/green_bulb.png"
yellowBulb = "/home/pi/projects/SoilCO2Logger/images/yellow_bulb.png"
peakList = []    #use for Excel file
co2List = []     #use for Excel file
MINCO2 = 50
co2 = 0
sampleNo = 0
resp = []
pk_counter = 0     #peak_time counter
bt_counter = 0     #break_time counter
xs = []
ys = []



root = tk.Tk()    
root.wm_title("Soil CO2 Logger" )
root.geometry("800x480")

def quit(*args):
    root.destroy()  

#Create a display window
class Main:    
    def __init__(self, root):
        self.root = root
        self.maxt = 30
        self.xs = [0]
        self.ys = [0]
        self.dt = 1
        self.after_id = None
        self.tt_counter = 0
       
       
        #Build graph
        self.fig = plt.Figure(figsize=(5,3), dpi=100)        
        self.ax = self.fig.add_subplot(111)
        self.ax.set_xlim(1, self.maxt)
        self.ax.set_title('K-30 CO2 Sensor Readings', fontweight='bold')
        self.ax.set_xlabel('Time in Seconds')
        self.ax.set_ylabel('CO2 ppm')        
       
        #Create a tk.DrawingArea to overlay figure
        self.plotcanvas = FigureCanvasTkAgg(self.fig, root)
        self.plotcanvas.get_tk_widget().pack(side=LEFT, fill=Y)
       
        self.frame1 = tk.Frame(root)
        self.lblReadings = tk.Label(self.frame1, text="CO2 Readings")
        self.lblReadings.grid(row=0, column=0, sticky=W)
       
        #Add a listbox to frame1
        self.lboxResult = Listbox(self.frame1, width=32, height=9)
        self.lboxResult.grid(row=1, column=0, sticky=W)          
       
        #Add a scrollbar to listbox
        self.ybar = tk.Scrollbar(self.frame1, orient=VERTICAL, command=self.lboxResult.yview)        
        self.lboxResult.config(yscrollcommand = self.ybar.set)  
        self.ybar.grid(row=1, column=1, sticky=W)      
        self.frame1.pack()
               
        self.frame2 = tk.Frame(root)
        self.spacer = tk.Label(self.frame2, text="")
        self.spacer.grid(row=0, sticky=W)
       
        self.lblCoffee = tk.Label(self.frame2, text="Coffee Break (secs):")
        self.lblCoffee.grid(row=1, sticky=W)
        self.valBreakTime = tk.Label(self.frame2, bg="white", width=12, borderwidth=1, relief="solid")
        self.valBreakTime.grid(row=1, column=1, sticky=W)        
       
        #Start the testing time
        self.lblTestTime = tk.Label(self.frame2, text="Test Time (hh:mm:ss)")
        self.lblTestTime.grid(row=2, sticky=tk.W)
        self.valTestTime = tk.Label(self.frame2, text="", bg="white", width= 12, borderwidth=1, relief="solid")
        self.valTestTime.grid(row=2, column=1, sticky=tk.W)
               
        self.lblPeakTime = tk.Label(self.frame2, text="Countdown to Peak Time")
        self.lblPeakTime.grid(row=3, sticky=tk.W)
        self.valPeakTime = tk.Label(self.frame2, bg="white", width= 12, borderwidth=1, relief="solid")
        self.valPeakTime.grid(row=3, column=1, sticky=tk.W)
       
        self.lblCo2 = tk.Label(self.frame2, text="CO2 (ppm)")
        self.lblCo2.grid(row=4, sticky=tk.W)
        self.valCo2 = tk.Label(self.frame2, bg="white", width=12, borderwidth=1, relief="solid")
        self.valCo2.grid(row=4, column=1, sticky=tk.W)
       
        self.lblPeakCaptured = tk.Label(self.frame2, text="Peak Captured @ 30-second")
        self.lblPeakCaptured.grid(row=5, sticky=tk.W)
        self.valPeakCaptured = tk.Label(self.frame2, bg="white", width= 12, borderwidth=1, relief="solid")
        self.valPeakCaptured.grid(row=5, column=1, sticky=tk.W)
        self.frame2.pack()

        self.frame3 = tk.Frame(root)
        self.lblInfo = tk.Label(self.frame3, text="Sample #", foreground='blue')
        self.lblInfo.grid(row=0, column=0, columnspan=2, sticky=tk.W)
       
        self.img = PhotoImage(file=offBulb)
        self.canvas2 = tk.Canvas(self.frame3, width=50, height=70)
        self.canvas2.create_image(14,14, anchor=NW, image=self.img)
        self.canvas2.grid(row=1, column=0, sticky=W)        
        self.btnStart = tk.Button(self.frame3, text="Start", command=self.on_btn_start)
        self.btnStart.grid(row=1, column=1, sticky=W)
        self.btnStop = tk.Button(self.frame3, text="Stop", command=self.on_btn_stop)
        self.btnStop.grid(row=1, column=2, sticky=W)
        self.btnReset = tk.Button(self.frame3, text="Reset", command=self.on_btn_reset)
        self.btnReset.grid(row=1, column=3, sticky=W)
        self.btnExit = tk.Button(self.frame3, text="Exit", command=self.on_btn_exit)
        self.btnExit.grid(row=1, column=4, sticky=W)
        self.frame3.pack(side="top")
       
   
    def on_btn_start(self):
        self.tt_counter = 0
        self.pk_counter = 0
       
        if not self.after_id:
            self.increase_counter()
       
        #fargs = (xs, ys),
        self.anim = animation.FuncAnimation(self.fig, self.animate, interval=1000)
        self.plotcanvas.draw()
               
   
    def on_btn_stop(self):
        if self.after_id:
            self.frame3.after_cancel(self.after_id)
            self.after_id = None
       
        self.anim.event_source.stop()
        #self.stop_test_timer()
       
        #Get co2List
        co2Df = pd.DataFrame(co2List)
       
       
    def on_btn_reset(self):
        self.on_btn_stop()
        self.tt_counter = 0
        self.display_counter()
       
        self.lboxResult.delete(0, END)
        self.valPeakTime.config(text= '')
        self.valPeakCaptured.config(text='')
        self.valCo2.config(text= '')
        self.lblInfo.config(text = 'Sample #')
        self.img.config(file=offBulb)
        self.ax.clear()
        self.plotcanvas.draw()
       
   
    def increase_counter(self):
        self.tt_counter += 1
        self.display_counter()
        self.after_id = self.frame3.after(1000, self.increase_counter)
       
       
    def display_counter(self):
        startTime = datetime.fromtimestamp(self.tt_counter)
        startTime = startTime.replace(hour=0)              
        ttStr = startTime.strftime("%H:%M:%S")
        display = ttStr                  
             
        self.valTestTime.config(text=display)  
       
       

    def on_btn_exit(self):  
        quit()
       
       
    def getSampleNo(self):
        global sampleNo
       
        co2 = self.valCo2.cget('text')      
           
        #Note: length of list is NOT zero-based
        if co2 > MINCO2 and bt_counter == 0 and len(peakList) == 0:
            sampleNo = 1
           
        if co2 < MINCO2 and bt_counter == 0 and len(peakList) == 0:
            sampleNo = 1
           
        if co2 < MINCO2 and bt_counter == 0 and len(peakList) > 0:
            sampleNo += 1
           
        if co2 < MINCO2 and bt_counter > 0 and len(peakList) > 0:
            if bt_counter == 1:  #so it won't increment at every tick
                sampleNo += 1        
   
        return sampleNo
       
    def start_test_time(self):  
        def count():
            #global tt_counter
            self.tt_counter = 0
           
            #Manage initial delay
            if self.tt_counter == 0:
                display = "Starting "
            else:
                startTime = datetime.fromtimestamp(self.tt_counter)
                startTime = startTime.replace(hour=0)              
                ttStr = startTime.strftime("%H:%M:%S")
                display = ttStr                  
             
            self.valTestTime.config(text=display)            
                                   
            #Trigger after 1 second delay
            self.after_id = self.valTestTime.after(1000, count)
           
            self.tt_counter += 1            
           
        #Trigger the start of counter
        count()
       

    def stop_test_time(self):
        self.start_test_time.stop()
               
       
    def animate(self, i):
        global co2
       
        #Initialize x,y data
        self.tt_counter = i
               
        #Initialize serial port
        ser = self.serialCo2_init()
        counter = 0
   
        #Parse sensor data
        ser.flushInput()
        ser.write(b"\xFE\x44\x00\x08\x02\x9f\x25")
        time.sleep(.1)        
        resp = ser.read(7)
        co2 = resp[4]
        self.valCo2.config(text=co2)      
       
        #Log if CO2 > 600
        self.record_co2(co2)        
       
               
        #Add x and y to lists
        xs.append(self.tt_counter)
        ys.append(co2)
       
        #Draw x and y lists
        self.ax.plot(xs, ys, label='CO2 (ppm)', ms=4, marker='s', color='blue')      
        self.update()
       
   
    def update(self):
        self.dt = 1
        lastt = self.xs[-1]
        if lastt > self.xs[0] + self.maxt:   #reset the arrays
            self.xs = [self.xs[-1]]
            self.ys = [self.ys[-1]]
            self.ax.set_xlim(self.xs[0], self.xs[0] + self.maxt)
            self.ax.figure.canvas.draw()
       
        t = self.xs[-1] + self.dt
        self.xs.append(t)
        self.ys.append(ys)
     
       
       
    def record_co2(self, co2):
        global bt_counter
        bt_counter = 0
        peak = self.valPeakCaptured.cget('text')
        self.valCo2.config(text=co2)
       
        self.countdown_peaktime()

           
    def countdown_peaktime(self):  
        def count():
            global pk_counter
                       
            peak = self.valPeakCaptured.cget('text')
            info = self.lblInfo.cget('text')
            sampleNo = self.getSampleNo()
            co2 = self.valCo2.cget('text')
            self.valCo2.config(text=co2)
       
           
            #Manage initial delay
            if pk_counter == 0:
                pkDisplay = "Countdown..."            
            else:                                
                if co2 > MINCO2:
                    startTime = datetime.fromtimestamp(pk_counter)
                    startTime = startTime.replace(hour=0)
                   
                    #Get min:sec part of counter
                    pkMins = startTime.strftime("%M:%S")
                    self.valPeakTime.config(text = pkMins)
                    self.valBreakTime.config(text = '')
                   
                    sampleNo = self.getSampleNo()                    
                    self.lblInfo.config(text = 'Testing Sample #' + str(sampleNo))  
                    self.img.config(file=redBulb)
                   
                    #Add item to Listbox
                    now = datetime.now()
                    d1 = now.strftime("%d-%m-%y %H:%M:%S")
                               
                    lboxStr = d1 + ' S#' + str(sampleNo) + ' ' + str(co2)                      
                    self.lboxResult.insert('end', lboxStr)
               
                    #Add item to peakList
                    carbon = round((co2 / 40) * 0.273, 1)
                    pklistStr = d1 + ' S#' + str(sampleNo) + ' ' + str(co2) + ' ' + str(carbon)
                   
                    co2List.append(lboxStr)
                    peakList.append(pklistStr)  
                   
                    #Record peaks captured
                    if pk_counter == 30:                    
                        lboxStr = d1 + ' S#' + str(sampleNo) + ' ' + str(co2)
                        self.lboxResult.insert('end', lboxStr + ' **PEAK**')
                       
                        self.valPeakCaptured.config(text=co2)
                        self.lblInfo.config(text = 'Sample Captured')
                        self.img.config(file=yellowBulb)
                   
                    #Record subsequent peaks
                    if pk_counter >= 30 and peak != '':
                        #Should stay on yellow until co2 drops below MINCO2
                        self.lblInfo.config(text = 'Sample Captured')
                        self.img.config(file=yellowBulb)                
                else:
                    self.coffee_break()                  
                 
            pk_counter += 1
             
        #Trigger the start of counter
        count()
       
       
    def coffee_break(self):
        def bt_count():
            global bt_counter
            peak = self.valPeakCaptured.cget('text')
            co2 = self.valCo2.cget('text')
           
            #Manage initial delay
            if bt_counter == 0:
                pkDisplay = "Break..."                
            else:
                if co2 < MINCO2 and peak > 0:
                    '''
                    - Replace sample and start coffee break.
                    - Stays green once co2 drops below MINCO2.
                    - Note: change sample at this point but wait
                      until co2 drops below MINCO2 and bulbs turn
                      green before replacing sample
                    '''
                       
                    bt_counter = 0
                   
                    if str(peak) != '' or str(peak) == '':                        
                        self.lblInfo.config(text = 'Ready for Sample')
                        self.img.config(file=greenBulb)
                             
                        #Start coffee break counter
                        self.valBreakTime.config(text = str(bt_counter))
                   
                        #Hide peak time counter
                        self.valPeakTime.config(text = '')  
           
            bt_counter += 1
       
        #Trigger the start of counter
        bt_count()
       
    def serialCo2_init(self):
        #Initialize serial port
        dev = "/dev/ttyUSB0"
        scan = glob.glob(dev)
        rate = "9600"
           
        if(len(scan) == 0):
            dev = '/dev/ttyUSB*'
            scan = glob.glob(dev)
           
            if(len(scan) == 0):
                messagebox.showerror("Error","Unable to find any ports for /dev/[ttyACM*|ttyUSB*]") + dev
            sys.exit()
           
        serport = scan[0]        
           
        if(len(sys.argv) > 1):
            l = len(sys.argv) - 1
            while( l > 0):
                if(sys.argv[l][0] == '/'):
                   serport = sys.argv[l]
                else:
                    l = l - 1
               
        ser = serial.Serial(
            port = serport,  # or '/dev/ttyUSB0'
            baudrate = rate,
            parity = serial.PARITY_NONE,
            stopbits = serial.STOPBITS_ONE,
            bytesize = serial.EIGHTBITS,
            timeout = 1)
       
        return ser


main = Main(root)
root.mainloop()
Appreciate any help.
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020