The code below should run as it stands. I have three classes (pages in the GUI) that I want to animate a plot in. In each class (or page) I will be accessing a remote file with live data being generated by different audio codecs and network interference. So each class (page) needs to access this live data and plot it.
The problem is that only my last plot is animated with the live data. The first two plots are static, they don't get or update with the current data. I am trying to run the plotting in a separate thread for each class. It seems whichever matplotlib canvas I create last is the only one that properly animates, canvas3 = FigureCanvasTkAgg(f, self)
In this shortened version of the code I am randomly generating the live data, but it is using some logic from accessing a local text file, sampleData.txt, which is also included below the pyhton3 script.
I am having issues mainly in the bottom of the python file when I am defining Class MyApp(tk.Tk):. The class where the plot works is Class PageThree(), and the classes where the plot is currently not working are Class PageOne() and Class PageTwo(). If you click the button for "PageThree" you will see the working animated plot. Please help me get the other plots to also animate. I'm not sure if the issue is with the threading, matplotlib canvas, or the ani1 = animation.FuncAnimation(f, animate, interval=1000) function. I'm building from the command line ($ python3 gui2.py) and I don't get any errors or warnings either.
Thanks!
gui2.py
#!/usr/bin/env python3
import paramiko, threading
import time, os, subprocess
from subprocess import Popen
import sys
#if not sys.warnoptions:
# import warnings
# warnings.simplefilter("ignore")
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib import style
import tkinter as tk
from tkinter import ttk
# Just being used to debug plots
import random
LARGE_FONT=("Verdana", 12)
style.use("ggplot")
f = Figure(figsize=(9,6), dpi=100)
aPlot = f.add_subplot(211)
aPlot2 = f.add_subplot(212)
f2 = Figure(figsize=(9,6), dpi=100)
bPlot = f2.add_subplot(211)
bPlot2 = f2.add_subplot(212)
###==========================================================================================
### BEGIN FUNCS 4 FUN ###++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Function to execute the C++ code over ssh (Adaptive mode is default):
def start_ssh():
print("SSH started")
# Function to update live labels
def label_reader(label):
def reader():
label.config(text="Mode: "+ str(mode[-1]))
label.after(100, reader)
reader()
# Function to kill the processes running the C++ networking code:
def clear():
print("processes closed")
# Function to execute the C++ code over ssh using only Mode 1:
def start_ssh_singleMode():
print("SSH single mode started")
# Function to setup interference and/or set it back to 0%:
def interference_setup():
print("Interference setup")
# Function to add a hard-coded amount of interference:
def add_interference():
print("Interference added")
global mode
mode = ["0"]
global plossRate
global counter, counter2
# Plot to animate
def animate(i):
pullData = open("sampleData.txt", "r").read()
dataList = pullData.split('\n')
remote_file = dataList
curFrame = []
recFrame = []
#global mode
#mode = []
#bytesRead = []
#missingFrames = []
plossRate = []
counter = []
counter2 = []
counter2.append(0)
value = 0
for eachLine in remote_file:
if len(eachLine)>1:
value=value+1
#a, b, c, d, e = eachLine.split(',')
#curFrame.append(int(a))
#recFrame.append(int(b))
mode.append(random.randint(1,3))
#bytesRead.append(int(d))
#missingFrames.append(int(e))
plossRate.append(random.randint(0,90))
counter.append(int(value))
counter2.append(int(value))
#print("mode = " + str(c) + " lastFrame = " + str(b) + " conter = " + str(value))
aPlot.clear()
aPlot2.clear()
aPlot.plot(counter, plossRate)
aPlot.set_title('Packet Loss Rate')
aPlot.set_ylabel('Percentage')
aPlot2.plot(counter, mode[-counter[-1]:], 'bo')
#aPlot2.axis(0,counter[-1],0,3)
aPlot2.set_title('Current Audio Mode')
aPlot2.set_ylabel('mode')
#finally:
#remote_file.close()
# def make_plot(i):
# pullData = open("sampleData.txt", "r").read()
# dataList = pullData.split('\n')
# remote_file = dataList
# curFrame = []
# recFrame = []
# #global mode
# #mode = []
# #bytesRead = []
# #missingFrames = []
# plossRate = []
# counter = []
# counter2 = []
# counter2.append(0)
# value = 0
# for eachLine in remote_file:
# if len(eachLine)>1:
# value=value+1
# #a, b, c, d, e = eachLine.split(',')
# #curFrame.append(int(a))
# #recFrame.append(int(b))
# mode.append(random.randint(1,3))
# #bytesRead.append(int(d))
# #missingFrames.append(int(e))
# plossRate.append(random.randint(0,90))
# counter.append(int(value))
# counter2.append(int(value))
# #print("mode = " + str(c) + " lastFrame = " + str(b) + " conter = " + str(value))
#
# bPlot.clear()
# bPlot2.clear()
# bPlot.plot(counter, plossRate)
# bPlot.set_title('Packet Loss Rate')
# bPlot.set_ylabel('Percentage')
# bPlot2.plot(counter, mode[-counter[-1]:], 'bo')
# #aPlot2.axis(0,counter[-1],0,3)
# bPlot2.set_title('Current Audio Mode')
# bPlot2.set_ylabel('mode')
# Start plot
# def start_plot(self):
# canvas = FigureCanvasTkAgg(f, self)
# canvas.draw()
# canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
### END FUNCS 4 FUN ###++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
###==========================================================================================
###==========================================================================================
### BEGIN MAIN CLASS FOR NETWORKUP APP ###+++++++++++++++++++++++++++++++++++++++++++++++++++
class MyApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
#tk.Tk.iconbitmap(self, default="logo-no-halo-sm.png")
tk.Tk.wm_title(self, "Network Up")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, PageOne, PageTwo, PageThree):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = ttk.Label(self, text="Home Page", font=LARGE_FONT)
label.pack(pady=10, padx=10)
button1 = ttk.Button(self, text="PageOne", command=lambda: controller.show_frame(PageOne))
button1.pack()
button2 = ttk.Button(self, text="PageTwo", command=lambda: controller.show_frame(PageTwo))
button2.pack()
button3 = ttk.Button(self, text="PageThree", command=lambda: controller.show_frame(PageThree))
button3.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = ttk.Label(self, text="Audio (No Interference)", font=LARGE_FONT)
label.pack(pady=10, padx=10)
button1 = ttk.Button(self, text="Back to Home", command=lambda:[controller.show_frame(StartPage), clear()])
button1.pack()
button_start = ttk.Button(self, text="Play", command=lambda: start_ssh)
button_start.pack()
mode_label = ttk.Label(self, text="Mode 1")
mode_label.pack()
label_reader(mode_label)
#time.sleep(.5)
# plot_thread1 = threading.Thread(target=make_plot(self))
# plot_thread1.daemon = True
# plot_thread1.start()
# canvas1 = FigureCanvasTkAgg(f, self)
# canvas1.draw()
# canvas1.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
# Just trying something different here
self.start_plot()
def start_plot(self):
plot_thread1 = threading.Thread(target=animate(self))
plot_thread1.daemon = False
plot_thread1.start()
canvas = FigureCanvasTkAgg(f, self)
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = ttk.Label(self, text="Audio (Interference)", font=LARGE_FONT)
label.pack(pady=10, padx=10)
button1 = ttk.Button(self, text="Back to Home", command=lambda: [controller.show_frame(StartPage), clear(), interference_setup()])
button1.pack()
button_start_mode1 = ttk.Button(self, text="Play", command=lambda: [start_ssh_singleMode(), add_interference()])
button_start_mode1.pack()
mode_label = ttk.Label(self, text="Mode 1")
mode_label.pack()
label_reader(mode_label)
plot_thread2 = threading.Thread(target=animate(self))
plot_thread2.daemon = True
plot_thread2.start()
canvas2 = FigureCanvasTkAgg(f, self)
canvas2.draw()
canvas2.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
class PageThree(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = ttk.Label(self, text="Audio (Interference and Adaptive Codec)", font=LARGE_FONT)
label.pack(pady=10, padx=10)
button1 = ttk.Button(self, text="Back to Home", command=lambda: [interference_setup(), clear(), controller.show_frame(StartPage)])
button1.pack()
button_start2 = ttk.Button(self, text="Play", command=lambda: [start_ssh(), add_interference()])
button_start2.pack()
mode_label = ttk.Label(self, text="Mode 1")
mode_label.pack()
label_reader(mode_label)
plot_thread3 = threading.Thread(target=animate(self))
plot_thread3.daemon = True
plot_thread3.start()
canvas3 = FigureCanvasTkAgg(f, self)
canvas3.draw()
canvas3.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
#toolbar = NavigationToolbar2Tk(canvas, self)
#toolbar.update()
#canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
app = MyApp()
ani1 = animation.FuncAnimation(f, animate, interval=1000)
#ani2 = animation.FuncAnimation(f2, make_plot, interval=1000)
app.mainloop()
And here is the text file that I am using in some of the logic. It needs to be placed in the same directory that you run the python program from.
sampleData.txt
1,1
2,3
3,4
4,3
5,5
6,7
7,7
8,14
9,30
10,23
11,5
12,4
13,27
14,9
15,14
16,16
17,2
18,1
19,5
20,2