Using animated graph with Tkinter GUI python - python

I am developing a python code where my computer reads data being transmitted using serial communication and my program presents this data in graphical format. New data is received every 0.1 seconds after which the graph is updated.
The code is:
import matplotlib
matplotlib.use("TkAgg")
from matplotlib import *
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
from random import *
from Serial import *
import Tkinter as tk
import ttk
import xlwt
import serial
COMPORT = "COM7"
BUAD = 115200
def init():
connected = False
global ser
global COMPORT, BUAD
ser = serial.Serial(COMPORT, BUAD)
while not connected:
serin = ser.read()
connected = True
def TransmitCommand(Command):
SCommand = Command + 'y'
ser.write(SCommand)
def TransmitData(Data):
SData = str(Data) + 'y'
ser.write(SData)
def ClosePort():
ser.close()
def RecieveData():
return ser.readline()
################################################################################
############################ Graph1 #######################################
################################################################################
F1 = Figure(figsize=(15,8), dpi=80)
rc("font", size = 10)
Figure
a1 = F1.add_subplot(111)
tList = []
xList = []
yList = []
zList = []
fList = []
xfList = []
yfList = []
zfList = []
fxList = []
fyList = []
fzList = []
t = 0.00
t1 = 0
x = 0
y = 0
z = 0
ex = 0
ey = 0
ez = 0
r1 = 0
r2 = 0
r3 = 0
l = 0
txp = 0
typ = 0
tzp = 0
global fx1, fy1, fz1
fx1 = 0.00000
fy1 = 0.00000
fz1 = 0.00000
PPS1 = 1
def Play1():
global PPS1
TransmitCommand('SendData')
#print 'Done'
PPS1 = 0
def Pause1():
global PPS1
TransmitCommand('StopData')
PPS1 = 1
def Stop1():
global PPS1
TransmitCommand('StopData')
PPS1 = 2
def Extract1():
pass
def Save1():
pass
def SaveGraph1():
pass
def animate1(i):
global l, ex, ey, ez, t, x, y, z, tList, xList, yList, zList, r1, r2, r3
global fx1, fy1, fz1, txp, typ, tzp, xt, yt, zt
if(PPS1 == 0):
tList.append(t)
xList.append(x)
yList.append(y)
zList.append(z)
t = int(RecieveData())
x = int(RecieveData())
if(l == 0):
x = (x*0.707)/300
else:
x = ex - (x*0.707)/300
if(x > 0):
if(xList[l-1] == 0)|(xList[l-1] < 0):
fx1 = (1.0/(t - txp))*1000
txp = t
r1 = 1
y = int(RecieveData())
if(l == 0):
y = (y*0.707)/300
else:
y = ey - (y*0.707)/300
if(y > 0):
if(yList[l-1] == 0)|(yList[l-1] < 0):
fy1 = (1.0/(t - typ))*1000
typ = t
r2 = 1
z = int(RecieveData())
if(l == 0):
z = (z*0.707)/300
else:
z = ez - (z*0.707)/300
if(z > 0):
if(zList[l-1] == 0)|(zList[l-1] < 0):
fz1 = (1.0/(t - tzp))*1000
tzp = t
r3 = 1
if(l == 0):
ex = x
ey = y
ez = z
l = l+1
if(PPS1 == 2):
tList = []
xList = []
yList = []
zList = []
t = 0
x = 0
y = 0
z = 0
a1.clear()
a1.plot(tList, xList, label = "ax")
a1.plot(tList, yList, 'r', label = "ay")
a1.plot(tList, zList, 'g', label = "az")
a1.set_ylim(-1,1)
a1.set_xlabel("Time (ms)")
a1.set_ylabel("Acceleration (g)")
a1.legend()
def GenerateGraph1(Master):
dataPlot = FigureCanvasTkAgg(F1, master=Master)
dataPlot.show()
dataPlot.get_tk_widget()
display1 = tk.Canvas(Master, width=100, height=400, bg ="white")
display1.pack(side = 'right')
button1 = tk.Button(display1, text='Play', command=Play1, font='Times 12', bd=5,
height = 2, width = 10, anchor = 'w').grid(row = 0, column = 0)
button2 = tk.Button(display1, text='Pause', command=Pause1, font='Times 12', bd=5,
height = 2, width = 10, anchor = 'w').grid(row = 1, column = 0)
button3 = tk.Button(display1, text='Stop', command=Stop1, font='Times 12', bd=5,
height = 2, width = 10, anchor = 'w').grid(row = 2, column = 0)
button4 = tk.Button(display1, text='Extract Data', command=Extract1, font='Times 12', bd=5,
height = 2, width = 10, anchor = 'w').grid(row = 3, column = 0)
button5 = tk.Button(display1, text='Save Data', command=Save1, font='Times 12', bd=5,
height = 2, width = 10, anchor = 'w').grid(row = 4, column = 0)
button5 = tk.Button(display1, text='Save Graph', command=SaveGraph1, font='Times 12', bd=5,
height = 2, width = 10, anchor = 'w').grid(row = 5, column = 0)
button5 = tk.Button(display1, text='Send Mail', command=Save1, font='Times 12', bd=5,
height = 2, width = 10, anchor = 'w').grid(row = 6, column = 0)
toolbar = NavigationToolbar2TkAgg(dataPlot, Master)
toolbar.update()
dataPlot._tkcanvas.pack()
def show_frame():
frame = GenerateGraph(Graph)
frame.tkraise()
Main = tk.Tk()
init()
n = ttk.Notebook(Main, width= 800, height = 400)
n.grid(row=6,column=0, columnspan=9)
n.columnconfigure(0, weight=1)
n.rowconfigure(6, weight=1)
f1 = ttk.Frame(n);
n.add(f1, text='Acceleration vs Time')
GenerateGraph1(f1)
ani1 = animation.FuncAnimation(F1, animate1, interval=100)
mainloop()
Arduino code is:
int toggle1 = 0;
boolean Graph = 0;
int x = 0;
int y = 0;
int z = 0;
int i = 0;
const int groundpin = 18; // analog input pin 4 -- ground
const int powerpin = 19; // analog input pin 5 -- voltage
const int xpin = A3; // x-axis of the accelerometer
const int ypin = A2; // y-axis
const int zpin = A1; // z-axis (only on 3-axis models)
#include <elapsedMillis.h>
elapsedMillis timeElapsed;
void Acceleration(){
Serial.print(timeElapsed);
Serial.print("\n");
Serial.print(analogRead(xpin));
Serial.print("\n");
Serial.print(analogRead(ypin));
Serial.print("\n");
Serial.print(analogRead(zpin));
Serial.print("\n");
}
void setup()
{
Serial.begin(115200);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
pinMode(xpin, INPUT);
pinMode(ypin, INPUT);
pinMode(zpin, INPUT);
Serial.write('1');
noInterrupts(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
OCR1A = 25000; // compare match register 16MHz/64/10Hz
TCCR1B |= (1 << WGM12); // CTC mode
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
interrupts(); // enable all interrupts
TCCR1B |= (1 << CS10);
TCCR1B |= (1 << CS11);
pinMode(groundpin, OUTPUT);
pinMode(powerpin, OUTPUT);
digitalWrite(groundpin, LOW);
digitalWrite(powerpin, HIGH);
}
ISR(TIMER1_COMPA_vect){
if(Graph == 1){
Acceleration();
}}
void loop() {
String brightness, recieve, r1;
int b1, b2, ledPin;
while(Serial.available() == 0) {}
recieve = Serial.readStringUntil('y');
r1 = recieve + "t";
if(recieve == "SendData"){Graph = 1;}
if(recieve == "StopData"){Graph = 0;timeElapsed = 0;}
if(recieve == "motor1"){
ledPin = 8;
while(Serial.available() == 0){}
brightness = Serial.readStringUntil('y');
b1 = brightness.toInt();
analogWrite(ledPin, b1);}
if(recieve == "motor2"){
ledPin = 9;
while(Serial.available() == 0){}
brightness = Serial.readStringUntil('y');
b1 = brightness.toInt();
analogWrite(ledPin, b1);}
}
}
I am taking data from a vibration sensor. I used stopwatch to time the difference between the time that the vibrations actually started and the time at which the graph began to show the change in data. For the first data set the time difference was 5 seconds, for data set 2 the difference was 1 minute 10 seconds and for the third set it was 2 minutes. The effect became more pronounced as I added a second graph and the rest of my GUI. I need this software to be able to run for hours which means at one point my GUI might become unresponsive and I also need the data to be real-time. What could be the problem causing this and how do I solve it. There may be a better way to show real-time data in GUI as a graph but since I am new to python I dont know.

this simple animation example will plot a new red dot every second, the method should be more stable than what you're doing; I've had an app running over 4 months that plots once a minute like this:
import matplotlib.pyplot as plt
import random
import time
plt.ion()
x = 0
while x<100:
y = random.random()
plt.plot(x,y,marker='.',c='red')
x+=1
time.sleep(1)
plt.tight_layout()
plt.show()
plt.pause(0.1)
You said you need it to be "real time", but it may be that the execution time of your code is taking longer than the 0.1 seconds that you get new data. It might be best to update the graph every 10 data points; or as I'm doing... once a minute.

Related

How can I fix Attribute error in python for real time plotting in GUI?

My aim to is to plot real time data coming from Arduino to a GUI in python. I have developed everything required for it. I want to see some of the values, so defined labels accordingly to display varying input data beside the plotting. I did around 3, but the aim is to do 9 values. But when I try to include the 4th one, am getting an Attribute error.
import copy
from threading import Thread
from tkinter import Frame
import tkinter as Tk
import tkinter.font
import serial
import time
import collections
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import struct
from PIL import ImageTk
from PIL import Image
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from tkinter import Text
import math
import pandas as pd
A=0
B=0
C=0
D=0
Sum = 0
Az = 0
El = 0
angle = 0
ang = 0
Ang = 0
Ta = 0
Tb = 0
Va = 0
Vb = 0
cond = False
class serialPlot:
def __init__(self, serialPort='com7', serialBaud=38400, plotLength=100, dataNumBytes=4, numPlots=9):
self.port = serialPort
self.baud = serialBaud
self.plotMaxLength = plotLength
self.dataNumBytes = dataNumBytes
self.numPlots = numPlots
self.rawData = bytearray(numPlots * dataNumBytes)
self.dataType = None
if dataNumBytes == 2:
self.dataType = 'h' # 2 byte integer
elif dataNumBytes == 4:
self.dataType = 'f' # 4 byte float
self.data = []
self.privateData = None # for storing a copy of the data so all plots are synchronized
for i in range(numPlots): # give an array for each type of data and store them in a list
self.data.append(collections.deque([0] * plotLength, maxlen=plotLength))
self.isRun = True
self.isReceiving = False
self.thread = None
self.plotTimer = 0
self.previousTimer = 0
self.started = False
self.csvData = []
print('Trying to connect to: ' + str(serialPort) + ' at ' + str(serialBaud) + ' BAUD.')
try:
self.serialConnection = serial.Serial(serialPort, serialBaud, timeout=4)
print('Connected to ' + str(serialPort) + ' at ' + str(serialBaud) + ' BAUD.')
except:
print("Failed to connect with " + str(serialPort) + ' at ' + str(serialBaud) + ' BAUD.')
def readSerialStart(self):
if self.thread == None:
self.thread = Thread(target=self.backgroundThread)
self.thread.start()
# Block till we start receiving values
while self.isReceiving != True:
time.sleep(0.1)
def getSerialData(self, frame, lines, lineValueText, lineLabel, timeText, pltNumber):
global cond
if cond == True:
# if pltNumber == 0: # in order to make all the clocks show the same reading
if not self.started:
currentTimer = time.perf_counter()
self.plotTimer = int((currentTimer - self.previousTimer) * 1000) # the first reading will be erroneous
self.previousTimer = currentTimer
self.started = True
self.privateData = copy.deepcopy(
self.rawData) # so that the 4 values in our plots will be synchronized to the same sample time
timeText.set_text('Plot Interval = ' + str(self.plotTimer) + 'ms')
data = self.privateData[(pltNumber * self.dataNumBytes):(self.dataNumBytes + pltNumber * self.dataNumBytes)]
value, = (struct.unpack(self.dataType, (data)))
self.data[pltNumber].append(value) # we get the latest data point and append it to our array
lines.set_data(range(self.plotMaxLength), self.data[pltNumber])
lineValueText.set_text('[' + lineLabel + '] = ' + str(value))
# self.csvData.append([self.data[0][-1], self.data[1][-1], self.data[2][-1]])
if lineLabel == 'Detector A':
global A
A = float(value)
if lineLabel == 'Detector B':
global B
B = float(value)
if lineLabel == 'Detector C':
global C
C = float(value)
if lineLabel == 'Detector D':
global D
D = float(value)
if lineLabel == 'Temp Detector':
global Ta
Ta = float(value)
if lineLabel == 'Temp Servo':
global Tb
Tb = float(value)
if lineLabel == 'Voltage Servo':
global Va
Va = float(value)
if lineLabel == 'Voltage Detector':
global Vb
Vb = float(value)
if lineLabel == 'Angle':
global Ang
Ang = float(value)
Sum = (A + B + C + D)
Az = (A + D - C - B)
El = (A + B - C - D)
def backgroundThread(self): # retrieve data
time.sleep(0.8) # give some buffer time for retrieving data
self.serialConnection.reset_input_buffer()
while (self.isRun):
self.serialConnection.readinto(self.rawData)
self.isReceiving = True
def Sum(self):
global A, B, C, D
return '%.6f' % (A + B + C + D)
def Az(self):
global A, B, C, D
return '%.6f' % (A - B - C + D)
def El(self):
global A, B, C, D
return '%.6f' % (A + B - C - D)
def A(self):
global A
return '%.6f' % (A)
def B(self):
global B
return '%.6f' % (B)
def C(self):
global C
return '%.6f' % (C)
def D(self):
global D
return '%.6f' % (D)
def Ang(self):
global Ang
return '%.6f' % (Ang)
def TA(self):
global Ta
return '%.6f' % (Ta)
def TB(self):
global Tb
return '%.6f' % (Tb)
def VA(self):
global Va
return '%.6f' % (Va)
def VB(self):
global Vb
return '%.6f' % (Vb)
''''
def m1(self):
x = A - B - C + D
y = A + B - C - D
if x > 0 and y > 0:
angle = math.degrees(math.atan(y / x))
elif (x < 0 and y > 0) or (x < 0 and y < 0):
angle = math.degrees(math.atan(y / x)) + 180
elif x > 0 and y < 0:
angle = math.degrees(math.atan(y / x)) + 360
elif x == 0:
angle = 0
return int(angle)
def m1(self):
angle=0
global A,B,C,D
x = A-B-C+D
y = A+B-C-D
if x > 0 and y > 0:
angle = math.degrees(math.atan(y / x))
elif (x < 0 and y > 0) or (x < 0 and y < 0):
angle = math.degrees(math.atan(y / x)) + 180
elif x > 0 and y < 0:
angle = math.degrees(math.atan(y / x)) + 360
elif x==0:
angle=0
return angle
def m2(self):
angle1=0
global A,B,C,D
x1 = math.log10((A + D) / (B + C))
y1 = math.log10((A + B) / (D + C))
if x1==0 or (B+C)==0 or (D+C)==0:
angle1=0
elif x1 > 0 and y1 > 0:
angle1 = math.degrees(math.atan(y1 / x1))
elif (x1 < 0 and y1 > 0) or (x1 < 0 and y1 < 0):
angle1 = math.degrees(math.atan(y1 / x1)) + 180
elif x1 > 0 and y1 < 0:
angle1 = math.degrees(math.atan(y1 / x1)) + 360
return angle1
'''''
def close(self):
self.isRun = False
self.thread.join()
self.serialConnection.close()
print('Disconnected...')
# df = pd.DataFrame(self.csvData)
# df.to_csv('/home/Abhinav Kumar/Desktop/data.csv')
def plot_start():
global cond
cond = True
def plot_stop():
global cond
cond = False
def makeFigure(xLimit, yLimit, title):
xmin, xmax = xLimit
ymin, ymax = yLimit
fig = plt.figure(dpi=100)
fig.set_figheight = 1
fig.set_figwidth = 3
ax = plt.axes(xlim=(xmin, xmax), ylim=(int(ymin - (ymax - ymin) / 10), int(ymax + (ymax - ymin) / 10)))
ax.set_title(title)
ax.set_xlabel("Time")
ax.set_ylabel("Detector Output")
ax.grid(True)
return fig, ax
class Window(Frame):
def __init__(self, flist, master, SerialReference):
Frame.__init__(self, master)
self.entry = None
self.setPoint = None
self.master = master # a reference to the master window
self.serialReference = SerialReference # keep a reference to our serial connection so that we can use it for bi-directional communicate from this class
self.initWindow(flist) # initialize the window with our settings
def initWindow(self, flist):
self.master.title("Detector Data")
p = {1: [50, 120], 2: [480, 120], 3: [900, 120], 4: [50, 430], 5: [480, 430], 6: [900, 430], 7: [50, 750],
8: [480, 750], 9: [900, 750]}
x = 1
for item in flist:
canvas = FigureCanvasTkAgg(item, master=self.master)
# toolbar = NavigationToolbar2TkAgg(canvas, self.master)
canvas.get_tk_widget().place(x=p[x][0], y=p[x][1], width=400, height=300)
canvas.draw()
x += 1
def main():
portName = 'COM7'
# portName = '/dev/ttyUSB0'
baudRate = 38400
maxPlotLength = 100 # number of points in x-axis of real time plot
dataNumBytes = 4 # number of bytes of 1 data point
numPlots = 9 # number of plots in 1 graph
s = serialPlot(portName, baudRate, maxPlotLength, dataNumBytes, numPlots) # initializes all required variables
s.readSerialStart() # starts background thread
# plotting starts below
pltInterval = 1 # Period at which the plot animation updates [ms]
lineLabelText = ['Detector A', 'Detector B', 'Detector C', 'Detector D', 'Temp Detector', 'Temp Servo',
'Voltage Servo', 'Volatge detector', 'Angle']
title = ['Detector A', 'Detector B', 'Detector C', 'Detector D', 'Temp Detector', 'Temp Servo', 'Voltage Servo',
'Volatge detector', 'Angle']
xLimit = [(0, maxPlotLength), (0, maxPlotLength), (0, maxPlotLength), (0, maxPlotLength), (0, maxPlotLength),
(0, maxPlotLength), (0, maxPlotLength), (0, maxPlotLength), (0, maxPlotLength)]
yLimit = [(0, 3), (0, 3), (0, 3), (0, 3), (0, 60), (0, 60), (0, 8), (0, 8), (-120, 120)]
style = ['r-', 'g-', 'b-', 'y-', 'r-', 'g-', 'b-', 'y-', 'r-'] # linestyles for the different plots
# anim = []
root = Tk.Tk()
image = Image.open("kalyani(3).gif")
test = ImageTk.PhotoImage(image)
label = Tk.Label(image=test)
label.image = test
label.place(x=25, y=25)
image = Image.open("a.gif")
test = ImageTk.PhotoImage(image)
label = Tk.Label(image=test)
label.image = test
label.place(x=1350, y=40)
image = Image.open("b.gif")
test = ImageTk.PhotoImage(image)
label = Tk.Label(image=test)
label.image = test
label.place(x=1650, y=40)
image = Image.open("c.gif")
test = ImageTk.PhotoImage(image)
label = Tk.Label(image=test)
label.image = test
label.place(x=1400, y=170)
root.configure(background='light blue')
# root.geometry('1920x1080')
w = Tk.Label(root, text="LASER SEEKER GUI", fg='black', bg='light blue')
desired_font = tkinter.font.Font(family="Arial Bold", size=40, weight='bold')
w.configure(font=desired_font)
w.pack(pady=15)
labelf = Tk.Label(root, text='A:', fg='black', bg='light blue')
desired_font = tkinter.font.Font(family="calibri", size=25, weight='bold')
labelf.configure(font=desired_font)
labelf.place(x=1350, y=450)
label4 = Tk.Label(root, text='ELE', fg='black', bg='light blue')
desired_font = tkinter.font.Font(family="calibri", size=25, weight='bold')
label4.configure(font=desired_font)
label4.place(x=1500, y=450)
def a():
label4.config(text=s.A())
root.after(1, a)
a()
labels = Tk.Label(root, text='SUM:', fg='black', bg='light blue')
desired_font = tkinter.font.Font(family="calibri", size=25, weight='bold')
labels.configure(font=desired_font)
labels.place(x=1350, y=300)
label1 = Tk.Label(root, text='SUM', fg='black', bg='light blue')
desired_font = tkinter.font.Font(family="calibri", size=25, weight='bold')
label1.configure(font=desired_font)
label1.place(x=1500, y=300)
def sum():
label1.config(text=s.Sum())
root.after(1, sum)
sum()
labela = Tk.Label(root, text='AZ:', fg='black', bg='light blue')
desired_font = tkinter.font.Font(family="calibri", size=25, weight='bold')
labela.configure(font=desired_font)
labela.place(x=1350, y=350)
label2 = Tk.Label(root, text='AZ', fg='black', bg='light blue')
desired_font = tkinter.font.Font(family="calibri", size=25, weight='bold')
label2.configure(font=desired_font)
label2.place(x=1500, y=350)
def az():
label2.config(text=s.Az())
root.after(1, az)
az()
labele = Tk.Label(root, text='ELE:', fg='black', bg='light blue')
desired_font = tkinter.font.Font(family="calibri", size=25, weight='bold')
labele.configure(font=desired_font)
labele.place(x=1350, y=400)
label3 = Tk.Label(root, text='ELE', fg='black', bg='light blue')
desired_font = tkinter.font.Font(family="calibri", size=25, weight='bold')
label3.configure(font=desired_font)
label3.place(x=1500, y=400)
def el():
label3.config(text=s.El())
root.after(1, el)
el()
start = Tk.Button(root, width=10, height=3, activebackground='green', fg='black', text="START",
font=('calibri', 12), command=lambda: plot_start())
start.place(x=1800, y=600)
# root.update();
stop = Tk.Button(root, width=10, height=3, activebackground='red', text="STOP", fg='black', font=('calibri', 12),
command=lambda: plot_stop())
stop.place(x=1800, y=700)
# root.update()
exit = Tk.Button(root, width=10, height=3, activebackground='yellow', fg='black', text="EXIT", font=('calibri', 12),
command=root.destroy)
exit.place(x=1800, y=900)
flist = []
axlist = []
a = []
b = []
c = []
anim = []
for i in range(9):
fig, ax = makeFigure(xLimit[i], yLimit[i], title[i])
lines = ax.plot([], [], style[i], label=lineLabelText[i])[0]
timeText = ax.text(0.40, 0.95, '', transform=ax.transAxes)
lineValueText = ax.text(0.40, 0.90, '', transform=ax.transAxes)
flist.append(fig)
axlist.append(axlist)
a.append(lines)
b.append(timeText)
c.append(lineValueText)
app = Window(flist, root, s)
for i in range(9):
anim.append(animation.FuncAnimation(flist[i], s.getSerialData, frames=200,
fargs=(a[i], c[i], lineLabelText[i], b[i], i),
interval=pltInterval)) # fargs has to be a tuple
# plt.legend(loc="upper left")
# plt.show()
root.mainloop()
s.close()
if __name__ == '__main__':
main()
Am getting error for the A label. How do I solve this? Thanks in advance.
The error is happening for this part of code:
labelf = Tk.Label(root, text='A:', fg='black', bg='light blue')
desired_font = tkinter.font.Font(family="calibri", size=25, weight='bold')
labelf.configure(font=desired_font)
labelf.place(x=1350, y=450)
label4 = Tk.Label(root, text='ELE', fg='black', bg='light blue')
desired_font = tkinter.font.Font(family="calibri", size=25, weight='bold')
label4.configure(font=desired_font)
label4.place(x=1500, y=450)
def a():
label4.config(text=s.A())
root.after(1, a)
a()
And the traceback is this:
Traceback (most recent call last):
File "C:\Program Files\Python39\lib\tkinter\__init__.py", line 1892, in __call__
return self.func(*args)
File "C:\Program Files\Python39\lib\tkinter\__init__.py", line 814, in callit
func(*args)
File "C:\Users\Abhinav Kumar\PycharmProjects\realtimemultiple\Nag2.py", line 351, in a
root.after(1, a)
File "C:\Program Files\Python39\lib\tkinter\__init__.py", line 820, in after
callit.__name__ = func.__name__
AttributeError: 'list' object has no attribute '__name__'
When you do this:
def a():
label4.config(text=s.A())
root.after(1, a) a()
You defined a as an function that is callable to your interpreter.
But later in your code you define a as a list that isn't callable.
a = []
So you basically have overwritten your variable and you try to access the older content of that variable which isn't possible. So when you do this:
a = 1
a = 2
then a will become 2 and 1 is gone except you would resign it to 1.
Got it?

Python Tkinter & matplotlib - RuntimeError

Okay so I wrote this code...
#!/usr/bin/env
import sys
import time
import subprocess
from Tkinter import *
import numpy
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib
import matplotlib.pyplot as plt
import threading
CDatei = subprocess.Popen("/home/pi/meinc++/Spi")
print("Hallo")
i = 0
x = 0
def GetValue():
with open("/home/pi/meinc++/BeispielDatei.txt","r") as Datei:
for line in Datei:
time.sleep(0.01)
return line
def WithoutNull(input):
ReturnValue = input
while ReturnValue is None:
ReturnValue = GetValue()
return ReturnValue
def UebergabeWert():
while x == 0:
WholeString = WithoutNull(GetValue())
StringVar, DatumVar = WholeString.strip().split(' - ')
IntStringVar = [int(v) for v in StringVar.split()]
return IntStringVar, DatumVar
def MinutenWert():
Maximum = 0
Minimum = 0
i = 0
LaengeArray = 0
Multiplikator = 10000
ArrayValue = [-999999]*Multiplikator
AlteZeit = time.time()
while 1:
CompleteValue, Trash = UebergabeWert()
ArrayValue[i] = CompleteValue[0]
i = i + 1
ArrayFilter = filter(lambda c: c != -999999,ArrayValue)
ArraySumme = numpy.sum(ArrayFilter)
LaengeArray = len(ArrayFilter)
Mittelwert = ArraySumme/LaengeArray
ArraySortierung = sorted(ArrayFilter)
Maximum = ArraySortierung[LaengeArray-1]
Minimum = ArraySortierung[0]
NeueZeit = time.time()
if NeueZeit-AlteZeit >= 60:
AlteZeit = time.time()
ArrayValue[i:Multiplikator] = [-999999]*(Multiplikator-i)
i = 0
yield Mittelwert
yield Maximum
yield Minimum
yield LaengeArray
yield ArrayFilter
def UebergabeTkinter():
while 1:
Mittelwert = next(MinutenWertYield)
Maximum = next(MinutenWertYield)
Minimum = next(MinutenWertYield)
LaengeArray = next(MinutenWertYield)
ArrayFilter = next(MinutenWertYield)
CompleteValue, DatumVar = UebergabeWert()
Variable1.set(CompleteValue[0])
Variable2.set(CompleteValue[1])
Variable3.set(CompleteValue[2])
Variable4.set(CompleteValue[3])
VariableMittelwert.set(Mittelwert)
VariableMaximum.set(Maximum)
VariableMinimum.set(Minimum)
t = threading.Thread(target = Grafik)
t.start()
root.update()
def Grafik():
GrafikAnfang = time.time()
Array = 0
ArrayGrafik = [0]*20
GrafikEnde = 1
while 1:
CompleteValue, DatumVar = UebergabeWert()
ArrayGrafik[Array] = CompleteValue[0]
LaengeArrayGrafik = len(ArrayGrafik)
fig = Figure(figsize = (3, 3))
axis = fig.add_subplot(111)
axis.legend()
axis.grid()
canvas = FigureCanvasTkAgg(fig, master = root)
canvas.get_tk_widget().grid(row=10,column=0,rowspan=2,columnspan=2)
LinienBreite = numpy.linspace(1,LaengeArrayGrafik,LaengeArrayGrafik)
axis.plot(LinienBreite,ArrayGrafik,'b-')
axis.set_xticks(LinienBreite)
DatumArray = [DatumVar]
axis.set_xticklabels(DatumArray)
canvas.draw()
fig.clear()
print Array
if GrafikEnde-GrafikAnfang < 600:
Array = Array + 1
GrafikEnde = time.time()
if GrafikEnde-GrafikAnfang >= 600:
del ArrayGrafik[0]
def Exit():
root.destroy()
return
try:
MinutenWertYield = MinutenWert()
root = Tk()
Leiste = Menu(root)
root.config(menu = Leiste)
DateiMenu = Menu(Leiste)
Leiste.add_cascade(label = "datei", menu = DateiMenu)
DateiMenu.add_command(label = "Exit", command = Exit)
EditMenu = Menu(Leiste)
Leiste.add_cascade(label = "edit", menu = EditMenu)
Variable1 = IntVar()
Variable2 = IntVar()
Variable3 = IntVar()
Variable4 = IntVar()
VariableMittelwert = IntVar()
VariableMaximum = IntVar()
VariableMinimum = IntVar()
Ausgang = 0
for column in range(0,8,2):
String1 = "Ausgang "
String1 += `Ausgang`
Ausgang = Ausgang + 1
Label(text = String1).grid(row=0,column=column)
Ausgang = 0
for column in range(0,8,2):
String1 = "Der Wert von "
String2 = " ist: "
String1 += `Ausgang`
Ausgang = Ausgang + 1
String3 = String1+String2
Label(text = String3).grid(row=2,column=column)
Label(text = "Der Mittelwert ist: ").grid(row=4,column=0)
Label(text = "Das Maximum ist: ").grid(row=5,column=0)
Label(text = "Das Mimimum ist: ").grid(row=6,column=0)
Label1 = Label(root, textvariable = Variable1)
Label1.grid(row = 2, column = 1)
Label2 = Label(root, textvariable = Variable2)
Label2.grid(row = 2, column = 3)
Label3 = Label(root, textvariable = Variable3)
Label3.grid(row = 2, column = 5)
Label4 = Label(root, textvariable = Variable4)
Label4.grid(row = 2, column = 7)
LabelMittelwert = Label(root, textvariable = VariableMittelwert)
LabelMittelwert.grid(row = 4, column = 1)
LabelMaximum = Label(root, textvariable = VariableMaximum)
LabelMaximum.grid(row = 5, column = 1)
LabelMinimum = Label(root, textvariable = VariableMinimum)
LabelMinimum.grid(row = 6, column = 1)
UebergabeTkinter()
print "Hallo"
root.mainloop()
except KeyboardInterrupt:
CDatei.kill()
root.quit()
root.destroy()
and when i run it, it says "RuntimeError: main thread is not in the main loop".
Short explanation of the code: It's a code to read out sensor data from a text file -
GetValue().
If the Data is Null it'll read out again - WithoutNull().
The Data is then splitted into data and timestamp (cause it has the format val1, val2, val3, val4, time) - UebergabeWert.
Then the maxima, minima and average of the data will be measured - MinutenWert()
After this, the values are set as labels and go their way into Tkinter - UebergabeTkinter()
The Tkinter build is mainly in Try:
What I wanted to do there, is to implement a graph to Tkinter, but because of the fast changing values it got tremendously slow so i decided to put the graph build in a thread and run it parallel to Tkinter. Unfortunately, it doesn't seem to work and I don't know why
Any suggestions?

Threading, tkinter and winsound

I am trying to create a program that plays sound and shows the wave. However, when I try to call the multiprocessing.Process method so that I can cancel the playback if the user clicks the play button before the sound has finished. However, instead of calling my actualPlaySound method, it seems to recall my code and another tkinter window opens. Please help!
from tkinter import *
import math, time, threading, winsound, os, multiprocessing
def update ():
global modded
if not modded:
modded = True
stringAmplitude.set (str (amplitude.get ()))
stringFrequency.set (str (frequency.get ()))
modded = False
c.delete (ALL)
width, height = c.winfo_width (), c.winfo_height () / 2
freq = frequency.get ()
c.create_line (0, 25, width, 25)
c.create_polygon (15, 10, 0, 25, 15, 40)
c.create_polygon (width - 15, 10, width, 25, width - 15, 40)
if freq <= 100: c.create_text (width / 2, 15, text = "1 second")
elif freq <= 1000:
c.create_text (width / 2, 15, text = "10 milliseconds")
freq = freq / 100
else:
c.create_text (width / 2, 15, text = "1 millisecond")
freq = freq / 1000
ampl, freq = amplitude.get () ** 0.5, freq * math.pi * 2
for x in range (width):
c.create_line (x, math.sin (x * freq / width) * ampl * (height - 30 if height > 30 else 0) / 10 + (height if height > 35 else 35), x + 1, math.sin ((x + 1) * freq / width) * ampl * (height - 30 if height > 30 else 0) / 10 + (height if height > 35 else 35))
def stringUpdate ():
global modded
if not modded:
modded = True
try: amplitude.set (float (stringAmplitude.get ()))
except: pass
try: frequency.set (float (stringFrequency.get ()))
except: pass
modded = False
def playSound ():
global p
if not p or not p.is_alive ():
p = multiprocessing.Process (target = actualPlaySound)
p.start ()
else: p.terminate ()
def actualPlaySound ():
freq = int (frequency.get ())
if freq < 37:
l.config (text = "Too low!")
elif freq > 32767:
l.config (text = "Too high!")
else:
l.config (text = "Playing...")
winsound.Beep (freq, 5000)
l.config (text = "Played!")
modded, p = False, None
root = Tk ()
root.title ("Waves")
amplitude, frequency, stringAmplitude, stringFrequency = DoubleVar (), DoubleVar (), StringVar (), StringVar ()
amplitude.set (50)
frequency.set (1000)
stringAmplitude.set ("50")
stringFrequency.set ("1000")
amplitude.trace ("w", lambda a, b, c: update ())
frequency.trace ("w", lambda a, b, c: update ())
stringAmplitude.trace ("w", lambda a, b, c: stringUpdate ())
stringFrequency.trace ("w", lambda a, b, c: stringUpdate ())
root.bind ("<Configure>", lambda event: update ())
Label (root, text = "Frequency").grid (row = 0, column = 0)
Scale (root, from_ = 50, to = 32000, orient = "horizontal", variable = frequency).grid (row = 1, column = 0)
Entry (root, textvariable = stringFrequency).grid (row = 2, column = 0)
Frame (root, height = 50).grid (row = 3, column = 0)
Label (root, text = "Amplitude (this doesn't\nchange playback volume)").grid (row = 4, column = 0)
Scale (root, orient = "horizontal", variable = amplitude).grid (row = 5, column = 0)
Entry (root, textvariable = stringAmplitude).grid (row = 6, column = 0)
l = Label (root, text = "Ready to go!")
l.grid (row = 7, column = 0)
Button (root, text = "Play", command = lambda: threading.Thread (target = playSound).start ()).grid (row = 8, column = 0)
c = Canvas (root, bg = "white", width = 500, height = 400)
c.grid (row = 0, column = 1, rowspan = 20, sticky = "nsew")
Grid.columnconfigure (root, 1, weight = 1)
for y in range (20): Grid.rowconfigure (root, y, weight = 1)
update ()
root.mainloop ()
os._exit (0)

Real time plotting of serial data with python and tkinter

I have been working for some time to find a way to graph incoming data from an arduino with a Python GUI. I was able to accomplish this using the Matplotlib animation function to read in 6 different variables and plot 4 of them 2 on one subplot 2 on another subplot. This was able to be done fast enough that it was graphing real time (20 samples per second).
I now need to modify the system to read in 12 different variables at the same time of which 8 are graphed. 4 on one sub plot 4 on another at the same rate of 20 samples per second. I haven't been able to get this to work and have tried a few different things and done a lot of research but can't seem to figure out how to do it with my limited knowledge of python. Im not very familiar with multiprocessing or multithreading but they seem to be the way that people are able to speed up the graphing process. I know that the matplotlib animated function itself is threaded so I'm not sure how much the threading would help with that or if there's a way to read in one thread and update the graph in another. I'm operating at the highest baudrate that the arduino supports 250000. I also was able to find an example where someone was able to get a very high speed plot in this post but havent been able to modify to work for my use: What is the best real time plotting widget for wxPython?
the data is received from the arduino like this:
integer.integer.integer|integer.integer.integer|integer.integer.integer|integer.integer.integer
where the pipe represents a new actuator (what each variable im sending is coming from)
I'm fairly new to python so sorry if this isnt so pythonic but here are two examples I have:
This is a gui using the animation function:
import Tkinter
import serial
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from matplotlib import pyplot as plt
import matplotlib.animation as animation
from collections import deque
import random
class App:
def __init__(self, master):
self.arduinoData = serial.Serial('com5', 250000)#115200)
frame = Tkinter.Frame(master)
self.running = False
self.ani = None
self.start = Tkinter.LabelFrame(frame, text="Start", borderwidth=10, relief=Tkinter.GROOVE, padx=10, pady=10)
self.start.grid(row=0, column=0, padx=20, pady=20)
self.run = Tkinter.Button(self.start, text="RUN", bd=10, height=5, width=10, command=self.getData)
self.run.grid(row=0, column=0, padx=5, pady=5)
self.stop_frame = Tkinter.LabelFrame(frame, text="STOP", borderwidth=10, relief=Tkinter.GROOVE, padx=10, pady=10 )
self.stop_frame.grid(row=0, column=1, padx=20, pady=20)
self.stop = Tkinter.Button(self.stop_frame, text="STOP", bd=10, height=5, width=10, command=self.stopTest)
self.stop.grid(row=0, column=0, padx=5, pady=5)
self.fig = plt.Figure()
self.ax1 = self.fig.add_subplot(211)
self.line0, = self.ax1.plot([], [], lw=2)
self.line1, = self.ax1.plot([], [], lw=2)
self.line2, = self.ax1.plot([], [], lw=2)
self.line3, = self.ax1.plot([], [], lw=2)
self.ax2 = self.fig.add_subplot(212)
self.line4, = self.ax2.plot([], [], lw=2)
self.line5, = self.ax2.plot([], [], lw=2)
self.line6, = self.ax2.plot([], [], lw=2)
self.line7, = self.ax2.plot([], [], lw=2)
self.canvas = FigureCanvasTkAgg(self.fig,master=master)
self.canvas.show()
self.canvas.get_tk_widget().grid(row=0, column=4, padx=20, pady=20)
frame.grid(row=0, column=0, padx=20, pady=20)
def getData(self):
if self.ani is None:
self.k = 0
self.arduinoData.flushInput()
self.arduinoData.write("<L>")
return self.start()
else:
self.arduinoData.write("<L>")
self.arduinoData.flushInput()
self.ani.event_source.start()
self.running = not self.running
def stopTest(self):
self.arduinoData.write("<H>")
if self.running:
self.ani.event_source.stop()
self.running = not self.running
def resetTest(self):
self.k = 0
self.xdata = []
self.pressure1 = []
self.displacement1 = []
self.cycle1 = []
self.pressure2 = []
self.displacement2 = []
self.cycle2 = []
self.pressure3 = []
self.displacement3 = []
self.cycle3 = []
self.pressure4 = []
self.displacement4 = []
self.cycle4 = []
self.line1.set_data(self.xdata, self.ydata1)
self.line2.set_data(self.xdata, self.ydata2)
self.ax1.set_ylim(0,1)
self.ax1.set_xlim(0,1)
self.ax2.set_ylim(0,1)
self.ax2.set_xlim(0,1)
def start(self):
self.xdata = []
self.pressure1 = []
self.displacement1 = []
self.cycle1 = []
self.pressure2 = []
self.displacement2 = []
self.cycle2 = []
self.pressure3 = []
self.displacement3 = []
self.cycle3 = []
self.pressure4 = []
self.displacement4 = []
self.cycle4 = []
self.k = 0
self.arduinoData.flushInput()
self.ani = animation.FuncAnimation(
self.fig,
self.update_graph,
interval=1,
repeat=True)
self.arduinoData.write("<L>")
self.running = True
self.ani._start()
def update_graph(self, i):
self.xdata.append(self.k)
while (self.arduinoData.inWaiting()==0):
pass
x = self.arduinoData.readline()
strip_data = x.strip()
split_data = x.split("|")
actuator1 = split_data[0].split(".")
actuator2 = split_data[1].split(".")
actuator3 = split_data[2].split(".")
actuator4 = split_data[3].split(".")
self.pressure1.append(int(actuator1[0]))
self.displacement1.append(int(actuator1[1]))
self.cycle1 = int(actuator1[2])
self.pressure2.append(int(actuator2[0]))
self.displacement2.append(int(actuator2[1]))
self.cycle2 = int(actuator2[2])
self.pressure3.append(int(actuator3[0]))
self.displacement3.append(int(actuator3[1]))
self.cycle3 = int(actuator3[2])
self.pressure4.append(int(actuator4[0]))
self.displacement4.append(int(actuator4[1]))
self.cycle4 = int(actuator4[2])
self.line0.set_data(self.xdata, self.pressure1)
self.line1.set_data(self.xdata, self.pressure2)
self.line2.set_data(self.xdata, self.pressure3)
self.line3.set_data(self.xdata, self.pressure4)
self.line4.set_data(self.xdata, self.displacement1)
self.line5.set_data(self.xdata, self.displacement2)
self.line6.set_data(self.xdata, self.displacement3)
self.line7.set_data(self.xdata, self.displacement4)
if self.k < 49:
self.ax1.set_ylim(min(self.pressure1)-1, max(self.pressure3) + 1)
self.ax1.set_xlim(0, self.k+1)
self.ax2.set_ylim(min(self.displacement1)-1, max(self.displacement3) + 1)
self.ax2.set_xlim(0, self.k+1)
elif self.k >= 49:
self.ax1.set_ylim(min(self.pressure1[self.k-49:self.k])-1, max(self.pressure3[self.k-49:self.k]) + 1)
self.ax1.set_xlim(self.xdata[self.k-49], self.xdata[self.k-1])
self.ax2.set_ylim(min(self.displacement1[self.k-49:self.k])-1, max(self.displacement3[self.k-49:self.k]) + 1)
self.ax2.set_xlim(self.xdata[self.k-49], self.xdata[self.k-1])
self.k += 1
root = Tkinter.Tk()
app = App(root)
root.mainloop()
This is a gui that prints to the monitor:
import Tkinter
import serial
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from matplotlib import pyplot as plt
import matplotlib.animation as animation
import time
class App:
def __init__(self, master):
self.arduinoData = serial.Serial('com5', 250000, timeout=0)
frame = Tkinter.Frame(master)
self.go = 0
self.start = Tkinter.LabelFrame(frame, text="Start", borderwidth=10, relief=Tkinter.GROOVE, padx=10, pady=10)
self.start.grid(row=0, column=0, padx=20, pady=20)
self.run = Tkinter.Button(self.start, text="RUN", bd=10, height=5, width=10, command=self.getData)
self.run.grid(row=0, column=0, padx=5, pady=5)
self.stop_frame = Tkinter.LabelFrame(frame, text="STOP", borderwidth=10, relief=Tkinter.GROOVE, padx=10, pady=10 )
self.stop_frame.grid(row=0, column=1, padx=20, pady=20)
self.stop = Tkinter.Button(self.stop_frame, text="STOP", bd=10, height=5, width=10, command=self.stopTest)
self.stop.grid(row=0, column=0, padx=5, pady=5)
self.fig = plt.Figure()
self.ax1 = self.fig.add_subplot(211)
self.line0, = self.ax1.plot([], [], lw=2)
self.line1, = self.ax1.plot([], [], lw=2)
self.line2, = self.ax1.plot([], [], lw=2)
self.line3, = self.ax1.plot([], [], lw=2)
self.ax2 = self.fig.add_subplot(212)
self.line4, = self.ax2.plot([], [], lw=2)
self.line5, = self.ax2.plot([], [], lw=2)
self.line6, = self.ax2.plot([], [], lw=2)
self.line7, = self.ax2.plot([], [], lw=2)
self.canvas = FigureCanvasTkAgg(self.fig,master=master)
self.canvas.show()
self.canvas.get_tk_widget().grid(row=0, column=4, padx=20, pady=20)
frame.grid(row=0, column=0, padx=20, pady=20)
def getData(self):
self.k = 0
self.xdata = []
self.pressure1 = []
self.displacement1 = []
self.cycle1 = []
self.pressure2 = []
self.displacement2 = []
self.cycle2 = []
self.pressure3 = []
self.displacement3 = []
self.cycle3 = []
self.pressure4 = []
self.displacement4 = []
self.cycle4 = []
self.arduinoData.flushInput()
self.go = 1
self.readData()
def readData(self):
if self.go == 1:
self.xdata.append(self.k)
while (self.arduinoData.inWaiting()==0):
pass
x = self.arduinoData.readline()
strip_data = x.strip()
split_data = x.split("|")
actuator1 = split_data[0].split(".")
actuator2 = split_data[1].split(".")
actuator3 = split_data[2].split(".")
actuator4 = split_data[3].split(".")
self.pressure1.append(int(actuator1[0]))
self.displacement1.append(int(actuator1[1]))
self.cycle1 = int(actuator1[2])
self.pressure2.append(int(actuator2[0]))
self.displacement2.append(int(actuator2[1]))
self.cycle2 = int(actuator2[2])
self.pressure3.append(int(actuator3[0]))
self.displacement3.append(int(actuator3[1]))
self.cycle3 = int(actuator3[2])
self.pressure4.append(int(actuator4[0]))
self.displacement4.append(int(actuator4[1]))
self.cycle4 = int(actuator4[2])
self.printData()
root.after(0, self.readData)
def printData(self):
print str(self.pressure1[self.k-1]) + " " +
str(self.displacement1[self.k-1]) + " " + str(self.cycle1) + " " +
str(self.pressure2[self.k-1]) + " " + str(self.displacement2[self.k-
1]) + " " + str(self.cycle2) + " " + str(self.pressure3[self.k-1]) +
" " + str(self.displacement3[self.k-1]) + " " + str(self.cycle3) + "
" + str(self.pressure4[self.k-1]) + " " +
str(self.displacement4[self.k-1]) + " " + str(self.cycle4)
def stopTest(self):
self.arduinoData.write("<H>")
self.go = 0
def resetTest(self):
self.k = 0
self.xdata = []
self.pressure1 = []
self.displacement1 = []
self.cycle1 = []
self.pressure2 = []
self.displacement2 = []
self.cycle2 = []
self.pressure3 = []
self.displacement3 = []
self.cycle3 = []
self.pressure4 = []
self.displacement4 = []
self.cycle4 = []
self.line1.set_data(self.xdata, self.ydata1)
self.line2.set_data(self.xdata, self.ydata2)
self.ax1.set_ylim(0,1)
self.ax1.set_xlim(0,1)
self.ax2.set_ylim(0,1)
self.ax2.set_xlim(0,1)
def start(self):
self.xdata = []
self.pressure1 = []
self.displacement1 = []
self.cycle1 = []
self.pressure2 = []
self.displacement2 = []
self.cycle2 = []
self.pressure3 = []
self.displacement3 = []
self.cycle3 = []
self.pressure4 = []
self.displacement4 = []
self.cycle4 = []
self.k = 0
self.arduinoData.write("<L>")
root = Tkinter.Tk()
app = App(root)
root.mainloop()
and here is an example arduino code:
int analog0 = 0;
int analog1 = 1;
int analog2 = 2;
int sensor0;
int sensor1;
int sensor2;
String pot0;
String pot1;
String Force;
int pot0holder;
int pot1holder;
String Forceholder;
unsigned long i = 0;
String Is;
int val = 0;
boolean Sensordata = false;
int cycles;
const byte numChars = 32;
char receivedChars[numChars];
boolean newData = false;
unsigned long CurrentMillis = 0;
unsigned long PrintMillis = 0;
int PrintValMillis = 50;
unsigned long SensorMillis = 0;
int SensorValMillis = 0;
void setup() {
// put your setup code here, to run once:
Serial.begin(250000);
}
void loop()
{
CurrentMillis = millis();
recvWithStartEndMarkers();
commands();
sensordata();
}
void sensordata()
{
if (CurrentMillis - SensorMillis >= SensorValMillis)
{
sensor0 = analogRead(analog0);
pot0holder = sensor0;
sensor1 = analogRead(analog1);
pot1holder = sensor1;
i += 1;
String potcolumn = String(pot0holder) + "." + String(pot1holder) + "." + String(i) + "|" + String(int(pot0holder)+30) + "." + String(int(pot1holder)+30) + "." + String(i) + "|" + String(int(pot0holder)+60) + "." + String(int(pot1holder)+60) + "." + String(i) + "|" + String(int(pot0holder)+90) + "." + String(int(pot1holder)+90) + "." + String(i);
Serial.println(potcolumn);
SensorMillis += SensorValMillis;
}
}
void recvWithStartEndMarkers()
{
static boolean recvInProgress = false; //creates variable visible to only one function with boolean
static byte ndx = 0;
char startMarker = '<'; //sets begin condition
char endMarker = '>'; //sets end condition
char rc; //sets variable type to char
while (Serial.available() > 0 && newData == false) {
rc = Serial.read(); //sets rc equal to serial value
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker) {
recvInProgress = true;
}
}
}
void commands()
{
if (newData == true)
{
if (receivedChars[0] == 'T')
{
PrintValMillis = atoi(&receivedChars[1]); //atoi -> Converting strings to integer
}
else if (receivedChars[0] == 'S')
{
cycles = atoi(&receivedChars[1]);
i = 0;
}
else if (receivedChars[0] == 'L')
{
val = atoi(&receivedChars[1]);
i = 0;
}
}
newData = false;
}
Thanks in advance for any help or advice any one has.
So your reading process takes most of the time. I would put the reading in a separate task and do the evaluation/splitting of the data in the main (drawing)-process. Unfortunately, I am not a tkinter user, so I have written this without any special gui-framework. But I think you can adapt this to your needs.
That would look like that:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import multiprocessing as mp
import time
# global variables
fig = plt.figure(1)
# first sub-plot
ax1 = fig.add_subplot(211)
line1, = ax1.plot([], [], lw=2)
ax1.grid()
xdata1, ydata1 = [], []
# second sub-plot
ax2 = fig.add_subplot(212)
line2, = ax2.plot([], [], lw=2)
ax2.grid()
xdata2, ydata2 = [], []
# the multiprocessing queue
q = mp.Queue()
# data generator in separate process
# here would be your arduino data reader
def dataGen(output):
for x in range(50):
output.put((x, np.sin(x)))
# update first subplot
def update1(data):
# update the data
t, y = data
xdata1.append(t)
ydata1.append(y)
xmin, xmax = ax1.get_xlim()
ymin, ymax = ax1.get_ylim()
if t >= xmax:
ax1.set_xlim(xmin, 2*xmax)
if y >= ymax:
ax1.set_ylim(ymin, 2*ymax)
if y <= ymin:
ax1.set_ylim(2*ymin, ymax)
line1.set_data(xdata1, ydata1)
return line1,
# update second subplot
def update2(data):
# update the data
t, y = data
xdata2.append(t)
ydata2.append(y)
xmin, xmax = ax2.get_xlim()
ymin, ymax = ax2.get_ylim()
if t >= xmax:
ax2.set_xlim(xmin, 2*xmax)
if y >= ymax:
ax2.set_ylim(ymin, 2*ymax)
if y <= ymin:
ax2.set_ylim(2*ymin, ymax)
line2.set_data(xdata2, ydata2)
return line2,
# called at each drawing frame
def run(data):
# get data from queue, which is filled in separate process, blocks until
# data is available
data = q.get(block=True, timeout=.5)
# put here your variable separation
data1 = (2*data[0], 3*data[1])
data2 = (data[0], data[1])
#provide the data to the plots
a = update1(data1)
b = update2(data2)
fig.canvas.draw()
return a+b
if __name__ == "__main__":
# count of reader processes
n_proc = 1
# setup workers
pool = [mp.Process(target=dataGen, args=(q,)) for x in range(n_proc)]
for p in pool:
p.daemon = True
p.start()
# wait a few sec for the process to become alive
time.sleep(3)
# start your drawing
ani = animation.FuncAnimation(fig, run, frames=60, blit=True, interval=10,
repeat=False)
plt.show()
print('done')
My question is very similar with yours.
I need to get data from a profi-bus network every 80ms, and I want the data to be plotted while sampling.
I used multiprocessing to solve the problem.The Pipe was used for communication between two processes.
When the Plotter gets data from Collector, and it is going to plot something, the Plotter will send a message to the Collector. and then the Collector will stop sending the data and put the data into a list.
when plotter done its job, it tells the collector that 'now you can send data', the collector then send the data and clear the list.
import time
import numpy as np
from matplotlib import pyplot as plt
import multiprocessing
from multiprocessing import Process, Pipe
from random import random
class DataGennerator(object):
"""docstring for DataGennerator"""
def __init__(self, data_pipe):
super(DataGennerator, self).__init__()
print('Data Gennerator Init...')
self.data_buffer = []
self.t = 0
self.start_time = 0
self.data_pipe = data_pipe
self.plot_inprogess = False
self.data_ready = False
def run(self):
self.start_time = time.time()
for i in range(0, 400):
self.loop_cycle()
print('Total Time:', time.time()-self.start_time)
print('Run completion......')
def loop_cycle(self):
self.t = time.time()-self.start_time
new_data = [time.time()-self.start_time, np.sin(self.t), np.cos(2*self.t), np.cos(self.t*4), random()]
self.send_data(new_data)
time.sleep(0.08)
def send_data(self, new_data):
if self.plot_inprogess or not self.data_ready:
self.data_buffer.append(new_data)
self.data_ready = True
# Wait 1ms to read plotter's msg
if self.data_pipe.poll(0.0001):
self.plot_inprogess = self.data_pipe.recv()
else:
self.data_pipe.send(self.data_buffer)
self.data_buffer = []
self.data_ready = False
# Function to collect data by using DataGennerator
def get_data(data_pipe):
dg = DataGennerator(data_pipe)
dg.run()
data_pipe.send('EXIT')
print('>>> Finished')
# use plotter_pipe to communicate with data collector
# and when get data from the collector, updata the figure
def updata_plot(plotter_pipe, plot_inprogess=True):
plot_inprogess = True
fig, ax = plt.subplots(nrows=4, ncols=1, figsize=(6,8), sharex=True)
fig.set_tight_layout(True)
styles = ['rs-', 'gs-', 'bs-', 'ro-', 'go-', 'bo-']*10
lines = []
for index, name in enumerate(['sin(t)', 'cos(t)', 'cos(2t)', 'random']):
line, = ax[index].plot([],[], styles[index],label=name, markersize=4, markerfacecolor='w')
ax[index].set_ylabel(name, color=styles[index][0], fontweight='bold')
lines.append(line)
ax[-1].set_xlabel('Time /s')
fig.align_ylabels(ax)
plt.ion()
plt.show(block=False)
plt.draw()
# Read the 1st data package and convert it to Numpy Array
data_array = np.array(plotter_pipe.recv())
while True:
try:
# Read data_buffer sent by Data Collector
data_buffer = plotter_pipe.recv() #[ [data1], [data2]...]
# If the DataCollector says EXIT, then break the while loop
if data_buffer == 'EXIT': break
# Raise a flag to indicate that Plot is in progress
plotter_pipe.send(True)
# Append data_buffer to Data Array
data_array = np.append(data_array, np.array(data_buffer), axis=0)
for i in range(0, 4):
lines[i].set_xdata(data_array[:,0])
lines[i].set_ydata(data_array[:,i+1])
ax[i].relim()
ax[i].autoscale_view()
fig.canvas.draw()
plt.pause(0.001)
# Tell data collector that Plot has been finished
plotter_pipe.send(False)
except Exception as e:
raise e
finally:
pass
print('>>> Stop receiving data')
data_content = '\n'.join([', '.join(map(str,data_line)) for data_line in data_array])
with open('data.txt', 'w', encoding='UTF-8') as f:
f.write('time, xx, yy, zz, bb\n')
f.writelines(data_content)
plt.show(block=True)
if __name__ == '__main__':
plot_inprogess = True
data_pipe, plotter_pipe = multiprocessing.Pipe(True)
P1 = Process(target=get_data , args=(data_pipe,))
P2 = Process(target=updata_plot, args=(plotter_pipe,))
P1.start()
P2.start()
P1.join()
P2.join()

Improving tkinter.ttk code

I am a fairly novice programmer and through coffee, google, and an immense loss of hair and fingernails have managed to write a very messy code. I am asking anyone to help me simplify the code if possible.
from tkinter import ttk
from tkinter import *
from tkinter.ttk import *
one = 0
why = 'Total Number: {}'
no = 0
clack = 0
click = 'Clicks: {}'
s = ttk.Style()
s.theme_use('clam')
s.configure('red.Vertical.TProgressbar', foreground = 'red', background = 'red')
s.configure('green.Vertical.TProgressbar', foreground = 'green', background = 'green')
s.configure('TButton', relief = 'groove')
def iround(x):
y = round(x) - .5
return int(y) + (y > 0)
class Application(ttk.Frame):
def __init__(self, master = None):
ttk.Frame.__init__(self, master)
self.grid()
self.createWidgets()
def Number(self):
global one
cost = 10*(self.w['to'])
if self.number['text'] == "Make Number go up":
self.number['text'] = one
if iround(self.w.get()) == 0:
one += 1
else:
one += iround(self.w.get())
self.number['text'] = one
if self.number['text'] >= cost:
self.buy['state'] = 'normal'
else:
self.buy['state'] = 'disabled'
self.number['text'] >= cost
self.progress['value'] = one
if self.number['text'] >= cost:
self.progress['style'] = 'red.Vertical.TProgressbar'
else:
self.progress['style'] = 'green.Vertical.TProgressbar'
def Buy(self):
global one
self.w['to'] += 1
cost = 10*(self.w['to'])
one = self.number['text'] = (one + 10 - cost)
self.buy['text'] = ('+1 to slider | Cost: {}'.format(cost))
if self.number['text'] < (cost):
self.buy['state'] = 'disabled'
self.progress['value'] = one
self.progress['maximum'] += 10
if self.number['text'] >= cost:
self.progress['style'] = 'red.Vertical.TProgressbar'
else:
self.progress['style'] = 'green.Vertical.TProgressbar'
def scaleValue(self, event):
self.v['text'] = 'Slider Bonus + ' + str(iround(self.w.get()))
def clicks(self, event):
global click
global clack
if self.Clicks['text'] == 'Clicks: 0':
clack += 1
self.Clicks['text'] = click.format(clack)
self.Clicks['text'] = click.format(clack)
clack += 1
def NumberVal(self, event):
global why
global no
if self.fun['text'] == "Total Number: 0":
self.fun['text'] = why.format(no)
if iround(self.w.get()) == 0:
no += 1
else:
no += iround(self.w.get())
self.fun['text'] = why.format(no)
def createWidgets(self):
self.number = Button(self, text = 'Make number go up', command = self.Number, width = 20, style = 'TButton')
self.number.grid(row = 1, column = 1)
self.number.bind('<ButtonRelease>', self.clicks, add = '+')
self.number.bind('<ButtonRelease>', self.NumberVal, add = '+')
self.buy = Button(self, text = '+1 to Slider | Cost: 10', command = self.Buy, width = 20, style = 'TButton')
self.buy.grid(row = 2, column = 1)
self.buy.config(state = 'disabled')
self.v = Label(self, text = 'Slider Bonus + 0', width = 20, anchor = 'center')
self.v.grid(row = 3, column = 3)
self.w = Scale(self, from_ = 0, to = 1, orient = 'horizontal')
self.w.grid(row = 3, column = 1)
self.w.bind('<Motion>', self.scaleValue)
self.progress = Progressbar(self, value = 0, orient = 'vertical', maximum = 10, mode = 'determinate')
self.progress.grid(row = 1, rowspan = 5, column = 2)
self.Clicks = Label(self, text = 'Clicks: 0', width = 20, anchor = 'center')
self.Clicks.grid(row = 1, column = 3)
self.fun = Label(self, text = 'Total Number: 0', width = 20, anchor = 'center')
self.fun.grid(row = 2, column = 3)
app = Application()
app.master.title('Number')
app.mainloop()

Categories

Resources