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()
Related
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.
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()
I try to make a simple bar chart, where I can monitor a chemical reaction ( A -> B) using a slider for reaction steps.
So far, the following code yields a bar chart for A with a slider for reactionsteps. The print function prints the expected values for A after certain reaction steps. However, the plot won't be updated. I tried plt.draw(), plt.show() and fig.canvas.draw() but none of them worked.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.widgets import Slider, Button
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)
fig.canvas.set_window_title('Einfluss der Geschwindigkeitskonstanten')
a0 = 1
b0 = 0
plt.axis([0, 5, 0, 2])
plt.xticks([1, 4], ['A', 'B'])
plt.bar(1, a0, color = 'red')
#slider:
axcolor = 'lightblue'
axrs = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor)
srs = Slider(axrs, 'RS', 0, 20, valinit=0)
def slider_val_fkt(t):
ya = []
t = srs.val
ya = [np.exp(-0.6 * t)]
print(ya)
plt.bar(1, ya, color = 'red')
#plt.draw()
#plt.show()
fig.canvas.draw()
srs.on_changed(slider_val_fkt)
plt.show()
The new bar is drawn inside the slider axes instead of the original axes:
To overcome this you should work on the axes objects instead of using pyplot. However, since you anyways want to update the bar instead of drawing a new one, it is sufficient here to work with the bars themselves and update them using set_height.
bars = plt.bar(1, a0, color = 'red')
axrs = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor='lightblue')
srs = Slider(axrs, 'RS', 0, 20, valinit=0)
def slider_val_fkt(t):
t = srs.val
ya = [np.exp(-0.6 * t)]
bars[0].set_height(ya[0])
fig.canvas.draw_idle()
srs.on_changed(slider_val_fkt)
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()])
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()