How to format slider - matplotlib

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')

Related

How to use Radio Buttons to control a pie chart matplotlib?

I have a little issue in my code and I hope someone will have an idea to help me. So, I have created a pie chart and, with RadioButtons, I would like to 'control' the 'explode parameter'. Expected result : we have a pie chart, we have Radiobutton and when we click on a radio button, in the pie chart the 'serie' that matches with the Radiobutton clicked is pulled out (thanks to 'explode') and we can do that as long as we want.
My problem : I have created the Radio Buttons, and when they are clicked, it opens a new window with the expected result but I would like to have the pie chart and the Radiobuttons in the same window.
My code just below :
import matplotlib.pyplot as plt
from matplotlib.widgets import RadioButtons
rax = plt.axes([0.05, 0.7, 0.15, 0.15])
radio = RadioButtons(rax, ('0-4 ans', '5-10 ans', '11-13 ans', '14-17 ans'))
def explode_function(label):
labels = '0-4 ans', '5-10 ans', '11-13 ans', '14-17 ans'
sizes = [17.4, 25.7, 18.6, 38.3]
explode = (0, 0, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs')
fig1, ax = plt.subplots()
if label == '0-4 ans':
explode = (0.15, 0, 0, 0)
elif label == '5-10 ans':
explode = (0, 0.15, 0, 0)
elif label == '11-13 ans':
explode = (0, 0, 0.15, 0)
elif label == '14-17 ans':
explode = (0, 0, 0, 0.15)
ax.pie (sizes, explode=explode, labels=labels, autopct='%1.1f%%',
shadow=True, colors=['lightblue', 'lightgreen', 'salmon', 'lightgray'])
plt.show()
radio.on_clicked(explode_function)
plt.show()```
Thanks a lot, and good evening
By creating the fig and ax inside the explode_function, a new plot will be generated each time a radio button is clicked. Creating the fig and ax beforehand, allows to constantly work with the same plot. As an initialization, explode_function(labels[0]) already shows one variant of the pie chart. fig.canvas.redraw() forces the updated chart to be drawn.
import matplotlib.pyplot as plt
from matplotlib.widgets import RadioButtons
fig = plt.figure()
ax = plt.subplot(1, 1, 1)
rax = plt.axes([0.05, 0.7, 0.15, 0.15])
labels = '0-4 ans', '5-10 ans', '11-13 ans', '14-17 ans'
radio = RadioButtons(rax, labels)
def explode_function(label):
ax.cla()
sizes = [17.4, 25.7, 18.6, 38.3]
explode = [0.15 if label == label_i else 0 for label_i in labels]
ax.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%',
shadow=True, colors=['lightblue', 'lightgreen', 'salmon', 'lightgray'])
fig.canvas.draw()
explode_function(labels[0])
radio.on_clicked(explode_function)
plt.show()

Is it allowed to draw a matplotlib.patches.Rectangle on a blank figure with matplotlib?

This code adds an rectangle on the Lenna image.
imgurl = 'https://upload.wikimedia.org/wikipedia/en/thumb/7/7d/Lenna_%28test_image%29.png/330px-Lenna_%28test_image%29.png'
f = urllib.request.urlopen(imgurl)
img = plt.imread(f)
fig,ax = plt.subplots(1)
ax.imshow(img)
rect = patches.Rectangle((50, 50), 50, 30, linewidth=1, edgecolor='b', facecolor='w')
ax.add_patch(rect)
plt.show()
while this code cannot do the job on a blank figure
fig,ax = plt.subplots(1)
rect = patches.Rectangle((50, 50), 50, 30, linewidth=1, edgecolor='b', facecolor='r')
ax.add_patch(rect)
plt.show()
why is that?
Running your second part of code I get:
Please notice the x and y axis scale. They each go up to 1.0, but your rectangle patch has coordinates of (50,50). Let's expand the axis limits:
fig,ax = plt.subplots(1)
rect = mpl.patches.Rectangle((50,50), 50, 30, linewidth=1, edgecolor='b', facecolor='r')
ax.add_patch(rect)
ax.set_xlim(left = 0, right = 150)
ax.set_ylim(bottom = 0, top = 150)
plt.show()
And you get:
As you can see, it is plotting the rectangle onto an empty axis, it's just that you couldn't see it the first time around, due to rectangle being out of the view of the axis.

Matplotlib - Setting a tick label's background colour

I have a subplot and its tick labels overlap with the data. I would like to set the x-tick labels to have a background colour (e.g. white). Currently I have only been able to find how to change the label's colour, but not the background. I know how to get the effect using a text object as shown below. (NB - I don't want the whole subplot's margin to be coloured, but just the tick label).
MWE
import matplotlib as mpl
rc_fonts = {
"text.usetex": True,
'text.latex.preview': True,
"font.size": 50,
'mathtext.default': 'regular',
'axes.titlesize': 55,
"axes.labelsize": 55,
"legend.fontsize": 50,
"xtick.labelsize": 50,
"ytick.labelsize": 50,
'figure.titlesize': 55,
'figure.figsize': (10, 6.5), # 15, 9.3
'text.latex.preamble': [
r"""\usepackage{lmodern,amsmath,amssymb,bm,physics,mathtools,nicefrac,letltxmacro,fixcmex}
"""],
"font.family": "serif",
"font.serif": "computer modern roman",
}
mpl.rcParams.update(rc_fonts)
import matplotlib.pylab as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes, InsetPosition, mark_inset
from numpy import linspace, sin
x = linspace(0, 1, 100)
plt.clf()
ax1 = plt.gca()
ax2 = plt.axes([0, 0, 1, 1], label=str(2))
ip = InsetPosition(ax1, [0.08, 0.63, 0.45, 0.3])
ax2.set_axes_locator(ip)
ax1.plot(x, x)
ax1.plot(x, x + 0.3)
ax1.set_xlim(0, 1)
ax1.set_ylim(0, 1)
ax2.xaxis.set_tick_params(labelcolor='r')
ax1.text(0.3, 0.3, '$1$', transform=ax1.transAxes, horizontalalignment='center', verticalalignment='center', color='black', backgroundcolor='white')
To set a label's background color you may use the same property as for a text, essentially because a label is a text.
plt.setp(ax2.get_xticklabels(), backgroundcolor="limegreen")
For more sophisticated backgrounds, you could also use the bbox property.
bbox = dict(boxstyle="round", ec="limegreen", fc="limegreen", alpha=0.5)
plt.setp(ax2.get_xticklabels(), bbox=bbox)
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
ax.plot(np.linspace(0, 1, 5), np.random.rand(5))
# set xticklabels
xtl = []
for x in ax.get_xticks():
xtl += ['lbl: {:.1f}'.format(x)]
ax.set_xticklabels(xtl)
# modify labels
for tl in ax.get_xticklabels():
txt = tl.get_text()
if txt == 'lbl: 1.0':
txt += ' (!)'
tl.set_backgroundcolor('C3')
tl.set_text(txt)

How to set the updated value of Slider always in the centre?

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()

How to make dependent sliders in matplotlib

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.