My God, It’s Full of Stars (6/7) — Adding Sliders to an Interactive Matplotlib Interface
So far, we have learned some of the astrophysics of star clusters and white dwarfs, gotten Python to talk to our legacy C code using ctypes, parsed C header files to get the values of macros and use them as variables in Python, plotted a color-magnitude diagram (CMD), and added buttons to our matplotlib interface to plot different filters, models, and types of stars.
Our next task is to create sliders so that we can vary certain parameters continuously over a range.
To do this, we first need to import two different types of sliders from matplotlib.widgets:
from matplotlib.widgets import Slider, RangeSlider
As with our buttons, in order to place a slider on the graph, we need to create an axes object to contain it:
slider_width = (1.8 * buttonwidth)
slider_height = 0.04
param_ax = fig.add_axes([figwidth + (2 * buttonwidth) + (8 * pad),
.95 - (slider_height),
slider_width,
slider_height])
Then we can add the Slider object:
nStars_slider = Slider(
ax=param_ax,
label="N",
valmin=1,
valmax=maxStars,
valinit=params.nSystems[0],
valstep = 1
)
We give the slider a label, as well as minimum, maximum, and initial values. The “valstep” argument confines our values to integers for this particular slider.
Next, similar to a button, we have to connect the slider to a function that tells the code what to do when the slider is moved:
nStars_slider.on_changed(change_nStars)
Finally, we create the function:
def change_nStars(val):
params.nSystems[0] = int(nStars_slider.val)
reDrawSim()
deleteScatter()
createScatter()
plotScatter()
params.updateFileStem()
This particular slider changes the number of stars in our cluster. Our function updates the number of stars, then re-scatters the photometry for that new number of stars, and replots both results, leaving field stars unchanged.
We add sliders for the number of field stars, the percentage of stars that are binaries, the age, metallicity, distance, and reddening of the cluster, the exposure times for each individual band of photometry (which changes the amount that the stars are scattered, as well as their error bars), and the limiting signal-to-noise that we will accept for a main-sequence star’s photometry.
Our last slider will be a RangeSlider. This type of slider allows us to specify two numbers at once (i.e. a range). It is defined in largely the same way as a standard slider:
# Bright and Faint Limits
brightLimit_ax = fig.add_axes([figwidth + (2 * buttonwidth) + (8 * pad),
.95 - ((18) * slider_height),
slider_width,
slider_height])
brightLimit_slider = RangeSlider(
ax=brightLimit_ax,
label="limits",
valmin=0,
valmax=30,
valinit=(params.brightLimit[0],params.brightLimit[1]),
valfmt = "%.1f"
)
brightLimit_slider.on_changed(change_brightLimit)
The function that is called by the slider is:
def change_brightLimit(new_val):
for line in brightLines:
line.remove()
params.brightLimit[0] = new_val[0]
params.brightLimit[1] = new_val[1]
plotBrightLimits()
reDrawScatter()
The slider passes a list of two values to the function, which we use to change the brightest and faintest main-sequence stars we will use in our scattering:
And that’s it for our sliders! In principle, very easy. In practice, there’s a lot more going on under the hood. You can check out the complete code for more details.
In our final installment, we will add a text box and a couple more buttons so that we can save our data to a file once we have it exactly how we want it.