Introduction

paramBokeh is a small library that represents Parameters graphically in a notebook and on bokeh server. Parameters are Python attributes extended using the Param library to support types, ranges, and documentation, which turns out to be just the information you need to automatically create widgets for each parameter. paramBokeh currently uses bokeh to display the widgets, but the design of Param and paramBokeh allows your code to be completely independent of the underlying widgets library, and can also be used with other widget frameworks such as paramNB, which uses ipywidgets.

Parameters and widgets

To use paramBokeh, first declare some Parameterized classes with various Parameters:

In [1]:
import param
import datetime as dt

class BaseClass(param.Parameterized):
    x                       = param.Parameter(default=3.14,doc="X position")
    y                       = param.Parameter(default="Not editable",constant=True)
    string_value            = param.String(default="str",doc="A string")
    num_int                 = param.Integer(50000,bounds=(-200,100000))
    unbounded_int           = param.Integer(23)
    float_with_hard_bounds  = param.Number(8.2,bounds=(7.5,10))
    float_with_soft_bounds  = param.Number(0.5,bounds=(0,None),softbounds=(0,2))
    unbounded_float         = param.Number(30.01,precedence=0)
    hidden_parameter        = param.Number(2.718,precedence=-1)
    integer_range           = param.Range(default=(3,7),bounds=(0, 10))
    float_range             = param.Range(default=(0,1.57),bounds=(0, 3.145))
    dictionary              = param.Dict(default={"a":2, "b":9})
    
class Example(BaseClass):
    """An example Parameterized class"""
    timestamps = []

    boolean                 = param.Boolean(True, doc="A sample Boolean parameter")
    color                   = param.Color(default='#FFFFFF')
    date                    = param.Date(dt.datetime(2017, 1, 1),
                                         bounds=(dt.datetime(2017, 1, 1), dt.datetime(2017, 2, 1)))
    select_string           = param.ObjectSelector(default="yellow",objects=["red","yellow","green"])
    select_fn               = param.ObjectSelector(default=list,objects=[list,set,dict])
    int_list                = param.ListSelector(default=[3,5], objects=[1,3,5,7,9],precedence=0.5)
    single_file             = param.FileSelector(path='../../*/*.py*',precedence=0.5)
    multiple_files          = param.MultiFileSelector(path='../../*/*.py?',precedence=0.5)
    record_timestamp        = param.Action(lambda x: x.timestamps.append(dt.datetime.now()), 
                                           doc="""Record timestamp.""",precedence=0.7)
    
Example.num_int
Out[1]:
50000

As you can see, declaring Parameters depends only on the separate Param library. Parameters are a simple idea with some properties that are crucial for helping you create clean, usable code:

  • The Param library is pure Python with no dependencies, which makes it easy to include in any code without tying it to a particular GUI or widgets library, or even to the Jupyter notebook.
  • Parameter declarations focus on semantic information relevant to your domain, allowing you to avoid polluting your domain-specific code with anything that ties it to a particular way of displaying or interacting with it.
  • Parameters can be defined wherever they make sense in your inheritance hierarchy, allowing you to document, type, and range-limit them once, with all of those properties inherited by any base class. E.g. parameters work the same here whether they were declared in BaseClass or Example , which makes it easy to provide this metadata once, and avoiding duplicating it throughout the code wherever ranges or types need checking or documentation needs to be stored.

If you then decide to use these Parameterized classes in a notebook environment, you can import paramBokeh and easily display and edit the parameter values as an optional additional step:

In [2]:
import parambokeh
from bokeh.io import output_notebook
output_notebook()
Loading BokehJS ...
In [3]:
widgets = parambokeh.Widgets(BaseClass)
In [4]:
parambokeh.Widgets(Example)

As you can see, parambokeh.Widgets() does not need to be provided with any knowledge of your domain-specific application, not even the names of your parameters; it simply displays widgets for whatever Parameters may have been defined on that object. Using Param with paramBokeh thus achieves a nearly complete separation between your domain-specific code and your display code, making it vastly easier to maintain both of them over time. Here even the msg button behavior was specified declaratively, as an action that can be invoked (printing "Hello") independently of whether it is used in a GUI or other context.

Interacting with the widgets above is only supported on a live Python-backed server, but you can also export static renderings of the widgets to a file or web page.

By default, editing values in this way requires you to run the notebook cell by cell -- when you get to the above cell, edit the values as you like, and then move on to execute subsequent cells, where any reference to those parameter values will use your interactively selected setting:

In [5]:
Example.unbounded_int
Out[5]:
23
In [6]:
Example.num_int
Out[6]:
50000

Example.timestamps records the times you pressed the "record timestamp" button.

In [7]:
Example.timestamps
Out[7]:
[]
In [8]:
#Example.print_param_defaults() # see all parameter values

As you can see, you can access the parameter values at the class level from within the notebook to control behavior explicitly, e.g. to select what to show in subsequent cells. Moreover, any instances of the Parameterized classes in your own code will now use the new parameter values unless specifically overridden in that instance, so you can now import and use your domain-specific library however you like, knowing that it will use your interactive selections wherever those classes appear. None of the domain-specific code needs to know or care that you used ParamNB; it will simply see new values for whatever attributes were changed interactively. ParamNB thus allows you to provide notebook-specific, domain-specific interactive functionality without ever tying your domain-specific code to the notebook environment.

You can install ParamBokeh as described at github.com/ioam/parambokeh . Have fun widgeting!