I have a dictionary with data. For every entry I would like to display plots for 1 second and move to the next one. The plots to display are already coded in external scripts. I would like to do this automatically. So I loop through the dict, display first set of plots[0], close the plots[0], display plots[1] close plots[1] ... I would like to set up display time for let say 1 second and have the plot as full screen. The problem that during the presentation I don't want to touch the computer.
import pylab as pl
import numpy as np
x = np.arange(-np.pi, np.pi, 0.1) # only for the example purpose
myDict = {"sin":np.sin(x), "cos":np.cos(x), "exp":np.exp(x)}
for key in myDict:
print myDict[key]
pl.plt.plot(myDict[key]) # in origin coming from external function
pl.plt.plot(x) # in origin coming from external function
pl.plt.show()
Does anyone know what function should be used and how to modify above?
A simple method is to use plt.pause(1). A more sophisticated method is to usethe matplotlib.animate module. See pylab.ion() in python 2, matplotlib 1.1.1 and updating of the plot while the program runs
example, api, tutorial
import time
import pylab as pl
import numpy as np
pl.ion()
x = np.arange(-np.pi, np.pi, 0.1) # only for the example purpose
myDict = {"sin":np.sin, "cos":np.cos, "exp":np.exp}
for key in myDict:
print myDict[key]
pl.clf()
y = myDict[key](x)
pl.plt.plot(x, y, label=key)
pl.plt.draw()
time.sleep(1)
Related
So I have a function that scatter-plots some data and does so by creating new figures. The maximum amount of figures allowed at a time is 20 to avoid memory overload. If the user wants to plot a data-set with 6 variables to be exact, then there would be 30 different figures. Is there a way to wait until the user deletes the necessary amount of figures before adding more?
This is what I've though of:
import matplolib.pyplot as plt
... # some code
# this below is inside a loop structure
f = plt.figure
# add some stuff to the figure
plt.show(block=False)
Halt() # checks to see if there are too many figures
Where Halt() is defined as such:
def halt():
first = True
while plt.gcf().number > 20: # are there more than 20 figures
if first:
# show message
first = False
# time.sleep(100)
The only problem with this is that it "freezes" the program, not allowing the user to exit out of any of the figures, as it is "not responding". I've also tried the time.sleep() but that does not seem work either.
Does anyone know of a good way to loop until a condition is met?
https://matplotlib.org/api/_as_gen/matplotlib.pyplot.show.html says:
If False ensure that all windows are displayed and return immediately. In this case, you are responsible for ensuring that the event loop is running to have responsive figures.
How to do this, you ask? Well, the documentation is at https://matplotlib.org/users/interactive_guide.html#explicitly-spinning-the-event-loop .
After some fiddling around, I made the following which plots 20 figures with maximum 5 at the same time:
import matplotlib.pyplot as plt
import numpy as np
from time import sleep
def plot_stuff(exponent, titlenum):
x = np.linspace(0.0, 1.0)
f = plt.figure()
ax = f.add_subplot(1, 1, 1)
ax.set_title('{} - {}'.format(titlenum, exponent))
ax.plot(x, x**exponent)
def get_fighandles():
fignumbers = plt.get_fignums()
return [plt.figure(fign) for fign in fignumbers]
N_figs_eventually_plotted = 20
N_figs_max_simultaneous = 5
N=0
while N < N_figs_eventually_plotted:
if len(get_fighandles()) < N_figs_max_simultaneous:
N += 1
# put here whichever update is needed when you can add new figures
plot_stuff(np.random.random(), N)
plt.show(block=False)
print('hi')
for fig in get_fighandles():
print(fig.canvas)
fig.canvas.flush_events()
fig.canvas.draw_idle() # might not be needed, but here it's fast
sleep(0.1)
# note: solution terminates when the last figure is plotted, you might want something to prevent this (for instance a plt.show(block=True) when the last figure is plotted)
There might be some subtle concurrency bugs (for instance, if you close a figure after the loop reads the figure handles but before it flushes the events), but I do not see how you can avoid that for your use case.
Consider this Jupyter Python code, which uses Plotly:
import plotly.graph_objs as go
import numpy as np
from ipywidgets import widgets
from IPython.display import display
import random
mybutton = widgets.Button(description="Redraw")
xs = np.linspace(start=0, stop=10, num=100)
fig = go.FigureWidget( layout=go.Layout() )
# NB: function needs to be written in a way, that returns np.array for input np.array!
# or - use np.vectorize, to apply it element-by-element
def TestFunc(inval):
return inval+2*random.random()
fig.add_trace(go.Scatter(x=xs, y=np.vectorize(TestFunc)(xs),
mode='lines',
name='Test'))
def on_button_clicked(b):
fig.add_trace(go.Scatter(x=xs, y=np.vectorize(TestFunc)(xs),
mode='lines',
name='Test'))
mybutton.on_click(on_button_clicked)
widgets.VBox([mybutton, fig])
What I want to do, is redraw the function anew, whenever I click the button. However, since I use add_trace in the button callback, I get new traces added - I don't get the original one replaced:
So, my question is:
How do I obtain a reference to a "trace", added with add_trace, so that I could replace it? (say, fig.traces[0] = ...)
What is the best way to redraw the figure with a new retrace, with the minimal amount of object instantiation (I guess, I could do fig = go.FigureWidget( ... ) ... upon each button click, but that would have to recreate everything; I'd think, just recreating the y array, and triggering a redraw would be more "optimized")
OK, found something - still not sure if this is the way to do it, so if someone knows better, please post...
But anyways, fig.add_trace returns a reference that you can store in a variable; eventually that variable also contains the .x and .y arrays, and the .y array can be directly replaced, like so:
import plotly.graph_objs as go
import numpy as np
from ipywidgets import widgets
from IPython.display import display
import random
mybutton = widgets.Button(description="Redraw")
xs = np.linspace(start=0, stop=10, num=100)
fig = go.FigureWidget( layout=go.Layout() )
# NB: function needs to be written in a way, that returns np.array for input np.array!
# or - use np.vectorize, to apply it element-by-element
def TestFunc(inval):
return inval+2*random.random()
mytrace = fig.add_trace(go.Scatter(x=xs, y=np.vectorize(TestFunc)(xs),
mode='lines',
name='Test'))
print(repr(mytrace))
def on_button_clicked(b):
mytrace.data[0].y = np.vectorize(TestFunc)(xs)
mybutton.on_click(on_button_clicked)
widgets.VBox([mybutton, fig])
The above code works as intended - but I'm not yet sure whether it's the most optimized way to do it...
What I want to do is to update a mayavi plot in a loop. I want the updating of the plot to be done at a time specified by me (unlike, e.g., the animation decorator).
So an example piece of code I would like to get running is:
import time
import numpy as np
from mayavi import mlab
V = np.random.randn(20, 20, 20)
s = mlab.contour3d(V, contours=[0])
for i in range(5):
time.sleep(1) # Here I'll be computing a new V
V = np.random.randn(20, 20, 20)
# Update the plot with the new information
s.mlab_source.set(scalars=V)
However, this doesn't display a figure. If I include mlab.show() in the loop, then this steals the focus and doesn't allow the code to continue.
I feel what I should be using is a traits figure (e.g. this). I can follow the example traits application to run a figure which live-updates as I update the sliders. However, I can't get it to update when my code asks it to update; the focus now is 'stolen' by visualization.configure_traits().
Any pointers, or a link to appropriate documentation, would be appreciated.
EDIT
David Winchester's answer gets a step closer to the solution.
However, as I point out in the comments, I am not able to manipulate the figure with the mouse during the time.sleep() step. It is during this step that, in the full program, the computer will be busy computing the new value of V. During this time I would like to be able to manipulate the figure, rotating it with the mouse etc.
I thin Mayavi uses generators to animate data. This is working for me:
import time
import numpy as np
from mayavi import mlab
f = mlab.figure()
V = np.random.randn(20, 20, 20)
s = mlab.contour3d(V, contours=[0])
#mlab.animate(delay=10)
def anim():
i = 0
while i < 5:
time.sleep(1)
s.mlab_source.set(scalars=np.random.randn(20, 20, 20))
i += 1
yield
anim()
I used this post as reference ( Animating a mayavi points3d plot )
If you use the wx backend, you can call wx.Yield() periodically if you want to interact with your data during some long-running function. In the following example, wx.Yield() is called for every iteration of some "long running" function, animate_sleep. In this case, you could start the program with $ ipython --gui=wx <program_name.py>
import time
import numpy as np
from mayavi import mlab
import wx
V = np.random.randn(20, 20, 20)
f = mlab.figure()
s = mlab.contour3d(V, contours=[0])
def animate_sleep(x):
n_steps = int(x / 0.01)
for i in range(n_steps):
time.sleep(0.01)
wx.Yield()
for i in range(5):
animate_sleep(1)
V = np.random.randn(20, 20, 20)
# Update the plot with the new information
s.mlab_source.set(scalars=V)
I've written a simple GUI in python using pylabs and tkinter based on an example found here:
http://hardsoftlucid.wordpress.com/various-stuff/realtime-plotting/
used for sine wave generation.
Except I tweaked it to pull data through suds from a server on the internet. It's not working as I exactly anticipated as the GUI is somewhat slow. I think its due to the timer. I just started learning how to use matplotlib functions yesterday so I'm not aware of how every function works.
How can I speed it up? Right now the data comes in at 2-3 seconds which is fine, but I just want to increase the GUI responsiveness.
Here is my code:
import numpy as np
from matplotlib import pyplot as plt
plt.ion() # set plot to animated
url = "http://10.217.247.36/WSDL/v4.0/iLON100.WSDL"
client = Client(url, username='ilon', password='ilon', location = 'http://10.217.247.36/WSDL/iLON100.WSDL')
read = client.factory.create('ns0:E_xSelect')
read['xSelect'] = """//Item[starts-with(UCPTname, "Net/MB485/MAIN POWER/Fb/PowerSum")]"""
ydata = [0] * 50
ax1=plt.axes()
# make plot
line, = plt.plot(ydata)
plt.ylim([10,40])
# start data collection
while True:
x = client.service.Read(read).Item[0].UCPTvalue[0].value #data stream
x = float(x)
ymin = float(min(ydata))-10
ymax = float(max(ydata))+10
plt.ylim([ymin,ymax])
ydata.append(x)
del ydata[0]
line.set_xdata(np.arange(len(ydata)))
line.set_ydata(ydata) # update the data
plt.draw() # update the plot
I have searched numerous sites, used plots, subplots, some basic animation, and other roundabout ways, but the figure will not close despite using close(), clf(), etc.
I have something like this:
import numpy
from pylab import *
import time
fig = Figure()
counter1 = 0
counter2 = 0
while counter1<5:
counter1 = counter1+1
while counter2<10:
scatter(x_list[counter2], y_list[counter2], hold = 'on') ### x_list and y_list are just lists of random numbers
counter2 = counter2 + 1
show()
sleep(0.5)
close()
I am looking for any solution, as seen above. Plots, subplots, animation...
Two side issues to start: first, are you sure that this is the code you're actually running? sleep isn't a function in my version of pylab, so your import time doesn't seem to match your call, it should be time.sleep(0.5).. Second, I don't understand your loops at all. It looks like you're plotting the same thing 5 times, because counter1 has no effect and you add each point to the scatterplot before you pause. Are you trying to plot x_list/y_list point by point?
If you use draw() instead of show() I think it should work; the show() is what's holding the close(). Is the following something like what you want?
import time
from pylab import *
ion()
# test data
x = arange(0, 10, 0.5)
y = 10*x+exp(x)*abs(cos(x))
for j in range(len(x)):
if j > 0: scatter(x[:j], y[:j])
# assuming we don't want the limits to change
xlim(0, 10)
ylim(0, 1000)
draw()
time.sleep(2)
#close()
Note that I've commented out the close() because this way it produces a nice animation. If you leave it in, it'll keep closing and reopening the window, which could be what you want, but doesn't look very useful to my eyes. YMMV, of course.