Sliders can be a great tools for lazy engineers and modelists who want to play around with the parameters of a program without recompiling it a thousand times.

Most scientific languages ( python-pylab, matlab, scilab…) support some basic sliders methods but it can be quite long to create a GUI. In this blog I present a light, user-friendly function I implemented for my everyday work.

## Example with a numerical function

def volume(x,y,z): """ Volume of a box with width x, heigth y, and depth z """ return x*y*z intervals = [ { 'label' : 'width', 'valmin': 1 , 'valmax': 5 }, { 'label' : 'height', 'valmin': 1 , 'valmax': 5 }, { 'label' : 'depth', 'valmin': 1 , 'valmax': 5 } ] inputExplorer(volume,intervals)

The new value of volume is automatically computed each time you move the sliders. It is also computed if you press Enter (which can be useful to get several values, when your function is non-deterministic). In case you are studying a function that is long to evaluate, you can decide that it will only be evaluated when Enter is pressed, by adding “wait_for_enter = True” to the arguments list.

## Example with a graphical function

You can also decide to use a function that doesn’t return any value, but updates a plot. Let us illustrate this with the Lotka-Volterra model, in which wolves eat rabbits and die, leading to periodical fluctuations of both populations. From wikipedia:

This can be simulated easily in python with scipy’s function odeint :

import numpy as np from scipy.integrate import odeint # model def model(state,t, a,b,c,d): x,y = state return [ x*(a-b*y) , -y*(c - d*x) ] # dx/dt, dy/dt # vector of times t_vec = np.linspace(0,10,500) # 500 time points between 0 and 10 # initial conditions x0 = 0.5 y0 = 1 # parameters a = 1 b = 2 c = 1 d = 3 # simulate and return the values at each time t in t_vec result = odeint(model, [x0,y0], ts, args = (a,b,c,d) ) # plot import matplotlib.pyplot as plt plt.plot(ts,result) plt.show()

However, trying by hand several values of x0, y0, a, b, c, d, can be fastidious. So let us wrap the solving and the plotting in a function and feed it to inputExplorer. Let me rewrite the whole code from scratch :

import matplotlib.pyplot as plt import numpy as np from scipy.integrate import odeint def model(state,t, a,b,c,d): x,y = state return [ x*(a-b*y) , -y*(c - d*x) ] ts = np.linspace(0,10,500) fig,ax = plt.subplots(1) def plotDynamics(x0,y0,a,b,c,d): ax.clear() ax.plot(ts, odeint(model, [x0,y0], ts, args = (a,b,c,d)) ) fig.canvas.draw() sliders = [ { 'label' : label, 'valmin': 1 , 'valmax': 5 } for label in [ 'x0','y0','a','b','c','d' ] ] inputExplorer(plotDynamics,sliders)

## Code

Enjoy !

import matplotlib.pyplot as plt from matplotlib.widgets import Slider, Button def inputExplorer(f, sliders_properties, wait_for_validation = False): """ A light GUI to manually explore and tune the outputs of a function. slider_properties is a list of dicts (arguments for Slider ) whose keys are in ( label, valmin, valmax, valinit=0.5, valfmt='%1.2f', closedmin=True, closedmax=True, slidermin=None, slidermax=None, dragging=True) def volume(x,y,z): return x*y*z intervals = [ { 'label' : 'width', 'valmin': 1 , 'valmax': 5 }, { 'label' : 'height', 'valmin': 1 , 'valmax': 5 }, { 'label' : 'depth', 'valmin': 1 , 'valmax': 5 } ] inputExplorer(volume,intervals) """ nVars = len(sliders_properties) slider_width = 1.0/nVars print slider_width # CREATE THE CANVAS figure,ax = plt.subplots(1) figure.canvas.set_window_title( "Inputs for '%s'"%(f.func_name) ) # choose an appropriate height width,height = figure.get_size_inches() height = min(0.5*nVars,8) figure.set_size_inches(width,height,forward = True) # hide the axis ax.set_frame_on(False) ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) # CREATE THE SLIDERS sliders = [] for i, properties in enumerate(sliders_properties): ax = plt.axes([0.1 , 0.95-0.9*(i+1)*slider_width, 0.8 , 0.8* slider_width]) sliders.append( Slider(ax=ax, **properties) ) # CREATE THE CALLBACK FUNCTIONS def on_changed(event) : res = f(*(s.val for s in sliders)) if res is not None: print res def on_key_press(event): if event.key is 'enter': on_changed(event) figure.canvas.mpl_connect('key_press_event', on_key_press) # AUTOMATIC UPDATE ? if not wait_for_validation: for s in sliders : s.on_changed(on_changed) # DISPLAY THE SLIDERS plt.show()

Hello,

First… Thanks for sharing this! Quite nice as I was recently wondering how to do this very thing in IPython.

But second… I’m not able to make the code work as written. The issue seems to be with this line from inputExplorer:

for i,(name, [a,b] ) in enumerate(sets):

it returns ValueError: too many values to unpack

I’m somewhat of a beginner and wondering, How do the values in the dicts enumerated from sets get into name, a and b with this arrangement?

Rewriting sets as a list of lists (dropping the keys and keeping the values) and changing the for loop to:

for i, (name, a, b) in enumerate(sets):

the code works fine.

Cheers

Thanks Mike, I’m glad you liked it, these functions are extremely useful. They can even work interactively with matplotlib aminations (I’ll show that in my next posts).

I fixed the bug. *It is better to use a dict* to define the sliders, because you can actually put more in it than just the label, the valmin and the valmax (you can specify a valinit (initial value on the slider), the format in which to print the value of the slider, etc. , any keyword of the Slider function actually).

Note to self: NEVER edit sourcecode directly in WordPress.

Hi Valentin,

Thanks for the quick response… and bug fix!

Yes, I appreciate the advantage of dicts over lists… particularly in the context of passing keyword arguments 🙂

Looking forward to your next posts. Cheers.