Real-time plot matplotlib inside Tkinter - python

Let's say I have a function called trajectory that plots the position of a few objects in a 2D plot.
import matplotlib.pyplot as plt
def trajectory(listOfObjects, time):
#Stuff happens here
plt.scatter(x,y)
I iterate this function so it plots new points in the same graph, effectively plotting a full trajectory over enough iterations. Something like this:
while True:
time += 1
trajectory(listOfObjects, time)
plt.pause(0.001)
Now I want this program to run embedded in a Tkinter window. How can I achieve this? I don't know how to run this loop indefinitely while also using tkinter's mainloop()

Related

interactive matplotlib figure: show and then close an image on top of a graph

I am creating an interactive matplotlib figure. It is interactive in the sense that when I press a letter 'i' on the keyboard, an image is loaded into the figure. In a second step I would like to remove the image again, while I am still showing the plot. I really don't want to redraw the plot, as it takes too much time.
I am using plt.imshow(img) to display the image. So far I have not come across an equivalent that closes the image. I can only close the complete figure. Does anyone know of such a function?
PLT is tricky. In general, plt.COMMANDS apply to the most recently created object and don't offer much control over the figure, axis, plots, etc. If you label your global plt variables, it makes it more clear.
import matplotlib.pyplot as plt
X = [1,2,3,4]
Y = [1,1,3,3.5]
figure = plt.figure() #Creates the window.
axis = figure.add_subplot(1,1,1) #Creates a graphic inside the window.
axis.grid(True) #Change the axis.
plots = axis.plot(X,Y) #Put a plot in the axis.
figure.show() #Open the window.
Note, that plots is a list, since arrays, X and Y, could have generated many plots. Now, lets delete the plot while the window is open and watch it disappear, then insert the plot back into the axis.
plots[0].remove()
plots = axis.plot(X,Y)
In your case, you are working with axis.imshow() instead of axis.plot().

Is there a way to plot something iteratively so that the new plot overwrites the previous plot?

What I want to do is to dynamically change a plot so that I can see it update as Python is executing its code. Here is what I've come up with:
import matplotlib.pyplot as plt
import time
def plotResult(x,y):
plt.plot(x,y)
plt.figure()
for i in range(5):
x = [2,3,5*i]
y = [1,2,3]
plotResult(x,y)
time.sleep(1)
What I want is for each call of "plotResult" to erase the previous plot with the new plot in its place. What I end up with instead is each plot on top of each other. I'm using time.sleep here because I want some time to look at the newly plotted result before it gets erased and replaced with a new plot. I guess I'm essentially trying to create an animation here with each frame being a call to plotResult.
I'm going to do this for a code with a much longer execution time, so I don't want to have to wait until the code is done being executed to view the animation. Please let me know if you know of a way to do this.
Read about the FuncAnimation class. It repeatedly calls a function to update each frame of the animation.

Efficient way to update matplotlib figure in gui?

My application is receiving data over the network at about 30fps, and needs to update a horizontal bar chart dynamically based on this new data.
I am using a matplotlib figure inside a tkinter window for this purpose. Profiling my code has shown that a major bottleneck in my code is the updating of this figure.
A simplified version of the code is given below:
def update_bars(self):
"""
Updates a horizontal bar chart
"""
for bar, new_d in zip(self.bars, self.latest_data):
bar.set_width(new_d)
self.figure.draw()
The lag I am experiencing is significant, and grows quickly over time. Is there a more efficient way to update the matplotlib figure? Any help would be great.
EDIT: I will be looking at this for possible speedup tips. I'll update if I get something working.
You can update the data of the plot objects. But to some extent, you can't change the shape of the plot, you can manually reset the x and y axis limits.
e.g.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 6*np.pi, 100)
y = np.sin(x)
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
line1, = ax.plot(x, y)
for phase in np.linspace(0, 10*np.pi, 500):
line1.set_ydata(np.sin(x + phase))
# render the figure
# re-draw itself the next time
# some GUI backends add this to the GUI frameworks event loop.
fig.canvas.draw()
fig.canvas.flush_events() # flush the GUI events
flush_events
Flush the GUI events for the figure. Implemented only for backends
with GUIs.
flush_events make sure that the GUI framework has a chance to run its event loop and clear any GUI events.Sometimes this needs to be in a try/except block because the default implementation of this method is to raise NotImplementedError.
draw will render the figure,in the above code,maybe remove draw still work.But to some extent they're different.

Python matplotlib: how to plot realtime over plotted graph

Here I have plotted a line chart with two lists
import random as random
import matplotlib.pyplot as plt
lis1=random.sample(range(1, 100), 10)
lis2 = random.sample(range(1, 100), 10)
plt.plot(range(0,len(lis1), 1), lis1)
plt.plot(range(0,len(lis2), 1), lis2)
plt.show()
Now, I'm getting a third list from Arduino in realtime. My question is how to plot that third list/line over this plot without redrawing entire chart.
EDIT: Third list is something like this
import time
lis3 =[]
for i in range(10):
lis3.append(i)
time.sleep(1)
plt.show() will display the current chart that you're working on whereas plt.draw() will re-draw the figure. This essentially allows you to change the graph as your data changes
The plt.draw docs state:
This is used in interactive mode to update a figure that has been altered using one or more plot object method calls; it is not needed if figure modification is done entirely with pyplot functions, if a sequence of modifications ends with a pyplot function, or if matplotlib is in non-interactive mode and the sequence of modifications ends with show() or savefig().
have a look at the following post: When to use cla(), clf() or close() for clearing a plot in matplotlib?
i think you can clear the figure by using plt.clear() in an timer event. Re-drawing can be done by using the plt.draw() function. Because of the realtime data you have to have a function which is called after a certain delay. There i would call this plt.clear() or plt.draw() function.
Afterwards you have to re fill the lists or make a new list to draw the third line.
I don't know a better solution and maybe that's not what you want, because it's some keind of re-drawing but i hope that this is useful for you!
Also have a look at:
Dynamically updating plot in matplotlib
How to update a plot in matplotlib?

Plot in python that updates on keyboard hit

I've been struggling to achieve something that is trivial in Octave: to produce a series of plots, that change when I hit a key. Here's my octave code sample.
x = [1:10];
for c=1:3
plot(x,c.*x);
hold off;
input('a');
end
When I try to do the same in python, I realized that python matplotlib has the save function, which puts it in non-blocking mode, and so I have to close the figure using a mouse, for the next figure to be produced. And the next figure is at a random other location on the screen. How can I get python to imitate the above behavior? I've tried various combinations of ion(), ioff(), plt.show(), plt.draw(), but haven't succeeded.
You can do something fancier if you want, using mpl_connect
First import pylab
from pylab import *
Then define an updateData function that can be connected to the figure canvas.
i = 1
def updateData(event):
global i,x
i +=1
y = i*x
data.set_data(x,y)
ylim(y[0],y[-1])
draw()
i and x are global variables in this case. (This could be treated in better way, this is just an example!)
Then, create you plot and connect with your defined function.
f = figure()
data, = plot(x,x)
i=1
f.canvas.mpl_connect("key_press_event",updateData)
show()
Whenever you hit any key in the keyboard (When the figure window is selected) the function updateData is called, i is incremented and the plot updated.
Have fun!

Categories