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