matplotlib slider redraw does not update figtext - python

I used sliders in matplotlib to update a few graphs based on GUI input.
All my graphs update well.
But when I use figtext, I have the problem that the updated text will write over the existing one.
import numpy as np
import pylab as p
from matplotlib.widgets import Slider
p.subplot(111)
x = np.arange(0,500,1)
f = np.sin(x/100.0)
l11, = p.plot(f)
ax = p.axes([0.25, 0.05, 0.7, 0.03], axisbg='lightgoldenrodyellow')
slider1 = Slider(ax, 'amplitude', -1.0, 1.5, valinit=0)
def update(val):
f = slider1.val * np.sin(x/100.0)
l11.set_ydata(f)
np.set_printoptions(precision=2)
p.figtext(0.5, 0.65, str(slider1.val) )
p.draw()
slider1.on_changed(update)
p.show()

Every time you call p.figtext(0.5, 0.65, str(slider1.val)) you are creating a new Text object which is being written on top of the previous ones. What you should do is save a reference to the first Text object and update its contents by calling its set_text() method. I have updated your code with a working example.
import numpy as np
import pylab as p
from matplotlib.widgets import Slider
p.subplot(111)
x = np.arange(0,500,1)
f = np.sin(x/100.0)
l11, = p.plot(f)
ax = p.axes([0.25, 0.05, 0.7, 0.03], axisbg='lightgoldenrodyellow')
slider1 = Slider(ax, 'amplitude', -1.0, 1.5, valinit=0)
# the text on the figure
fig_text = p.figtext(0.5, 0.65, str(slider1.val))
def update(val):
f = slider1.val*np.sin(x/100.0)
l11.set_ydata(f)
np.set_printoptions(precision=2)
# update the value of the Text object
fig_text.set_text(str(slider1.val))
p.draw()
slider1.on_changed(update)
p.show()

Related

Struggling to get widgets working in python

I don't know why but I am really struggling to get widgets working well in python. I try to look at examples about how to use them but I don't know how to extrapolate that to get it to work with my code. I am trying to get a figure to display widgets such that the type, frequency, phase, and other variables adjust the graph itself.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.widgets as widgets
from scipy import signal
from matplotlib.widgets import RadioButtons
A = 1
ang_f = 5
t = np.linspace(0, 4*np.pi, 1000)
phase = 0
s0 = A*np.sin(ang_f*t + phase)
s2 = A*signal.sawtooth(ang_f*t + phase)
s1 = A*signal.square(ang_f*t + phase)
fig, ax = plt.subplots()
l, = ax.plot(t, s1, lw=2, color='red')
plt.subplots_adjust(left=0.4)
def sinf(x, omega):
return np.sin(omega*x)
def sliderCallback(val):
# """ 'val' is the current value selected by the slider
# Recalculate sine values with val as the frequency """
axesHandle.set_ydata(sinf(x, val))
plt.draw() # Redraw the axes
def clickcallback(val):
# 'val' is the current value selected by the slider
# Recalculate sine values with val as the frequency
axesHandle.set_ydata(sinf(x, val))
plt.draw() # Redraw the axes
def closeCallback(event):
plt.close('all') # Close all open figure windows
fig = plt.figure(figsize=(7, 5))
ax = plt.axes([0.1, 0.2, 0.6, 0.7])
axesHandle, = plt.plot(x, sinf(x, 1), lw=2, color='red')
# Add axis to contain the slider
fax = plt.axes([0.1, 0.04, 0.35, 0.03]) # Frequency
tax = plt.axes([0.1, 0.12, 0.35, 0.03]) # Time
sax_3 = plt.axes([0.60, 0.1, 0.35, 0.03]) # Number of points
pax = plt.axes([0.60, 0.05, 0.35, 0.03]) # Phase
rax = plt.axes([0.85, 0.65, 0.12, 0.15]) # Type
bax = plt.axes([0.85, 0.85, 0.1, 0.1]) # Close
pointshandle = widgets.Slider(sax_3, 'Number of points', 1, 200,
valfmt='%0.0f')
pointshandle.on_changed(sliderCallback)
graphchoice = widgets.RadioButtons(rax, ('Sine', 'Squarewave', 'Sawtooth'))
graphchoice.on_clicked(clickcallback)
freqhandle = widgets.Slider(fax, 'Frequancy (Hz)', 0, 5, valinit=1)
freqhandle.on_changed(sliderCallback)
phasehandle = widgets.Slider(pax, 'Phase', 0, 0*np.pi, valinit=0)
phasehandle.on_changed(sliderCallback)
timehandle = widgets.Slider(tax, 'Time (s)', 1, 10, valinit=1)
timehandle.on_changed(sliderCallback)
buttonHandle = widgets.Button(bax, 'Close')
buttonHandle.on_clicked(closeCallback)
def hzfunc(label):
hzdict = {'Sine': s0, 'Squarewave': s1, 'Sawtooth': s2}
ydata = hzdict[label]
l.set_ydata(ydata)
plt.draw()
graphchoice.on_clicked(hzfunc)
I'm really lost so any tips to put me on the right path would be much appreciated, im just so confused atm.

How do I fix my slider? It isn't working as intended

I'm trying to create a slider that will set the bins in matplotlib, here is my code:
%matplotlib notebook
import matplotlib.pyplot as plt
from matplotlib.pyplot import ion
import numpy as np
import matplotlib.animation as animation
from matplotlib.widgets import RadioButtons
from matplotlib.widgets import Slider
# generate 4 random variables from the random, gamma, exponential, and uniform distributions
sample_size = 10000
normal = np.random.normal(loc=0.0, scale=1.0, size=sample_size)
gamma = np.random.gamma(shape = 1.0, scale=1.0, size=sample_size)
uniform = np.random.uniform(low=0.0, high=10.0, size=sample_size)
exponential = np.random.exponential(scale=1.0, size=sample_size)
fig, sub_plt = plt.subplots()
plt.subplots_adjust(top=0.65) # Adjust subplot to not overlap with radio box
axcolor = 'lightgoldenrodyellow'
rax = plt.axes([0.05, 0.7, 0.25, 0.25], facecolor=axcolor)
axfreq = plt.axes([0.20, 0.02, 0.65, 0.03], facecolor=axcolor)
radio = RadioButtons(rax, ('Normal', 'Gamma', 'Uniform', 'Exponential'))
slide = Slider(axfreq, 'Bins', 10.0,200.0,valinit=30.0)
def dist_func(type_l):
sub_plt.clear() # comment this line if you want to keep previous drawings
dist_dict = {'Normal':normal, 'Gamma':gamma, 'Uniform':uniform, 'Exponential':exponential}
data_type = dist_dict[type_l]
sub_plt.hist(data_type, bins=100)
radio.on_clicked(dist_func)
def bin_func(val):
slide_val = slide.val
plt.figure()
sub_plt.hist(data_type,bins=slide_val)
fig.canvas.draw_idle()
slide.on_changed(bin_func)
plt.show()
I want the value of the slider to set the bins of the histogram. This renders the slider but the slider does not set the bins as intended, in fact it doesn't do anything. Is there any way to make the bins work as intended?
I believe sub_plt.hist(data_type,bins=slide_val) is the problem, data_type isn't a global variable so you can't create a plot with an undefined variable.
I moved the canvas redrawing code to inside the dist_func so that clicking one of the radio buttons redraws the plot without having to move the slider.
It is also important to ensure the slider value is an integer (must have a discrete number of bins!)
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.widgets import RadioButtons
from matplotlib.widgets import Slider
# generate 4 random variables from the random, gamma, exponential, and uniform distributions
sample_size = 10000
normal = np.random.normal(loc=0.0, scale=1.0, size=sample_size)
gamma = np.random.gamma(shape=1.0, scale=1.0, size=sample_size)
uniform = np.random.uniform(low=0.0, high=10.0, size=sample_size)
exponential = np.random.exponential(scale=1.0, size=sample_size)
fig, sub_plt = plt.subplots()
plt.subplots_adjust(top=0.65) # Adjust subplot to not overlap with radio box
axcolor = 'lightgoldenrodyellow'
rax = plt.axes([0.05, 0.7, 0.25, 0.25], facecolor=axcolor)
axfreq = plt.axes([0.20, 0.02, 0.65, 0.03], facecolor=axcolor)
radio = RadioButtons(rax, ('Normal', 'Gamma', 'Uniform', 'Exponential'))
slide = Slider(axfreq, 'Bins', 10.0, 200.0, valinit=30.0, valstep=1)
dist_dict = {'Normal': normal, 'Gamma': gamma, 'Uniform': uniform, 'Exponential': exponential}
def dist_func(type_l, bins=100):
sub_plt.clear() # comment this line if you want to keep previous drawings
data_type = dist_dict[type_l]
sub_plt.hist(data_type, bins=bins)
fig.canvas.draw_idle()
radio.on_clicked(dist_func)
def update(a):
dist_func(radio.value_selected, bins=int(a))
# the final step is to specify that the slider needs to
# execute the above function when its value changes
slide.on_changed(update)
dist_func('Normal', bins=100)
plt.show()

Matplotlib could not generate an accurate slider plot

I have the following equation:
y = ((b-6(x**k))/c)**(1/k)
k = 10/(6+c)
I know that when k > 1 then y is concave and when 0 < k < 1 then y is convex. However, the problem is that in the generated plot it does not matter whatever the value of k is, it always generates a concave y. I was wondering if anybody can help me to figure out what is the problem.
Codes to generate the dynamic plot:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button, RadioButtons
fig, ax = plt.subplots()
plt.subplots_adjust(left=0.25, bottom=0.25)
x = np.arange(0.0, 1.0, 0.001)
b_init = 1
c_init = 0
k = 10/(6+c_init)
delta_f = 1.0
y = ((b_init-6*(x**k))/c_init)**(1/k)
l, = plt.plot(x, y, lw=2)
ax.margins(x=0)
axcolor = 'lightgoldenrodyellow'
ax_b = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor)
ax_c = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=axcolor)
s_b = Slider(ax_b, 'b', 0.1, 18.0, valinit=b_init, valstep=delta_f)
s_c = Slider(ax_c, 'c', 0.1, 12.0, valinit=c_init)
def update(val):
b = s_b.val
c = s_c.val
l.set_ydata(((b-6*(x**k))/c)**(1/k))
fig.canvas.draw_idle()
s_b.on_changed(update)
s_c.on_changed(update)
resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975')
def reset(event):
s_b.reset()
s_c.reset()
button.on_clicked(reset)
def colorfunc(label):
l.set_color(label)
fig.canvas.draw_idle()
plt.show()
In case you are working with juypter notebooks you can use the widgets from ipywidgets as shown below.
Also, to get an intuition, it might help if you print out the b,c and k values.
%matplotlib inline
import numpy as np
import matplotlib.pyplot as p
from ipywidgets import *
def y(x,b,c):
k = 10/(6+c)
print(f' b={b:.3f},c={c:.3f},k={k:.3f}')
y = ((b-6*(x**k))/c)**(1/k)
return y
def inter(b0,c0):
y1=y(x,b0,c0)
p.figure(figsize=(20,6))
p.plot(x,y1)
dx=0.001
x = np.arange(0, 1.0+dx, dx) # assuming you want to go to 1 inclusively
b0=widgets.FloatSlider(value=10,min=-1,max=18.0,step=0.01,
description='b0',
continuous_update=False,
readout_format='.3f',
layout=Layout(width='90%', height='20px'))
c0=widgets.FloatSlider(value=0.1,min=-1,max=12.0,step=0.01,
description='c0',
continuous_update=False,
readout_format='.3f',
layout=Layout(width='90%', height='20px'))
interact(inter, b0=b0,c0=c0);

Updating contour plot levels with matplotlib slider

I'm trying to change the values of the colour levels on a matplotlib filled contour plot using a slider. i.e contourf(x,y,z,np.linspace(a,b,n)) where the sliders would control a and b and would change the plot colour levels when a slider is moved.
The following code takes column formatted data converts it to the form required by contourf and then the the sliders are implemented.
This is what I've tried:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
data=np.genfromtxt('file.dat',skip_header=1)
len=np.sqrt(data[:,0].size)
x=np.reshape(data[:,0],(len,len))
y=np.reshape(data[:,1],(len,len))
z=np.reshape(data[:,3],(len,len))
l=plt.contourf(x,y,z,np.linspace(0,100,255))
axmax = plt.axes([0.25, 0.1, 0.65, 0.03]) #slider location and size
axmin = plt.axes([0.25, 0.15, 0.65, 0.03])
smax = Slider(axmax, 'Max',0, 100, 50) #slider properties
smin = Slider(axmin, 'Min', 0, 100, 0)
def update(val):
l.levels(np.linspace(smin.val,smax.val,255))#changing levels of plot
fig.canvas.draw_idle() #line that throws error
smax.on_changed(update)
smin.on_changed(update)
plt.show()
A large number of matplotlib errors are thrown when a slider is moved with the relevant one being 'TypeError:'numpy.ndarray' object is not callable' which is thrown by the line
fig.canvas.draw_idle()
The problem is that l.levels is a array, so you would have to change the values in this array. In my testing changing these values does not cause the plot to update. So another solution is to just clear the axis and redraw the plot.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
data=np.random.random([25,4])
data = data*100
len=np.sqrt(data[:,0].size)
x=np.reshape(data[:,0],(len,len))
y=np.reshape(data[:,1],(len,len))
z=np.reshape(data[:,3],(len,len))
l=plt.contourf(x,y,z,np.linspace(0,100,255))
contour_axis = plt.gca()
axmax = plt.axes([0.25, 0.1, 0.65, 0.03]) #slider location and size
axmin = plt.axes([0.25, 0.15, 0.65, 0.03])
smax = Slider(axmax, 'Max',0, 100, 50) #slider properties
smin = Slider(axmin, 'Min', 0, 100, 0)
def update(val):
contour_axis.clear()
contour_axis.contourf(x,y,z,np.linspace(smin.val,smax.val,255))
plt.draw()
smax.on_changed(update)
smin.on_changed(update)
plt.show()

How to update the color bar in an interactive plot

The following code plots a 2D array, whose value can be changed interactively. The only thing missing is that I would like it to change the colobar. Somehow, when I modify the image, the colorbar doesn't adjust itself.
from pyplot import *
from numpy import *
from matplotlib.widgets import Slider, Button, RadioButtons
ax = subplot(111)
subplots_adjust(left=0.25, bottom=0.25)
my_img=random((100,100))*10.
l = imshow(my_img,origin='lower');cbar=colorbar()
ax1= axes([0.25, 0.1, 0.65, 0.03], axisbg='w')
par1 = Slider(ax1, 'normalization', 1, 100., valinit=1.5)
def update(val):
new_normalization = par1.val
new_img=random((100,100))*new_normalization
l.set_array(new_img)
# this doesn't change the new colobar maximum
cbar.vmax=new_img.max()
cbar.vmin=0
draw()
par1.on_changed(update)
resetax = axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Reset', color='w', hovercolor='0.975')
def reset(event):
par1.reset()
button.on_clicked(reset)
show()
I found out the solution myself.
All it takes is just
l.set_clim([new_img.min(), new_img.max()])

Categories