I implemented a live data-visualization with matplotlib in PySimpleGUI. The program works fine so far. The issue I am having is, that the plot ist printed with a weird constant value (s. figure).
`
import logging
from time import time
import PySimpleGUI as pg
import math
import serial
import serial.tools.list_ports
import csv
import matplotlib.pyplot as plt #import matülotlib for graph-plotting
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg #enable matplotlib to be embeded into PySimpleGUI
from queue import Queue #lib to define queues
#Variables
##bools
check_logging = False #check logging-status (True = active; False = incactive)
check_measure = True #check, if mode switches (Measure, Calibration)
check_csv = False #check, if csv-file is active
check_axline = False #check, if 'Start'- or 'Stop'-button was pressed --> vertical marker in plot
check_plot = False
##Strings
check_sensor_before = " " #check, if sensor-switch happened to only send index-char once
input1 = 0 #check, if input1 already has saved data
input2 = 0 #check, if input2 already has saved data
##double
t = 0.0 #check elapsed time since epoch
t_log = 0.0 #check start time of log to calculate elapsed time of logging-process
##lists
t_list = [] #time values
rpm1_list = []
rpm2_list = []
mark = [] #time values for start- and end-marker
##counter
plot_counter_start = 0 #keep track of the start index (just plot the 20 latest elements)
plot_counter_end = 0 #keep track of the end index (just plot the 20 latest elements)
#Serial-COM
ser=serial.Serial('COM3', 9600, timeout=10) #build up serial connection to Arduino
ser.flush()
def create_plot(t, rpm):
plt.clf() #clear the current figure's memory; if queues update, y- and x-axis update with the new point set (max 20 points)
#plt.xlabel('Time [s]')
#plt.ylabel('Rotational Speed [1/min]')
#plt.title('Rotational Speed Measurement')
plt.plot(t, rpm, color='blue', mfc='red', marker='o', label='points')
plt.grid(True)
return plt.gcf() #transform plot to figure for PySimpleGUI
col_layout1=[
[pg.Text('Mode: Sensor:', font=('Calibri', 12, 'bold'), size=(25, 1), justification='left')],
[pg.Combo(['Measure', 'Calibration'], font=('Calibri', 12), default_value='Measure', key='-mode-'), pg.Combo(['Auger LH/RH', 'Tamper', 'Vibration', 'Fan Speed', 'Fumes'], font=('Calibri', 12), default_value='Auger LH/RH', key='-sensor-')],
[pg.Text('', size=(1,1))],
[pg.Text('Auger LH Auger RH', size=(20, 1), font=('Calibri', 12))],
[pg.Input(size=(10, 1), font=('Calibri', 12), key='-input1-', justification='center'), pg.Input(size=(10, 1), font=('Calibri', 12), key='-input2-', justification='center')],
[pg.Text('', size=(1,1))],
[pg.Text('Data Logging', font=('Calibri', 12, 'bold'), size=(12, 1), justification='left'), pg.Radio('plot off', group_id='plot', default=True, key='-plot-'), pg.Radio('plot on', group_id='plot')],
[pg.Text('Filename (.csv):', font=('Calibri', 10), size=(20, 1), justification='left')],
[pg.Input(size=(21, 1), font=('Calibri', 12), key='-file_name-', justification='center')],
[pg.Button('Start', font=('Calibri', 12), size=(10,1)), pg.Button('Stop', font=('Calibri', 12), size=(6,1))],
[pg.Button('Save', font=('Calibri', 12), size=(21,1))],
[pg.Text('', font=('Calibri', 10), size=(24, 1), justification='left', key = '-save_status-')],
[pg.Text('', size=(1,1))],
[pg.Text('', size=(1,1))],
[pg.Text('', size=(1,1))],
[pg.Text('', size=(1,1))],
[pg.Text('', size=(1,1))],
[pg.Text('', size=(1,1))],
[pg.Text('', size=(1,1))],
[pg.Text('', size=(1,1))],
[pg.Text('', size=(1,1))]
]
layout = [
[pg.Column(col_layout1), pg.Canvas(size=(550, 450), key='-CANVAS-')]
]
#transform figure to canvas_figure (tkinter/PySimpleGUI-compatible)
def draw_figure(canvas, figure):
figure_canvas_agg = FigureCanvasTkAgg(figure, canvas)
figure_canvas_agg.draw()
figure_canvas_agg.get_tk_widget().place(width=500, height=450)
return figure_canvas_agg
window = pg.Window('Mobile GAM', layout, size=(800, 480), element_justification='left', finalize=True) #Create main-window
def plot(t, rpm):
global plot_counter_start, plot_counter_end, check_axline #refer to global variables to not encounter reference errors
t_list.append(float(t)) #time-axis
rpm1_list.append(int(rpm)) #rpm-axis (1st value)
#check for max. list length (20)
if (plot_counter_end - plot_counter_start) >= 20: #check, if 20 values are saved in value-lists
plot_counter_start = plot_counter_start + 1 #start incrementing the start-index as well to define the interval for the slice
draw_figure(window['-CANVAS-'].TKCanvas, create_plot(t_list[plot_counter_start:plot_counter_end], rpm1_list[plot_counter_start:plot_counter_end])) #plot the graph with sliced lists (20 most recent values)
else:
draw_figure(window['-CANVAS-'].TKCanvas, create_plot(t_list, rpm1_list))
plot_counter_end = plot_counter_end + 1 #increment the end index to define the interval for the slice
#window.Maximize() #Maximize window
#Deactivate 'Stop' and 'Save' at program-initialization
window.read(False)
window['Stop'].update(disabled=True)
window['Save'].update(disabled=True)
while True:
window.refresh()
event, values = window.read(False)
#---------------------------------------READ BUTTON EVENTS-----------------------------------------
if event == pg.WIN_CLOSED:
break
elif event == 'Start':
#Reset status-text
window['-save_status-'].update('')
#update bools
check_csv = True
check_logging = True #save status: logging-process is initiated
#create vertical marker lines
check_axline = True
t_log = time() #save start-time of logging-process
#Information for the CSV-File
##match-case-statements to check sensor-mode
if values['-sensor-'] == 'Auger LH/RH':
header = ['Auger LH [RPM]', 'Auger RH [RPM]', 'Time [s]']
elif values['-sensor-'] == 'Tamper':
header = ['Tamper [RPM]', 'Time [s]']
elif values['-sensor-'] == 'Vibration':
header = ['Vibration [RPM]', 'Time [s]']
elif values['-sensor-'] == 'Fan Speed':
header = ['Fan Speed [RPM]', 'Time [s]']
elif values['-sensor-'] == 'Fumes':
header = ['Fumes [RPM]', 'Time [s]']
#CSV-File
##check for file name input
if values['-file_name-'] == '':
f = open('data.csv', 'w+', encoding='UTF8') #declare file-pointer
else:
f = open(values['-file_name-']+'.csv', 'w+', encoding='UTF8')
writer = csv.writer(f) #declare write for 'writerow()'-function
writer.writerow(header) #write the header
#update buttons
window.read(False)
window['Start'].update(disabled=True)
window['Stop'].update(disabled=False)
window['Save'].update(disabled=True)
elif event == 'Stop':
check_logging = False
#update buttons
window.read(False)
window['Start'].update(disabled=False)
window['Stop'].update(disabled=True)
window['Save'].update(disabled=False)
elif event == 'Save':
f.close() #close csv-file-pointer
check_csv = False #update csv-bool
#update buttons
window.read(False)
window['Save'].update(disabled=True)
#update status-text
if values['-file_name-'] == '':
window['-save_status-'].update('\'data.csv\' saved to Desktop')
else:
window['-save_status-'].update('\'' + values['-file_name-'] + '.csv\' saved to Desktop')
if not values['-plot-']: #check, if 'plot off' is active
check_plot = True
else:
check_plot = False
#------------------------------------SEND DATA TO ARDUINO--------------------------------------------
#:::::::::::::::::::::::::::::::::::::::MODE STATUS::::::::::::::::::::::::::::::::::::::::::::::::::
if check_measure and values['-mode-'] == 'Calibration': #check for mode and measure-bool to only send index-char to Arduino at the switch
check_measure = False
ser.write('a'.encode())
elif not check_measure and values['-mode-'] == 'Measure':
check_measure = True
ser.write('b'.encode())
#:::::::::::::::::::::::::::::::::::::::::SENSORS::::::::::::::::::::::::::::::::::::::::::::::::::::
check_sensor = values['-sensor-']
if(check_sensor == 'Auger LH/RH' and check_sensor_before != check_sensor): #detect sensor-switch (combobox)
#update auger-time
t_auger = time()
#Reset control buttons and bools for CSV-Logging
check_logging = False
window.read(False)
window['Start'].update(disabled=False)
window['Stop'].update(disabled=True)
window['Save'].update(disabled=True)
#close running log-files from previous sensors
if(check_csv):
f.close()
check_sensor_before = check_sensor
ser.write('c'.encode())
elif(check_sensor == 'Tamper' and check_sensor_before != check_sensor):
t_tamper = time()
#Reset control buttons and bools for CSV-Logging
check_logging = False
window.read(False)
window['Start'].update(disabled=False)
window['Stop'].update(disabled=True)
window['Save'].update(disabled=True)
#close running log-files from previous sensors
if(check_csv):
f.close()
check_sensor_before = check_sensor
ser.write('d'.encode())
elif(check_sensor == 'Vibration' and check_sensor_before != check_sensor):
t_vibration = time()
#Reset control buttons and bools for CSV-Logging
check_logging = False
window.read(False)
window['Start'].update(disabled=False)
window['Stop'].update(disabled=True)
window['Save'].update(disabled=True)
#close running log-files from previous sensors
if(check_csv):
f.close()
check_sensor_before = check_sensor
ser.write('e'.encode())
elif(check_sensor == 'Fan Speed' and check_sensor_before != check_sensor):
t_fan = time()
#Reset control buttons and bools for CSV-Logging
check_logging = False
window.read(False)
window['Start'].update(disabled=False)
window['Stop'].update(disabled=True)
window['Save'].update(disabled=True)
#close running log-files from previous sensors
if(check_csv):
f.close()
check_sensor_before = check_sensor
ser.write('f'.encode())
elif(check_sensor == 'Fumes' and check_sensor_before != check_sensor):
t_fumes = time()
#Reset control buttons and bools for CSV-Logging
check_logging = False
window.read(False)
window['Start'].update(disabled=False)
window['Stop'].update(disabled=True)
window['Save'].update(disabled=True)
#close running log-files from previous sensors
if(check_csv):
f.close()
check_sensor_before = check_sensor
ser.write('g'.encode())
#------------------------------------RECEIVE DATA FROM ARDUINO--------------------------------------------
#::::::::::::::::::::::::::::::::::::RECEIVE AND PRINT TO GUI:::::::::::::::::::::::::::::::::::::::::::::
try:
input = ser.readline().decode('utf-8').rstrip() #read key from Arduino (only in use for Auger-Measurement)
except:
input = ' '
if(input == '-input1-'):
try:
input1 = ser.readline().decode()
window['-input1-'].update(int(input1))
except:
input = ' '
elif(input == '-input2-'):
try:
input2 = ser.readline().decode()
window['-input2-'].update(int(input2))
except:
input = ' '
#:::::::::::::::::::::::::::::::::::::::::SAVE TO CSV-FILE:::::::::::::::::::::::::::::::::::::::::::::
#Check, which sensor is selected for CSV-file and if logging is active
if(check_sensor == 'Auger LH/RH' and time()>(t+0.4) and check_logging): #update csv-values every 0.5 seconds
t_data_file = round(time()-t_log, 1) #time-coordinate
t_data = round(time(), 1) #time-coordinate
data = [int(input1), int(input2), t_data_file] #define data.list
writer.writerow(data) #write the data
#plot data
if check_plot:
plot(t_data, input1)
#Update time variable
t = time()
elif(check_sensor == 'Auger LH/RH' and time()>(t+0.4) and not check_logging): #update csv-values every 0.5 seconds (0.4, since program cycle need just under 0.4 seconds --> 1s)
t_data = round(time(), 1) #time-coordinate
#plot data
if check_plot:
plot(t_data, input1)
#Update time variable
t = time()
elif(check_sensor == 'Tamper' and time()>(t+0.4) and check_logging):
t_data_file = round(time()-t_log, 1) #time-coordinate
t_data = round(time()-t_log, 1)
data = [int(input1), t_data_file]
writer.writerow(data)
#plot data
if check_plot:
plot(t_data, input1)
t = time()
elif(check_sensor == 'Tamper' and time()>(t+0.4) and not check_logging): #update csv-values every 0.5 seconds
t_data = round(time(), 1) #time-coordinate
#plot data
if check_plot:
plot(t_data, input1)
#Update time variable
t = time()
elif(check_sensor == 'Vibration' and time()>(t+0.4) and check_logging):
t_data_file = round(time()-t_log, 1) #time-coordinate
t_data = round(time()-t_log, 1)
data = [int(input1), t_data_file]
writer.writerow(data)
#plot data
if check_plot:
plot(t_data, input1)
t = time()
elif(check_sensor == 'Vibration' and time()>(t+0.4) and not check_logging): #update csv-values every 0.5 seconds
t_data = round(time(), 1) #time-coordinate
#plot data
if check_plot:
plot(t_data, input1)
#Update time variable
t = time()
elif(check_sensor == 'Fan Speed' and time()>(t+0.4) and check_logging):
t_data_file = round(time()-t_log, 1) #time-coordinate
t_data = round(time()-t_log, 1)
data = [int(input1), t_data_file]
writer.writerow(data)
#plot data
if check_plot:
plot(t_data, input1)
t = time()
elif(check_sensor == 'Fan Speed' and time()>(t+0.4) and not check_logging): #update csv-values every 0.5 seconds
t_data = round(time(), 1) #time-coordinate
#plot data
if check_plot:
plot(t_data, input1)
#Update time variable
t = time()
elif(check_sensor == 'Fumes' and time()>(t+0.4) and check_logging):
t_data_file = round(time()-t_log, 1) #time-coordinate
t_data = round(time()-t_log, 1)
data = [int(input1), t_data_file]
writer.writerow(data)
#plot data
if check_plot:
plot(t_data, input1)
t = time()
elif(check_sensor == 'Fumes' and time()>(t+0.4) and not check_logging): #update csv-values every 0.5 seconds
t_data = round(time(), 1) #time-coordinate
#plot data
if check_plot:
plot(t_data, input1)
#Update time variable
t = time()
window.close()
I tried to remove x- and y-label as well as the plot title, to make sure, that there is no problem with them. The weird caption value is still printed.
Any idea what could cause this?
Thanks for your support :)