Following the Slider Demo of Matplotlib https://matplotlib.org/gallery/widgets/slider_demo.html, I would like to update the Slider ranges, so that every time I change the slider values, those are re-centred in the Slider.
I have tried to define the Sliders as
sfreq = Slider(axfreq, 'Freq', freq-10, freq+10, valinit=freq)
samp = Slider(axamp, 'Amp', amp-5, amp+5, valinit=amp)
but since the update() function does not return anything, that does not work. I also tried making these variables global inside the function, which also did not work. I finally tried defining the Sliders inside the update function,
def update(val):
amp = samp.val
freq = sfreq.val
l.set_ydata(amp*np.sin(2*np.pi*freq*t))
fig.canvas.draw_idle()
Slider(axfreq, 'Freq', freq-10, freq+10, valinit=freq)
Slider(axamp, 'Amp', amp-5, amp+5, valinit=amp)
but that overlays more and more Sliders as I change the values. Any suggestions?
So I just decided to make the range of the slider cover several orders of magnitude of the parameter, and display the values in a logarithmic scale. In case anyone wonders, and following the matplotlib demo:
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)
t = np.arange(0.0, 1.0, 0.001)
a0 = 5
f0 = 10
delta_f = 5.0
s = a0*np.sin(2*np.pi*f0*t)
l, = plt.plot(t, s, lw=2, color='red')
plt.axis([0, 1, -10, 10])
axcolor = 'lightgoldenrodyellow'
axfreq = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor)
axamp = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=axcolor)
sfreq = Slider(axfreq, 'Freq', np.log(1), np.log10(1000), valinit=np.log10(f0), valfmt='%4.2E')
samp = Slider(axamp, 'Amp', a0-5, a0+5, valinit=a0)
def update(val):
amp = samp.val
freq = sfreq.val
sfreq.valtext.set_text('{:4.2E}'.format(10**freq))
l.set_ydata(amp*np.sin(2*np.pi*10**freq*t))
fig.canvas.draw_idle()
sfreq.on_changed(update)
samp.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):
sfreq.reset()
samp.reset()
button.on_clicked(reset)
rax = plt.axes([0.025, 0.5, 0.15, 0.15], facecolor=axcolor)
radio = RadioButtons(rax, ('red', 'blue', 'green'), active=0)
def colorfunc(label):
l.set_color(label)
fig.canvas.draw_idle()
radio.on_clicked(colorfunc)
plt.show()
Related
With this code i'm creating colorbar scales with the function make_colormap. Source:Create own colormap using matplotlib and plot color scale
import matplotlib.colors as mcolors
def make_colormap(seq):
"""Return a LinearSegmentedColormap
seq: a sequence of floats and RGB-tuples. The floats should be increasing
and in the interval (0,1).
"""
seq = [(None,) * 3, 0.0] + list(seq) + [1.0, (None,) * 3]
cdict = {'red': [], 'green': [], 'blue': []}
for i, item in enumerate(seq):
if isinstance(item, float):
r1, g1, b1 = seq[i - 1]
r2, g2, b2 = seq[i + 1]
cdict['red'].append([item, r1, r2])
cdict['green'].append([item, g1, g2])
cdict['blue'].append([item, b1, b2])
return mcolors.LinearSegmentedColormap('CustomMap', cdict)
c = mcolors.ColorConverter().to_rgb
rvb = make_colormap([c('grey'), c('grey'), norm(3), c('sandybrown'), c('sandybrown'),
norm(5), c('yellow'), c('yellow'), norm(10), c('navajowhite'),
c('navajowhite'), norm(15),c('lightgreen'), c('lightgreen'),norm(20),c('lime'), c('lime'),
norm(50),c('limegreen'), c('limegreen'),norm(80),c('forestgreen'), c('forestgreen'),norm(120),
c('green'), c('green'),norm(160),c('darkgreen'), c('darkgreen'),norm(200),c('teal'), c('teal'),norm(300),
c('mediumaquamarine'), c('mediumaquamarine'),norm(500),c('lightseagreen'), c('lightseagreen'),norm(700),
c('lightskyblue'), c('lightskyblue')])
So in variable rvb i'm asssing a color to ranges of values. How can i assing a color to an specific ranges of values? For example: Grey to 0-3, sandybrown to 4-5, yellow to 6-10, etc.
The map is this:
Also i want to the legend show those values assigned. For example Grey color 0-3, sandybrown 4-5, etc.
Something similar to this image (no need to be equal to the image, just need to show ranges with colors):
I also will show you part of my code when i create the map:
fig = plt.figure('map', figsize=(7,7), dpi=200)
ax = fig.add_axes([0.1, 0.12, 0.80, 0.75], projection=ccrs.PlateCarree())
plt.title('xxx')
plt.xlabel('LONGITUD')
plt.ylabel('LATITUD')
ax.outline_patch.set_linewidth(0.3)
l = NaturalEarthFeature(category='cultural', name='admin_0_countries', scale='50m', facecolor='none')
ax.add_feature(l, edgecolor='black', linewidth=0.25)
img = ax.scatter(lons, lats, s=7, c=ppvalues, cmap=rvb,norm=norm,
marker='o', transform=ccrs.PlateCarree())
handles, labels = img.legend_elements(alpha=0.2)
plt.legend(handles, labels,prop={'weight':'bold','size':10}, title='Meteorological\nStations',title_fontsize=9, scatterpoints=2);
cb = plt.colorbar(img, extend='both',
spacing='proportional', orientation='horizontal',
cax=fig.add_axes([0.12, 0.12, 0.76, 0.02]))
ax.set_extent([-90.0, -60.0, -20.0, 0.0], crs=ccrs.PlateCarree())
I don't understand the function in the question, but I have coded how to create a legend with a specified color, specified label, and specified ticks, and how to give a color bar a specified tick. Please correct the addition of colors and the tick spacing in the color bar.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.colors import LinearSegmentedColormap
list_color = ['grey','sandybrown','sandybrown','yellow',
'navajowhite','lightgreen','lime','limegreen',
'forestgreen','green','darkgreen','teal',
'mediumaquamarine','lightseagreen','lightskyblue']
list_label = ['0-3', '4-5', '6-10', '11-15',
'16-20', '21-50', '51-80', '81-120',
'121-160', '161-200','201-300','301-500',
'501-700','701-900','901-1200']
list_ticks = np.linspace(0, 1, 15)
vmin,vmax = 0, 1
cm = LinearSegmentedColormap.from_list('custom_cmap', list_color, N=len(list_color))
plt.imshow(np.linspace(0, 1, 25).reshape(5,5), cmap=cm, interpolation='nearest', vmin=vmin, vmax=vmax)
cbar = plt.colorbar( orientation='horizontal', extend='neither', ticks=list_ticks)
cbar.ax.set_xticklabels(list_label, rotation=45, fontsize=14)
all_patches = []
for h,l in zip(list_color, list_label):
patch = mpatches.Patch(color=h, label=l)
all_patches.append(patch)
plt.legend(handles=all_patches, loc='upper right', ncol=3, bbox_to_anchor=(3, 1))
plt.show()
Is it possible to have a subplot taller than other subplots in order to make space for the X axis tick labels, but the height of the bar chart inside to be the same as the bar height in the shorter subplots? When I add height parameter to df.plot() I get "TypeError: () got multiple values for keyword argument 'height'". Here is my code:
from collections import OrderedDict
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
data = OrderedDict()
data['Test Break'] = [0.1, 0.5, np.nan]
data['No Break'] = [0.9, 0.5, np.nan]
data['Not Tested'] = [0.0, 0.0, 1.0000]
index = ['Very very long name ' + str(x+1) for x in range(len(data))]
df = pd.DataFrame(data=data, index=index)
num_plots = 2
rows = num_plots + 1
cols = 1
layout = (rows, cols)
red, green, grey = '#FF0000', '#00FF00', '#888888'
light_grey = '#AAAAAA'
fig = plt.figure()
fig.set_size_inches(6, 3)
for z in range(num_plots):
is_last = z == num_plots - 1
rowspan = 2 if is_last else 1
ax = plt.subplot2grid(layout, (z, 0), rowspan=rowspan)
df.plot(ax=ax, kind='bar', stacked=True, yticks=[0,1], legend=False, color=[red, green, grey])
plt.subplots_adjust(left=0.05, right=0.95, bottom=0.02, top=0.98, hspace=0.5)
ax.grid(True, which='major', axis='y')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_edgecolor(light_grey)
ax.spines['left'].set_edgecolor(light_grey)
if not is_last:
for tick in ax.xaxis.get_major_ticks():
tick.set_visible(False)
Similar to Matplotlib dependent sliders, I want to make two sliders whose sum make 10. To do that, i want that when i move one slider, the other one moves to compensate. At the moment, the code is the following :
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)
t = np.arange(0.0, 1.0, 0.001)
a0 = 5
f0 = 5
delta_f = 5.0
s = a0*np.sin(2*np.pi*f0*t)
l, = plt.plot(t, s, lw=2, color='red')
plt.axis([0, 1, -10, 10])
axcolor = 'lightgoldenrodyellow'
axfreq = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor)
axamp = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=axcolor)
sfreq = Slider(axfreq, 'Freq', 0.1, 10.0, valinit=f0)
samp = Slider(axamp, 'Amp', 0.1, 10.0, valinit=a0)
def update_sfreq(val):
samp.val = 10-sfreq.val
l.set_ydata(samp.val*np.sin(2*np.pi*sfreq.val*t))
fig.canvas.draw_idle()
sfreq.on_changed(update_sfreq)
resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975')
def reset(event):
sfreq.reset()
samp.reset()
button.on_clicked(reset)
rax = plt.axes([0.025, 0.5, 0.15, 0.15], facecolor=axcolor)
radio = RadioButtons(rax, ('red', 'blue', 'green'), active=0)
def colorfunc(label):
l.set_color(label)
fig.canvas.draw_idle()
radio.on_clicked(colorfunc)
plt.show()
This is one of matplotlib examples that I modified to suit my needs. At the moment, I only implemented s_freq.on_changed(). I want that when I move the freq slider, the graph changes (This part is working), and at the same time, the amp slider moves too (This part is not).
Any thoughts on how to modify my function update_sfreq to correctly update samp?
Note : I do realize that if both my sliders update each other, I might end up in an infinite loop. I have already thought of this and of a solution. The part that is not working is really the part where moving one slider makes the other slider move.
Well, after some digging in matplotlib source code, I managed to find an answer to my question. You need to change
samp.val = ...
to
samp.set_val(...)
This will update the bar correctly.
I am trying to get matplotlib to create a dynamic 3d graph based on user input - but I can't get the graph to update. If I use the exact same code but without the "projection='3d'" setting, the program works correctly - but as soon as the graph is changed to display in 3d - it doesn't work.
Any help would be greatly appreciated.
3D Graph Code (graph doesn't update)
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
plt.subplots_adjust(left=0.25, bottom=0.25)
x = np.arange(0.0, 1.0, 0.1)
a0 = 5
b0 = 1
y = a0 * x + b0
z = np.zeros(10)
l, = plt.plot(x, y, z)
# Set size of Axes
plt.axis([0, 1, -10, 10])
# Place Sliders on Graph
ax_a = plt.axes([0.25, 0.1, 0.65, 0.03])
ax_b = plt.axes([0.25, 0.15, 0.65, 0.03])
# Create Sliders & Determine Range
sa = Slider(ax_a, 'a', 0, 10.0, valinit=a0)
sb = Slider(ax_b, 'b', 0, 10.0, valinit=b0)
def update(val):
a = sa.val
b = sb.val
l.set_ydata(a*x+b)
fig.canvas.draw_idle()
sa.on_changed(update)
sb.on_changed(update)
plt.show()
2D Graph Code (graph updates properly)
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111)
plt.subplots_adjust(left=0.25, bottom=0.25)
x = np.arange(0.0, 1.0, 0.1)
a0 = 5
b0 = 1
y = a0 * x + b0
l, = plt.plot(x, y)
# Set size of Axes
plt.axis([0, 1, -10, 10])
# Place Sliders on Graph
ax_a = plt.axes([0.25, 0.1, 0.65, 0.03])
ax_b = plt.axes([0.25, 0.15, 0.65, 0.03])
# Create Sliders & Determine Range
sa = Slider(ax_a, 'a', 0, 10.0, valinit=a0)
sb = Slider(ax_b, 'b', 0, 10.0, valinit=b0)
def update(val):
a = sa.val
b = sb.val
l.set_ydata(a*x+b)
fig.canvas.draw_idle()
sa.on_changed(update)
sb.on_changed(update)
plt.show()
The line in the 3D case needs to be updated in all 3 dimensions (even the data in some dimension stays the same). In order to do so, you have to set the 2D data using set_data and the third dimension using set_3d_properties. So updating y would look like this:
l.set_data(x, a*x+b)
l.set_3d_properties(z)
I have a slider:
time_ax = fig.add_axes([0.1, 0.05, 0.8, 0.03])
var_time = Slider(time_ax, 'Time', 0, 100, valinit=10, valfmt='%0.0f')
var_time.on_changed(update)
and I want to customize the appearance of this slider:
I can add axisbg parameter to add_axes function, which will change default white background to assigned color, but that's all I see possible for now.
So, how to change other slider components:
silder border (default: black)
default value indicator (default: red)
slider progress (default: blue)
The slider border is just the spines of the Axes instance. The progress bar can be directly accessed for basic customization in the constructor, and the initial status indicator is an attribute of the slider. I was able to change all of those things:
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
fig = plt.figure()
time_ax = fig.add_axes([0.1, 0.05, 0.8, 0.03])
# Facecolor and edgecolor control the slider itself
var_time = Slider(time_ax, 'Time', 0, 100, valinit=10, valfmt='%0.0f',
facecolor='c', edgecolor='r')
# The vline attribute controls the initial value line
var_time.vline.set_color('blue')
# The spines of the axis control the borders
time_ax.spines['left'].set_color('magenta')
time_ax.spines['right'].set_color('magenta')
time_ax.spines['bottom'].set_color('magenta')
time_ax.spines['top'].set_color('magenta')
The color of the box you can change when you define the axis of the "ax" box:
fig, ax = plt.subplots()
plt.subplots_adjust(left=0.25, bottom=0.25)
t = np.arange(0.0, 1.0, 0.001)
a0 = 5
f0 = 3
s = a0*np.sin(2*np.pi*f0*t)
l, = plt.plot(t,s, lw=2, color='red')
plt.axis([0, 1, -10, 10])
axcolor = 'lightgoldenrodyellow'
axfreq = plt.axes([0.03, 0.25, 0.03, 0.65], axisbg=axcolor)
axamp = plt.axes([0.08, 0.25, 0.03, 0.65], axisbg=axcolor)
sfreq = Slider(axfreq, 'Freq', 0.1, 30.0, valinit=f0)
samp = Slider(axamp, 'Amp', 0.1, 10.0, valinit=a0)
# The vline attribute controls the initial value line
samp.vline.set_color('green')