问题
I am using 2 sliders to adjust the colorbar of a 2D heat map; one for the bottom (minimum) and one for the top (maximum). I want to make sure that the two cannot overlap i.e. if the full range is 0 till 5 and I set the maximum on 2 then the minimum cannot surpass 2. This should happen interactively. How can I achieve this? Additionally is there a maybe a way to integrate the two sliders into one? Thank you.
An example of my GUI. And the relevant part of the code:
def update(val, s=None):
"""Retreives the value from the sliders and updates the graph accordingly"""
_cmin = s_cmin.val
_cmax = s_cmax.val
pcm.set_clim([_cmin, _cmax])
plt.draw()
def reset(event):
"""Resets the sliders when the reset button is pressed"""
s_cmin.reset()
s_cmax.reset()
fig, ax = plt.subplots(figsize=(13,8))
plt.subplots_adjust(left=0.25,bottom=0.25)
# define axis minima and maxima:
x_min = Xi.min()
x_max = Xi.max()
y_min = Yi.min()
y_max = Yi.max()
c_min = Zi.min()
c_max = Zi.max()
pcm = ax.pcolormesh(Xi,Yi,Zi)
cb = plt.colorbar(pcm)
axcolor = 'lightgoldenrodyellow'
axx = plt.xlim([x_min, x_max])
ayy = plt.ylim([y_min, y_max])
# create a space in the figure to place the two sliders:
ax_cmin = plt.axes([0.15, 0.10, 0.65, 0.02], facecolor=axcolor)
ax_cmax = plt.axes([0.15, 0.15, 0.65, 0.02], facecolor=axcolor)
# the first argument is the rectangle, with values in percentage of the figure
# size: [left, bottom, width, height]
# create each slider on its corresponding place:
s_cmax = Slider(ax_cmax, 'max', c_min, c_max, valinit=c_max, valfmt='%1.4f')
s_cmin = Slider(ax_cmin, 'min', c_min, c_max, valinit=c_min, valfmt='%1.4f')
# set both sliders to call update when their value is changed:
s_cmin.on_changed(update)
s_cmax.on_changed(update)
# create a space in the figure to place the reset button
resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
# create the reset button
button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975')
button.on_clicked(reset)
# create a space in the figure to place the textboxes:
axbox_xmin = plt.axes([0.07, 0.55, 0.04, 0.04])
axbox_xmax = plt.axes([0.12, 0.55, 0.04, 0.04])
axbox_ymin = plt.axes([0.07, 0.49, 0.04, 0.04])
axbox_ymax = plt.axes([0.12, 0.49, 0.04, 0.04])
# create the textboxes
tb_xmin = TextBox(axbox_xmin,'x', color=axcolor, hovercolor='0.975', label_pad=0.01)
tb_xmax = TextBox(axbox_xmax,'', color=axcolor, hovercolor='0.975')
tb_ymin = TextBox(axbox_ymin,'y', color=axcolor, hovercolor='0.975', label_pad=0.01)
tb_ymax = TextBox(axbox_ymax,'', color=axcolor, hovercolor='0.975')
# create the submit action
tb_xmin.on_submit(submit)
tb_xmax.on_submit(submit)
tb_ymin.on_submit(submit)
tb_ymax.on_submit(submit)
plt.show()
回答1:
In some cases it may indeed be desired to have one single Slider which can set some minimum and maximum simultaneously. So instead of having one single value, the slider could have two values and the rectangle inside the slider would be bounded by the two values, instead of starting at the minimum value of the slider.
The following would be solution to such a case. It uses a MinMaxSlider, i.e. a subclass of the Slider which is adapted to host two values.
Instead of a single input value, it would expect two values,
MinMaxSlider(... , valinit=0.5,valinit2=0.8)
such that the Sliderbar ranges from 0.5 to 0.8. Clicking on the slider would change the value which is closer to the click, making dragging rather easy.
In order to use this slider be aware that the function that the callback via on_changed now naturally has two arguments.
import six
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
class MinMaxSlider(Slider):
def __init__(self,ax, label, valmin, valmax, **kwargs):
self.valinit2 = kwargs.pop("valinit2", valmax)
self.val2 = self.valinit2
Slider.__init__(self,ax, label, valmin, valmax, **kwargs)
self.poly.xy = np.array([[self.valinit,0],[self.valinit,1],
[self.valinit2,1],[self.valinit2,0]])
self.vline.set_visible(False)
def set_val(self, val):
if np.abs(val-self.val) < np.abs(val-self.val2):
self.val = val
else:
self.val2 = val
self.poly.xy = np.array([[self.val,0],[self.val,1],
[self.val2,1],[self.val2,0]])
self.valtext.set_text(self.valfmt % self.val +"\n"+self.valfmt % self.val2)
if self.drawon:
self.ax.figure.canvas.draw_idle()
if not self.eventson:
return
for cid, func in six.iteritems(self.observers):
func(self.val,self.val2)
import numpy as np
x = np.linspace(0,16,1001)
f = lambda x: np.sin(x)*np.sin(1.7*x+2)*np.sin(0.7*x+0.05)*x
fig,(ax, sliderax) = plt.subplots(nrows=2,gridspec_kw={"height_ratios":[1,0.05]})
fig.subplots_adjust(hspace=0.3)
ax.plot(x,f(x))
slider = MinMaxSlider(sliderax,"slider",x.min(),x.max(),
valinit=x.min(),valinit2=x.max())
def update(mini,maxi):
ax.set_xlim(mini,maxi)
slider.on_changed(update)
update(x.min(),x.max())
plt.show()
来源:https://stackoverflow.com/questions/47353014/prevent-data-overlap-between-two-sliders-use-a-single-slider-for-two-quantitie