Panel supports using parameters and dependencies between parameters as expressed by param in a simple way to encapsulate dashboards as declarative, self-contained classes.

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.

Parameters and widgets

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

In [1]:
import param
import pandas as pd
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)))
    dataframe               = param.DataFrame(pd.util.testing.makeDataFrame().iloc[:3])
    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.utcnow()), 
                                           doc="""Record timestamp.""", precedence=0.7)

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 or web-server environment, you can import panel and easily display and edit the parameter values as an optional additional step:

In [2]:
import panel as pn


base = BaseClass()
pn.Row(Example.param, base.param)