Tkinter Not Showing Animated Matplotlib - python-3.x

I want to show this matplotlib real-time graph in tkinter GUI
Real-time Graph
import tkinter as tk
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import style
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
root= tk.Tk()
style.use('fivethirtyeight')
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
def animate(i):
graph = open('data.txt','r').read()
lines = graph.split('\n')
xs = []
ys = []
zs = []
for line in lines:
if len(line) > 1:
x, y, z = line.split(',')
xs.append(float(x))
ys.append(float(y))
zs.append(float(z))
ax1.clear()
ax1.plot(xs, ys, zs)
anim = animation.FuncAnimation(fig, animate, interval=1000)
app = (fig, root)
root.mainloop()
I tried the code above, but the GUI isn't show anything. What can I do to show that real-time graph?

Sorry, my bad. I updated my code in this line:
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
With this line:
fig = plt.figure(figsize=(5,4), dpi=100)
ax1 = fig.add_subplot(111)
line2 = FigureCanvasTkAgg(fig, root)
line2.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH)
I got my answer now.

Related

How to connect matplotlib cursor mouse_move object with slider value?

I have a figure where 2 axhlines move with mouse movement. I want to put a slider at the bottom where it will change the range of y-axis values covered by these axhlines.
enter image description here
I tried the following code. Problem is that the value of the slider changes but the mouse event object does not update.
Thanks
%matplotlib notebook
import pandas as pd
import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from matplotlib.widgets import Slider
np.random.seed(12345)
df = pd.DataFrame([np.random.normal(32000,200000,3650),
np.random.normal(43000,100000,3650),
np.random.normal(43500,140000,3650),
np.random.normal(48000,70000,3650)],
index=[1992,1993,1994,1995])
df=df.T
data=df.describe().T
data['error']=df.sem()
data['error_range']=df.sem()*1.96
fig, ax = plt.subplots()
def plot_bar(x,y,error,title,alpha_level=0.7):
ax.bar(x,y, yerr=error,
align='center', alpha=alpha_level,
error_kw=dict(ecolor='black', elinewidth=1, capsize=5, capthick=1))
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.set_title(title)
ax.xaxis.set_major_locator(ticker.FixedLocator(data.index))
ax.yaxis.set_major_formatter(ticker.StrMethodFormatter('{x:,.0f}'))
ax.set_ylim([-5000,55000])
ax.set_xlim([1990.5,1995.5])
ax.spines['bottom'].set_position(('data',0))
ax.spines['left'].set_position(('data',1991.4))
plt.tight_layout()
return (ax, ax.get_children()[1:5])
ax, barlist=plot_bar(x=data.index,y=data['mean'],error=data['error_range'],title='Even Harder Option', alpha_level=0.6)
fig.subplots_adjust(bottom=0.1)
axcolor = 'lightgoldenrodyellow'
range_slider = plt.axes([0.2, 0.05, 0.65, 0.03], facecolor=axcolor)
slider = Slider(range_slider, 'Range', 0, 55000, valinit=10000, valstep=100)
def update(val):
slider.val = slider.val
slider.on_changed(update)
class Cursor(object):
_df=None
_bl=None
def __init__(self, ax,data_F, bars, slider):
#global slider
self._df=data_F
self._bl=bars
self.ax = ax
self.lx1 = ax.axhline(color='b')
self.lx2 = ax.axhline(color='b')
self.text1 = ax.text(1990.55, y, '%d' %45,bbox=dict(fc='white',ec='k'), fontsize='x-small')
self.text2 = ax.text(1990.55, y, '%d' %45,bbox=dict(fc='white',ec='k'), fontsize='x-small')
self._sl = slider.val
def mouse_move(self, event):
if not event.inaxes:
return
x, y = event.xdata, event.ydata
r = self._sl
y1 , y2 = y+r/2 , y-r/2
#self.lx1.set_ydata(y)
self.lx1.set_ydata(y+r/2)
self.lx2.set_ydata(y-r/2)
for i in range(4):
#shade = cmap(norm((data['mean'.values[i]-event.ydata)/df_std.values[i]))
prob1=stats.norm.cdf(y1,self._df['mean'].values[i],self._df['error'].values[i])
prob2=stats.norm.cdf(y2,self._df['mean'].values[i],self._df['error'].values[i])
shade = cmap(prob1-prob2)
self._bl[i].set_color(shade)
self.text1.set_text('%d' %y1)
self.text1.set_position((1990.55, y1))
self.text2.set_text('%d' %y2)
self.text2.set_position((1990.55, y2))
plt.draw()
cursor = Cursor(ax, data,barlist, slider)
#plt.connect('range_change', cursor.update)
plt.connect('motion_notify_event', cursor.mouse_move)

Matplotlib: animation saved with trace

In the following Python code, I am trying to make an animation of a 2 rotating vectors around the origin. I am using matplotlib 3.2.1 and Python 3.8.2 on Ubuntu 20.04.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
r = 2.0
def circle(phi):
return np.array([r*np.cos(phi), r*np.sin(phi)])
fig, ax = plt.subplots(figsize=(10,6))
ax.axis([-3.5*r,3.5*r,-2.5*r,2.5*r])
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
# set equal aspect
ax.set_aspect("equal")
point, = ax.plot(0, r, marker="o")
traj = plt.Circle((0,0), r, fill=False, color='black')
ax.add_artist(traj) # draw the circular trajectory
def update(phi):
x, y = circle(phi)
point.set_data([x], [y])
er_vec = np.array([0.5*x, 0.5*y])
eθ_vec = np.array([-0.5*y, 0.5*x])
er_arr = plt.arrow(x, y, dx=er_vec[0], dy=er_vec[1], head_width=0.1, head_length=0.2, color='gray')
eθ_arr = plt.arrow(x, y, dx=eθ_vec[0], dy=eθ_vec[1], head_width=0.1, head_length=0.2, color='grey')
annot_er = plt.text(1.7*x, 1.7*y, r'$\mathbf{e}_r$', fontsize=11)
annot_eθ = plt.text(1.1*(x-0.5*y), 1.1*(y+0.5*x), r'$\mathbf{e}_\theta$', fontsize=11)
ax.add_artist(er_arr)
ax.add_artist(eθ_arr)
ax.add_artist(annot_er)
ax.add_artist(annot_eθ)
return point, er_arr, eθ_arr, annot_er, annot_eθ
anim = FuncAnimation(fig, update, interval=10, blit=True, repeat=False, frames=np.linspace(0, 2.0*np.pi, 360, endpoint=False))
plt.show()
The code above runs smoothly and without any issues.
This is a screenshot of the animation:
However, when I try to save the animation to an mp4 video:
anim.save('anim-issue.mp4', writer='ffmpeg')
the animation in the video appears with traces which, something like this screenshot:
Could someone help me fix that issue with the video animation?
I appreciate your help.
Edit 1: According to this answer this is due to blit=True. But that doesn't solve the issue here, since the arrows have no set_position method.
Edit 2: I found another related question with the same issue I described above but I don't know how to adapt my code to make it work as expected in both cases (plt.show, anim.save).
Updating the position of an arrow would be quite tricky.
The easiest solution is indeed to create new arrows at each frame, but you have to make sure you remove the previous arrows first
full code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
r = 2.0
def circle(phi):
return np.array([r*np.cos(phi), r*np.sin(phi)])
fig, ax = plt.subplots(figsize=(10,6))
ax.axis([-3.5*r,3.5*r,-2.5*r,2.5*r])
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
# set equal aspect
ax.set_aspect("equal")
point, = ax.plot(0, r, marker="o")
traj = plt.Circle((0,0), r, fill=False, color='black')
ax.add_artist(traj) # draw the circular trajectory
er_arr = None
eθ_arr = None
annot_er = None
annot_eθ = None
def init():
global er_arr, eθ_arr, annot_er, annot_eθ
x,y = 0,0
er_vec = np.array([0.5*x, 0.5*y])
eθ_vec = np.array([-0.5*y, 0.5*x])
er_arr = plt.arrow(x, y, dx=er_vec[0], dy=er_vec[1], head_width=0.1, head_length=0.2, color='gray')
eθ_arr = plt.arrow(x, y, dx=eθ_vec[0], dy=eθ_vec[1], head_width=0.1, head_length=0.2, color='grey')
annot_er = plt.text(1.7*x, 1.7*y, r'$\mathbf{e}_r$', fontsize=11)
annot_eθ = plt.text(1.1*(x-0.5*y), 1.1*(y+0.5*x), r'$\mathbf{e}_\theta$', fontsize=11)
return er_arr, eθ_arr, annot_er, annot_eθ
def update(phi):
global er_arr, eθ_arr, annot_er, annot_eθ
x, y = circle(phi)
point.set_data([x], [y])
er_vec = np.array([0.5*x, 0.5*y])
eθ_vec = np.array([-0.5*y, 0.5*x])
#remove previous artists
er_arr.remove()
eθ_arr.remove()
er_arr = plt.arrow(x, y, dx=er_vec[0], dy=er_vec[1], head_width=0.1, head_length=0.2, color='gray')
eθ_arr = plt.arrow(x, y, dx=eθ_vec[0], dy=eθ_vec[1], head_width=0.1, head_length=0.2, color='grey')
annot_er.set_position((1.7*x, 1.7*y))
annot_eθ.set_position((1.1*(x-0.5*y), 1.1*(y+0.5*x)))
return point, er_arr, eθ_arr, annot_er, annot_eθ
anim = FuncAnimation(fig, update, init_func=init, interval=10, blit=True, repeat=False, frames=np.linspace(0, 2.0*np.pi, 360, endpoint=False))
plt.show()

Matplotlib Navigation Toolbar in wxPython Panel

I am working on a GUI (developed with wxPython) where you can plot graphs on different panels. At the moment I have this:
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wx import NavigationToolbar2Wx
from matplotlib import pyplot as plt
import numpy as np
import wx
class Frame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None,-1,'Plot',size=(1000,800))
# Main Panel (It will contain other elements besides the plotting panel)
self.mainPanel = wx.Panel(self,-1,size=(1000,800))
self.mainPanel.SetBackgroundColour('gray')
# Plotting panel
self.plottingPanel = wx.Panel(self,-1,pos=(50,20),size=(500,400))
self.plottingPanel.SetBackgroundColour('white')
# Plot example
figure = plt.figure()
axes = figure.add_subplot(111)
t = np.arange(0.0, 3.0, 0.01); s = np.cos(2 * np.pi * t);axes.plot(t,s)
plt.title('Cosine')
plt.xlabel('x');plt.ylabel('y')
# Canvas
canvas = FigureCanvas(self.plottingPanel,-1,figure)
# Navegation toolbar
navToolbar = NavigationToolbar2Wx(canvas)
navToolbar.DeleteToolByPos(6);navToolbar.DeleteToolByPos(2);navToolbar.DeleteToolByPos(1)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(canvas)
sizer.Add(navToolbar)
class App(wx.App):
def OnInit(self):
self.Frame = Frame()
self.Frame.Show()
self.SetTopWindow(self.Frame)
return True
def main():
app = App()
app.MainLoop()
if __name__ == '__main__':
main()
When I run the script get this:
I have colored the plotting panel white to highlight it. How can the plot size be adapted to the panel size?
I want to get something like this (this is a montage):
On the other hand, I managed to eliminate from the bar some buttons that are unnecessary for what I need but the bar does not work, that is, when pressing the buttons nothing happens :(
Thanks for your help
It is possible to set parameters when the container for the plot elements (matplotlib.figure.Figure) is created.
e.g. figsize sets the figure dimension in inches and tight_layout adjust the sub plots in tight layout.
figure = plt.figure(figsize = (4, 3), tight_layout=True)
Alternatively you can set the position of the matplotlib.axes.Axes object by .set_position:
figure = plt.figure()
axes = figure.add_subplot(111)
axes.set_position(pos = [0.15,0.3,0.55,0.55], which='both')

Add a point to a plot with a mouse event

I want to click on my graph and add a point to it. I am using event handling, but the append command is not working.
My Code:
from matplotlib import pyplot as plt
class LineBuilder:
def __init__(self, line):
self.line = line
self.xs = line.get_xdata()
self.ys = line.get_ydata()
self.cid = line.figure.canvas.mpl_connect('button_press_event', self.click)
def click(self, event):
if event.inaxes != self.line.axes:
return
self.xs.append(event.xdata)
self.ys.append(event.ydata)
self.line.set_data(self.xs, self.ys)
self.line.figure.canvas.draw()
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_title('click to add a point')
line, = ax.plot([], [], 'o') # empty point
linebuilder = LineBuilder(line)
plt.show()
My Error:
numpy.ndarray' object has no attribute 'append

How to add label and title to matplotlib animation

I am not able to add label and title to matplotlib animation.
Please find my code below
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import style
import time
%matplotlib inline
style.use("ggplot")
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
def animate(i):
pullData = open("twitter-out.txt","r").read()
lines = pullData.split('\n')
xar = []
yar = []
x = 0
y = 0
for l in lines[-200:]:
x += 1
if "pos" in l:
y += 1
elif "neg" in l:
y -= 1
xar.append(x)
yar.append(y)
ax1.clear()
ax1.plot(xar,yar)
ani = animation.FuncAnimation(fig, animate, interval=1000)
plt.show()

Resources