I am building a GUI that takes in sensor data from the raspberry pi and displays it onto a window via matplotlib animation. The code works fine, except when being run on raspberry pi, the matplotlib animation takes some time to execute, which momentarily blocks the sensor reading GetCPM that I'm interested in. How can I make both these programs run simultaneously without one clogging the other, I've tried the multiprocessing library, but I can't seem to get it to work.
Note: The sensor data that I'm plotting does not have to have a high sample rate, its the sensor that I'm displaying on label that does.
Here is my code
import matplotlib
matplotlib.use("TkAgg")
import numpy as np
import tkinter as tk
from tkinter import ttk
from tkinter import *
import math
import datetime as dt
import time
from collections import Counter
import random as rn
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib import style
import matplotlib.pyplot as plt
import threading as td
import multiprocessing as mp
from multiprocessing import Process, Queue
style.use('seaborn')
limit = np.array([5])
# Initialize Pressure Figure
fig1, ax1 = plt.subplots()
fig2, ax2 = plt.subplots()
x1, y1 = [], []
x2, y2 = [], []
TOT = []
CPM = 0
def GetValues(i, x, y, ax):
volts2 = rn.uniform(3,6)
x.append(dt.datetime.now().strftime('%H: %M: %S.%f'))
y.append(float(volts2))
x = x[-50:]
y = y[-50:]
ax.clear()
ax.plot(x, y, linewidth=1, color= 'k')
ax.fill_between(x, y, limit[0], where=(y > limit[0]), facecolor='forestgreen', alpha=0.7, interpolate=True)
ax.fill_between(x, y, limit[0], where=(y < limit[0]), facecolor='darkred', alpha=0.7, interpolate=True)
ax.set_xticklabels([])
def animate(i, x, y, ax):
volts = rn.uniform(2,8)
x.append(dt.datetime.now().strftime('%H: %M: %S.%f'))
y.append(float(volts))
x = x[-50:]
y = y[-50:]
ax.clear()
ax.plot(x, y, linewidth=1, color= 'k')
ax.fill_between(x, y, limit[0], where=(y > limit[0]), facecolor='forestgreen', alpha=0.7, interpolate=True)
ax.fill_between(x, y, limit[0], where=(y < limit[0]), facecolor='darkred', alpha=0.7, interpolate=True)
ax.set_xticklabels([])
def GetCPM():
global TOT, CPM
temp = 1
# Test Case
if temp == True:
TOT.append(True)
else:
TOT.append(False)
TOT = TOT[-2750:]
count = Counter(TOT)
CPM = count[True]
return CPM
class App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# If you want to customize the icon of the tk window, only accepts .ico
#tk.Tk.iconbitmap(self, default="iconname.ico")
tk.Tk.wm_title(self, "Pressure")
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 = {}
frame = GUI(container, self)
self.frames[GUI] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(GUI)
def show_frame(self,cont):
frame = self.frames[cont]
frame.tkraise()
class GUI(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
canvas1 = FigureCanvasTkAgg(fig1, self)
canvas1.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)
canvas2 = FigureCanvasTkAgg(fig2, self)
canvas2.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)
#Initialize label as self.
lbl = tk.Label(self, font = ('Sans Serif', 40, 'bold'),
background = 'purple',
foreground = 'White')
lbl.place(relx=0.5, rely=0.5, anchor=CENTER)
def update():
ll = GetCPM()
lbl.config(text = "CPM = {}".format(ll))
print(dt.datetime.now().strftime('%H: %M: %S.%f'))
lbl.after(20, update)
update()
# interval determines the speed at which data is recorded, 1000 = 1 second
if __name__ == '__main__':
app = App()
t1 = mp.Process(target=GetCPM)
t1.start()
t1.join()
ani_1 = animation.FuncAnimation(fig1, animate, interval = 500,
fargs=(x1, y1, ax1))
ani_2 = animation.FuncAnimation(fig2, GetValues, interval = 500,
fargs=(x2, y2, ax2))
app.mainloop()
Related
I have created a tkinter GUI that when a button is pushed a tk.toplevel is created with an interactive animated chart inside. When I run it for the first time and push the button to create the tk.toplevel with the chart, it works well, but when I close the tk.toplevel and push the button to open a new tk.toplevel, another extra chart appears. Apart from that, If I close the program and I want to run it again I have to close the terminal because it freezes.
I have tried removing plt.ion() and doing plt.show() but none of these options solve the problem. Also, I have seen the use of multiprocessing, but I don't really understand it well and I don't know why is it needed.
Why does it happen? How can I solve this problem?
Here is the code that I have used:
import tkinter as tk
import matplotlib
import matplotlib.figure as mf
import matplotlib.pyplot as plt
import matplotlib.animation as ani
from matplotlib.animation import FuncAnimation
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import numpy as np
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
container = tk.Frame(self)
container.pack()
frame = MAININTERFACE(parent=container, controller=self)
frame.grid(row=0, column=0, sticky="nsew")
def graph(self):
grafica1=GRAPHICATION(controller=self)
grafica1.Graph()
class MAININTERFACE(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.button=tk.Button(self, text='GRAPH', command=lambda: self.controller.graph())
self.button.pack(pady=20)
class GRAPHICATION(tk.Frame):
def __init__(self,controller):
tk.Frame.__init__(self)
self.controller=controller
self.x=[]
self.y=[]
def animation_frame(self, i):
if i==0.0:
self.time=0
self.energy=0
else:
self.time=self.time+1
self.energy=self.energy+1
self.x.append(self.time)
self.y.append(self.energy)
self.line.set_data(self.x,self.y)
self.ax.axis([0,10,0,10])
def Graph(self):
self.graphtoplevel=tk.Toplevel(self.controller)
self.graphtoplevel.title('Toplevel')
self.fig, self.ax = plt.subplots()
self.graph=FigureCanvasTkAgg(self.fig, self.graphtoplevel)
self.image=self.graph.get_tk_widget()
plt.ion()
self.line, = self.ax.plot(self.x,self.y)
self.image.grid(row=0, column=0, sticky='nsew')
self.animation=FuncAnimation(self.fig,func=self.animation_frame,frames=np.arange(0,11,1),interval=500, repeat=False)
self.snap_cursor = SnaptoCursor(self.ax, self.x, self.y)
self.fig.canvas.mpl_connect('button_press_event', self.snap_cursor.mouse_click)
class SnaptoCursor(object):
"""
Like Cursor but the crosshair snaps to the nearest x, y point.
For simplicity, this assumes that *x* is sorted.
"""
def __init__(self, ax, x, y):
self.ax = ax
self.lx = ax.axhline(color='k', linewidth=0.5) # the horiz line
self.ly = ax.axvline(color='k', linewidth=0.5) # the vert line
self.x = x
self.y = y
# text location in axes coords
self.txt = ax.text(0.05, 0.9, '', transform=ax.transAxes)
def mouse_click(self, event):
if not event.inaxes:
return
x, y = event.xdata, event.ydata
indx = min(np.searchsorted(self.x, x), len(self.x) - 1)
x = self.x[indx]
y = self.y[indx]
# update the line positions
self.lx.set_ydata(y)
self.ly.set_xdata(x)
self.txt.set_text('x=%1.2f, y=%1.2f' % (x, y))
self.ax.figure.canvas.draw()
if __name__ == "__main__":
app = SampleApp()
app.geometry('500x200')
app.title('MAIN GUI')
app.mainloop()
You need to make changes to your def Graph(self) function, as there are lines that are creating a window with tk.Toplevel and then the plt is creating another window.
Make your changes as follows, the capitalized comments you must do, and the others are optional for setting of titles:
def Graph(self):
#REMOVE THESE LINES
#self.graphtoplevel=tk.Toplevel(self.controller)
#self.graphtoplevel.title('Toplevel')
#modify the window title to your liking by assigning num
self.fig, self.ax = plt.subplots(num="TopLevel")
#REMOVE THESE LINES
#self.graph=FigureCanvasTkAgg(self.fig, self.graphtoplevel)
#self.image=self.graph.get_tk_widget()
plt.ion()
self.line, = self.ax.plot(self.x,self.y)
#REMOVE THIS LINE
#self.image.grid(row=0, column=0, sticky='nsew')
self.animation=FuncAnimation(self.fig,func=self.animation_frame,frames=np.arange(0,11,1),interval=500, repeat=False)
self.snap_cursor = SnaptoCursor(self.ax, self.x, self.y)
self.fig.canvas.mpl_connect('button_press_event', self.snap_cursor.mouse_click)
# displaying a title on top of figure (or do not add if not needed)
plt.title("Specific Fig Desc Title")
#ADD THIS LINE TO SHOW WINDOW
plt.show()
Please see this for more on figure window title:
Set matplotlib default figure window title
I would like to make an equivalent of the function FuncAnimation from matplotlib.animation, in which I could control the current plotted data using the scrollbar.
Say you have a data array which contains data points to be plotted at each time i. When using FuncAnimation, you first need to define a function ( here animate(i) ) which will be called for each time i = 1 to len(data[:,0]) :
def animate(i):
ax.plot(data[i,:])
anim = FuncAnimation(fig, animate, interval=100, frames=len(data[:,0]))
plt.draw()
plt.show()
but you cannot control the time i, like with a play/stop functionality. What I would like to do is to call the function animate(i), with i being the position of the scrollbar.
I found this example ( using the events from matplotlib:
https://matplotlib.org/3.2.1/users/event_handling.html )
but the mpl_connect doesn't have a "scrollbar_event".
import tkinter
from random import randint
import matplotlib as plt
import numpy as np
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
# Implement the default Matplotlib key bindings.
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure
root = tkinter.Tk()
root.wm_title("Embedding in Tk")
#create figure
fig = Figure(figsize=(5, 4), dpi=100)
ax = fig.add_axes([0,0,1,1])
ax.imshow(np.array([[0,10],[23,40]]))
#create canvas with figure
canvas = FigureCanvasTkAgg(fig, master=root) # A tk.DrawingArea.
canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1)
def on_key_press(event):
ax.clear()
ax.imshow(np.array([[randint(0,30),randint(0,30)],[randint(0,30),randint(0,30)]]))
canvas.draw_idle()
key_press_handler(event, canvas)
print("you pressed {}".format(event.key))
#connect canvas to event function
canvas.mpl_connect("key_press_event", on_key_press)
def _quit():
root.quit() # stops mainloop
root.destroy() # this is necessary on Windows to prevent
# Fatal Python Error: PyEval_RestoreThread: NULL tstate
button = tkinter.Button(master=root, text="Quit", command=_quit)
button.pack(side=tkinter.BOTTOM)
tkinter.mainloop()
Actually the scroll functionality is given by matplotlib widgets !!
The example below works fine :
import matplotlib
import tkinter as Tk
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from myPytHeader import *
matplotlib.use('TkAgg')
root = Tk.Tk()
root.wm_title("Embedding in TK")
fig = plt.Figure(figsize=(8, 6))
canvas = FigureCanvasTkAgg(fig, root)
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
nDt = nbLines("grid.dat")
nDx = nbGridPoints("grid.dat")
grid = np.zeros( (nDt,nDx) ) ; loadData("grid.dat", grid)
valu = np.zeros( (nDt,nDx) ) ; loadData("valu.dat", valu)
ax=fig.add_subplot(111)
fig.subplots_adjust(bottom=0.25)
ax_time = fig.add_axes([0.12, 0.1, 0.78, 0.03])
s_time = Slider(ax_time, 'Time', 0, nDt, valinit=0, valstep=1)
def update(val):
frame = int(s_time.val)
ax.clear()
ax.set(xlim=(-0.05, 1.05), ylim=(-0.05, 1.25))
ax.grid()
ax.scatter(grid[frame,:], valu[frame,:], color='b', marker='.')
fig.canvas.draw_idle()
s_time.on_changed(update)
Tk.mainloop()
After all these years I've found solutions to my problems here, I am in debt !!!
Here is the final solution I came with.
I hope it can be useful to someone someday somehow !!
import matplotlib
import numpy as np
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.widgets import Slider
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
# load data
nDt = 1000
nDx = 400
grd = np.zeros( (nDt,nDx) )
val = np.zeros( (nDt,nDx) )
for t in np.arange(nDt):
for x in np.arange(nDx):
grd[t,x] = x / nDx
val[t,x] = (x / nDx) * (t/nDt) * np.sin(10 * 2*np.pi * (t-x)/nDt)
matplotlib.use('TkAgg')
root = tk.Tk()
root.wm_title("Embedding in TK")
fig = plt.Figure(figsize=(8, 6))
canvas = FigureCanvasTkAgg(fig, root)
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
ax=fig.add_subplot(111)
fig.subplots_adjust(bottom=0.25)
ax.set(xlim=(-0.05, 1.05), ylim=(-1.05, 1.05))
ax.grid()
scat = ax.scatter(grd[0,:], val[0,:], color='b', marker='.')
ax_time = fig.add_axes([0.12, 0.1, 0.78, 0.03])
s_time = Slider(ax_time, 'Time', 0, nDt, valinit=0, valstep=1)
i_anim = 0
i_relative = 0
i_current = 0
def updateGraph(i):
y_i = val[i,:]
scat.set_offsets(np.c_[grd[i,:], y_i])
def updateFromAnim(i):
global i_anim
global i_current
global i_relative
i_anim = i
i_current = i + i_relative
s_time.set_val(i_current)
updateGraph(i_current)
def updateFromScroll(val):
global i_anim
global i_current
global i_relative
i_relative = int(s_time.val) - i_anim
i_current = int(s_time.val)
updateGraph(i_current)
def onClick():
global anim_running
if anim_running:
anim.event_source.stop()
anim_running = False
else:
anim.event_source.start()
anim_running = True
start_button = tk.Button(root, text="START/STOP", command=onClick)
start_button.pack()
anim_running = True
anim = FuncAnimation(fig, updateFromAnim, interval=100, frames=nDt)
s_time.on_changed(updateFromScroll)
tk.mainloop()
I would like to display two matplotlib plots below each other in one tkinter window. I have needed two different codes so far. How can I combine the codes in one? I have created the desired result with powerpoint below. I have already tried it with subplot, unfortunately without success: plt.subplots(2,1, figsize=(20,10)) and plt.subplots(1,1, figsize=(20,10))
My first code:
# ___________________________________________________________________________
# Library
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import tkinter as tk
import numpy as np
# ___________________________________________________________________________
# Carwash Array
Carwash_km = np.array([1,4,500,1500,2800,2960,2700,5,2000,2000,3100,4000,4150,4150])
Carwash_cost = np.array([5,1000,1000,2100,3000,3150,3150,20,50,600,3500,3800,3960,3700])
# Fuel Array
Fuel_km = np.array([2,5,600,2600,3900,3970,3800,6,3000,3000,4200,5000,5260,5260])
Fuel_cost = np.array([6,2000,2000,3200,4000,4260,4260,30,60,700,4600,4900,4070,4800])
# Maintenance Array
Maintenance_km = np.array([0,3,400,400,1700,1850,1600,4,1000,1000,2000,3000,3040,3040])
Maintenance_cost = np.array([4,500,500,1000,2000,2040,2040,10,40,500,2400,2700,2850,2600])
# ___________________________________________________________________________
# Main
Vis = tk.Tk()
Vis.title("Main") # titel
# ___________________________________________________________________________
# Plot
fig, ax = plt.subplots(1, 1, figsize=(20,5), facecolor = "white")
Plot_Carwash_cost = plt.bar(Carwash_km, Carwash_cost,
bottom=0,
color="#BDD7EE",
ec="black",
width=100,
label="Carwash_cost")
Plot_Carwash_cost2 = plt.bar(Maintenance_km, -Maintenance_cost,
bottom=-0,
color="#C5E0B4",
ec="black",
width=100,
label="Maintenance_cost")
Plot_Fuel = plt.scatter(Fuel_km, Fuel_cost,
marker="^",
s=150,
color="#C00000",
edgecolors="black",
zorder=3,
label="Fuel_cost")
ax.spines["bottom"].set_position("zero")
ax.spines["top"].set_color("none")
ax.spines["right"].set_color("none")
ax.spines["left"].set_color("none")
ax.tick_params(axis="x", length=20)
_, xmax = ax.get_xlim()
ymin, ymax = ax.get_ylim()
ax.set_xlim(-15, xmax)
ax.set_ylim(ymin, ymax+10) # legend
ax.text(xmax, -5, "km", ha="right", va="top", size=14)
plt.legend(ncol=5, loc="upper left")
plt.tight_layout()
# ___________________________________________________________________________
# Canvas, Toolbar
canvas = FigureCanvasTkAgg(fig, master=Vis)
canvas.draw() # TK-Drawingarea
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
canvas._tkcanvas.pack(side = tk.TOP, fill = tk.BOTH, expand = True)
toolbar = NavigationToolbar2Tk(canvas, Vis)
toolbar.update()
Vis.mainloop()
My second code:
# ___________________________________________________________________________
# Library
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import tkinter as tk
import numpy as np
# ___________________________________________________________________________
# 1 Array
Beg1 = np.array([1,4,500,1500,2800,2960,2700,5,2000,2000,3100,4000,4150,4150])
End1 = np.array([2,5,600,2600,3900,3970,3800,6,3000,3000,4200,5000,5260,5260])
# 2 Array
Beg2 = np.array([5,1000,1000,2100,3000,3150,3150,20,50,600,3500,3800,3960,3700])
End2 = np.array([6,2000,2000,3200,4000,4260,4260,30,60,700,4600,4900,4070,4800])
# 3 Array
Beg3 = np.array([0,3,400,400,1700,1850,1600,4,1000,1000,2000,3000,3040,3040])
End3 = np.array([4,500,500,1000,2000,2040,2040,10,40,500,2400,2700,2850,2600])
# ___________________________________________________________________________
# detail
Vis2 = tk.Tk()
Vis2.title("detail") # titel
# ___________________________________________________________________________
# Plot
fig, ax = plt.subplots(2, 1, figsize=(20,10), facecolor = "white")
Plot_1 = plt.barh(len(Beg1)+np.arange(len(Beg2)), End2-Beg2+500,
left=Beg2,
height=0.9,
color='red',
alpha=0.5)
Plot_2 = plt.barh(range(len(Beg1)), End1-Beg1,
left=Beg1,
height=0.9,
color='#BDD7EE')
Plot_3 = plt.barh(len(Beg1)+len(Beg2)+np.arange(len(Beg3)), End3-Beg3+500,
left=Beg3,
height=0.9,
color="#C5E0B4")
plt.tight_layout()
# ___________________________________________________________________________
# Canvas, Toolbar
canvas = FigureCanvasTkAgg(fig, master = Vis2)
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
canvas._tkcanvas.pack(side = tk.TOP, fill = tk.BOTH, expand = True)
toolbar = NavigationToolbar2Tk(canvas, Vis2)
toolbar.update()
Vis2.mainloop()
The short answer is to create two Frame widget, and have your FigureCanvasTkAgg master set to each Frame.
...
frame1 = tk.Frame(Vis)
frame2 = tk.Frame(Vis)
frame1.pack()
frame2.pack()
...
canvas = FigureCanvasTkAgg(fig, master = frame1)
...
canvas2 = FigureCanvasTkAgg(fig, master = frame2)
...
Vis.mainloop()
A better way is to create a class which inherits from Frame and handles the creation of Figure and NavigationToolBar. Depending on your need, it could be something like this:
from matplotlib.figure import Figure #use Figure instead of pyplot if you are embedding into tkinter
class GraphFrame(tk.Frame):
def __init__(self, master=None, **kwargs):
super().__init__(master, **kwargs)
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
self.canvas = FigureCanvasTkAgg(self.fig, master=self)
self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
self.toolbar = NavigationToolbar2Tk(self.canvas, self)
self.toolbar.update()
Then you can easily create new plotting areas under the same root window Vis:
Vis = tk.Tk()
graph1 = GraphFrame(Vis)
graph1.pack()
graph1.ax.bar(Carwash_km, Carwash_cost, ...)
graph1.ax.bar(Maintenance_km, -Maintenance_cost, ...)
graph1.ax.scatter(Fuel_km, Fuel_cost, ...)
graph2 = GraphFrame(Vis)
graph2.pack()
graph2.ax.barh(...)
graph2.ax.barh(...)
graph2.ax.barh(...)
Vis.mainloop()
#excuse me
#in
class GraphFrame(tk.Frame):
def __init__(self, master=None, **kwargs):
super().__init__(master, **kwargs)
self.fig = Figure()
I think the correct sintax is :
class GraphFrame(tk.Frame):
def __init__(self, master=None, **kwargs):
super().__init__(master, **kwargs)
self.fig = plt.figure()
Am I right?
Being new to python, I've come upon the matplotlib draw() freezes window problem myself and found the solution on this site:
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import random
import numpy as np
import sys
import Tkinter as tk
import time
def function1(fig, ax):
ax.cla()
color_grade_classes = ['#80FF00','#FFFF00','#FF8000', '#FF0000']
varsi = random.randint(1, 100)
for colors, rows in zip(color_grade_classes, [3,2,1,0] ):
indexs = np.arange(5)
heights = [varsi,varsi/2,varsi/3,0,0]
ax.bar(indexs, heights, zs = rows, zdir='y', color=colors, alpha=0.8)
return fig
class App():
def __init__(self):
self.root = tk.Tk()
self.root.wm_title("Embedding in TK")
self.fig = plt.figure()
self.ax = self.fig.add_subplot(111, projection='3d')
self.ax.set_xlabel('X')
self.ax.set_ylabel('Y')
self.fig = function1(self.fig, self.ax)
self.canvas = FigureCanvasTkAgg(self.fig, master=self.root)
self.toolbar = NavigationToolbar2TkAgg( self.canvas, self.root )
self.toolbar.update()
self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
self.label = tk.Label(text="")
self.label.pack()
self.update_clock()
self.root.mainloop()
def update_clock(self):
self.fig = function1(self.fig,self.ax)
self.canvas.show()
self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
now = time.strftime("%H:%M:%S")
self.label.configure(text=now)
self.root.after(1000, self.update_clock)
app=App()
My problem is incorporating the following plotting code into it. It's not quite the same as the example given. Not sure how to split this up between the function definition and the class declaration. Can anyone help me on this?
t0 = time.time()
while time.time() - t0 <= 10:
data = np.random.random((32, 32))
plt.clf()
im = plt.imshow(data,cmap=cm.gist_gray, interpolation='none')
plt.ion()
cbar = plt.colorbar(im)
cbar.update_normal(im)
cbar.set_clim(0, np.amax(data))
plt.draw()
time.sleep(0.5)
plt.show(block=True)
this seems to work.
Basically the __init__ part initialises the plot and draws the first "frame". Then the function self.update_clockis called every 1000ms, and that function calls function1() which generates new data and redraws the plot.
I moved things around a bit because of the colorbar in your example, but the idea remains the same.
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import random
import numpy as np
import sys
import Tkinter as tk
import time
class App():
def __init__(self):
self.root = tk.Tk()
self.root.wm_title("Embedding in TK")
self.fig = plt.figure()
self.ax = self.fig.add_subplot(111)
self.ax.set_xlabel('X')
self.ax.set_ylabel('Y')
data = np.random.random((32, 32))
im = self.ax.imshow(data,cmap=cm.gist_gray, interpolation='none')
self.cbar = self.fig.colorbar(im)
self.cbar.update_normal(im)
self.cbar.set_clim(0, np.amax(data))
self.fig = self.function1(self.fig, self.ax)
self.canvas = FigureCanvasTkAgg(self.fig, master=self.root)
self.toolbar = NavigationToolbar2TkAgg( self.canvas, self.root )
self.toolbar.update()
self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
self.label = tk.Label(text="")
self.label.pack()
self.update_clock()
self.root.mainloop()
def update_clock(self):
self.fig = self.function1(self.fig,self.ax)
self.canvas.show()
self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
now = time.strftime("%H:%M:%S")
self.label.configure(text=now)
self.root.after(1000, self.update_clock)
def function1(self, fig, ax):
ax.cla()
data = np.random.random((32, 32))
im = ax.imshow(data,cmap=cm.gist_gray, interpolation='none')
self.cbar.update_normal(im)
self.cbar.set_clim(0, np.amax(data))
return fig
app=App()
How can I redraw just the updated data instead of clearing the whole graph and drawing from the beginning?
(I don't want something like that https://jakevdp.github.io/blog/2012/08/18/matplotlib-animation-tutorial/ )
My code so far is something like that : (although, in this example, I used some random functions I found on Internet instead of using the real data that I am using)
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
#from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib import style
from matplotlib import pyplot as plt
from matplotlib.lines import Line2D
import numpy as np
f = plt.Figure()
class SubplotAnimation(animation.TimedAnimation):
def __init__(self):
fig= plt.figure()
ax1 = fig.add_subplot(2, 1, 1)
par = ax1.twinx()
ax2 = fig.add_subplot(2, 1, 2)
par2 = ax2.twinx()
#********************** FUNCTIONS **************************************#
dt = 1
self.t = np.arange(0, 40, dt)
self.nse1 = np.random.randn(len(self.t)) # white noise 1
self.nse2 = np.random.randn(len(self.t)) # white noise 2
self.nse3 = np.random.randn(len(self.t)) # white noise 2
self.r = np.exp(-self.t/0.05)
cnse1 = np.convolve(self.nse1, self.r, mode='same')*dt # colored noise 1
cnse2 = np.convolve(self.nse2, self.r, mode='same')*dt # colored noise 2
cnse3 = np.convolve(self.nse3, self.r, mode='same')*dt # colored noise 2
# two signals with a coherent part and a random part
self.x = 0.01*np.sin(2*np.pi*10*self.t) + cnse1
self.y = 0.01*np.sin(2*np.pi*10*self.t) + cnse2
self.k = 0.01*np.sin(2*np.pi*10*self.t) + cnse3
self.z = 10 * self.t
ax1.set_xlabel('x')
ax1.set_ylabel('y')
par.set_ylabel("y2")
###################### LINES ############################
self.line1 = Line2D([], [], color='red', linewidth=2)
self.line1a = Line2D([], [], color='black', linewidth=1)
ax1.add_line(self.line1)
par.add_line(self.line1a)
ax1.set_xlim(0, 35)
ax1.set_ylim(-10, 10)
par.set_ylim(-10, 10)
ax2.set_xlabel('x')
ax2.set_ylabel('y')
par2.set_ylabel("y2")
###################### LINES ############################
self.line2 = Line2D([], [], color='black')
self.line2a = Line2D([], [], color='red')
self.line2b = Line2D([], [], color='grey')
ax2.add_line(self.line2)
par2.add_line(self.line2a)
par2.add_line(self.line2b)
ax2.set_xlim(0, 35)
ax2.set_ylim(-0.5, 0.5)
par2.set_ylim(-0.3, 0.3)
animation.TimedAnimation.__init__(self, fig, interval=30, blit=True)
def _draw_frame(self, framedata):
i = framedata
self.line1.set_data(self.t[:i], self.x[:i])
self.line1a.set_data(self.t[:i], self.y[:i])
self.line2.set_data(self.t[:i], self.x[:i])
self.line2a.set_data(self.t[:i], self.y[:i])
self.line2b.set_data(self.t[:i], self.k[:i])
self._drawn_artists = [self.line1, self.line1a,
self.line2, self.line2a, self.line2b]
def new_frame_seq(self):
return iter(range(self.t.size))
def _init_draw(self):
lines = [self.line1, self.line1a,
self.line2, self.line2a, self.line2b]
for l in lines:
l.set_data([], [])
ani = SubplotAnimation()
plt.show()
So I kinda solved this problem but I also changed the code so... Have a look at my new one and let me know if you have any suggestions to make it look better:
(You may find things inside the code that are not used but that's because I tried to simplify my code and take out some lines etc)
import matplotlib
matplotlib.use("TkAgg") #the back-end of matplotlib
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
import Tkinter as tk
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.lines import Line2D
from matplotlib import style
ROTOR_SPEED = 100
PUMP_STATUS = False
LARGE_FONT = ("Verdana", 12)
NORM_FONT = ("Verdana", 10)
SMALL_FONT = ("Verdana", 8)
style.use("ggplot")
fig = plt.figure()
fig.set_size_inches(10, 7)
top_graph = plt.subplot2grid((2, 2), (0, 0), colspan=2)
line1 = Line2D([], [], color='black')
line2 = Line2D([], [], color='red', linewidth=2)
top_graph.add_line(line1)
top_graph.set_xlim(0, 24)
top_graph.set_ylim(-20, 20)
top_graph.set_aspect('equal', 'datalim')
line, = top_graph.plot([], [], lw=1)
line2, = top_graph.plot([], [], lw=3)
xdata, ydata = [], []
xline, yline = [], []
def data_gen():
t = data_gen.t
cnt = 0
height = 15
while cnt < 1000:
yield t, np.sin(2 * np.pi * t / 10.), 2*[t]*(height), range(-height, height)
cnt+=1
t += 0.05
data_gen.t = 0
def run(data):
# update the data
t, y, t2, rang = data
repeats = t//24
run.index = int(round(t%24.0/0.05))
if len(xdata) <= run.index:
xdata.append(t)
ydata.append(y)
xline = t2
yline = rang
else:
xdata[run.index] = t - (24 * repeats)
ydata[run.index] = y
xline = map(lambda (a, b): a-b, zip(t2, [24*repeats]*len(t2)))
yline = rang
line.set_data(xdata, ydata)
line2.set_data(xline, yline)
line2.set_color('white')
return line, line2
run.index = 1
class MainPage(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
#********** FRAMES*******#
self.frames = {} #empty..
frame = GraphPage(container, self)
self.frames[GraphPage] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(GraphPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class GraphPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
label = tk.Label(self, text="Example", font=LARGE_FONT)
label.grid(row=0, column=0, sticky='N')
canvas = FigureCanvasTkAgg(fig, self)
canvas.show()
canvas.get_tk_widget().grid(row=1, column=0, rowspan=6, columnspan=3, sticky='NSEW')
app = MainPage()
app.geometry("980x640")
ani = animation.FuncAnimation(fig, run, data_gen, blit=True, interval=20,
repeat=False)
app.mainloop()