Adding titles to tabulator parameters in panel param

Consider the following:


import panel as pn
import param as pm
import pandas as pd

pn.extension('tabulator')


class Dataset(pm.Parameterized):
    file = pm.FileSelector(
        default=None,
        path='./app/input/*.csv',
    )
    page_size = pm.Integer(default=20, precedence=-1)
    dataset = pm.DataFrame(
        default=None,
    )

    def __init__(self, **params):
        super().__init__(**params)
        self.load_file()

    @pm.depends('file', on_init=True, watch=True)
    def load_file(self):
        self.dataset = pd.read_csv(self.file)

    @pm.depends('dataset', 'page_size')
    def view(self):
        view = pn.panel(
            pn.Param(
                self.param,
                sort=False,
                widgets={
                    'dataset': {
                        'widget_type': pn.widgets.Tabulator,
                        'layout': 'fit_columns',
                        'page_size': self.page_size,
                        'pagination': 'remote',
                        'header_filters': True,
                        'sizing_mode': 'stretch_width',
                    },
                },
            )
        )
        return view

The above widget provides a little drop down csv file selector and viewer.

In the view function, I would like that a dataset has a title in the app. I would like something like:

    @pm.depends('dataset', 'page_size')
    def view(self):
        view = pn.panel(
            pn.Param(
                self.param,
                sort=False,
                widgets={
                    'dataset': {
                        'widget_type': pn.Markdown('### Dataset') + pn.widgets.Tabulator,

And in a more general case, how could I render all DataFrame parameters on a class with the above title+tabulator style without needing to hardcode for each one?

Panel supports quite a few different ways on how to create widgets out of param.parameterized class parameters.

Using pn.Param(…) to BULK autogenerate them is just one.

But you can also specify them one by one, with one of 2 methods:

  1. just use self.param.
    → panel will create the default widget
  2. use pn.widgets..from_param(self.param.<parameter_name>, … options)
    → Panel will create the widget specified, using the stuff from the parameter definition and automatically linking the 2 together

Specifying the widgets manually allows full control of the layout, eg. you can do things like the following easily:

import panel as pn
import param as pm
import pandas as pd

pn.extension('tabulator')


class Dataset(pm.Parameterized):
    file = pm.FileSelector(
        default=None,
        path='./*.csv',
    )
    page_size = pm.Integer(default=20, precedence=10)
    dataset = pm.DataFrame(default=None)

    def __init__(self, **params):
        super().__init__(**params)
        self.load_file()

    @pm.depends('file', on_init=True, watch=True)
    def load_file(self):
        self.dataset = pd.read_csv(self.file)

    @pm.depends('dataset', 'page_size')
    def view(self):
        view = pn.Column(
            # have panel create key input widgets automatically
            # from parameter definition and layout in a row
            pn.Row(self.param.file, self.param.page_size),
            # add a Header and the actual output table
            '## DATASET Table', 
            pn.widgets.Tabulator.from_param(
                self.param.dataset, 
                layout='fit_columns',
                page_size=self.page_size,
                pagination='remote',
                header_filters=True,
                sizing_mode='stretch_width'), 
        )
        return view

I tend to use the manual method more often because I mostly create more complex layouts.

But the pn.Param comes in handy to autogenerate stuff easily (and the part of the program doesn’t even need to know what parameters are defined ).
Eg. a cool example is that all? widgets have a “.control()” method that allows you to generate widgets to control the parameters of the widget.
Eg for your example you don’t even have to define the page_size yourself. you could do things like:

import panel as pn
import param as pm
import pandas as pd

pn.extension('tabulator')

class Dataset(pm.Parameterized):
    file = pm.FileSelector(default=None,path='./*.csv')
    page_size = pm.Integer(default=20, precedence=10)
    dataset = pm.DataFrame(default=None)

    def __init__(self, **params):
        super().__init__(**params)
        self.load_file()

    @pm.depends('file', on_init=True, watch=True)
    def load_file(self):
        self.dataset = pd.read_csv(self.file)

    @pm.depends('dataset', 'page_size')
    def view(self):

        table = pn.widgets.Tabulator.from_param(
                self.param.dataset, 
                layout='fit_columns',
                #page_size=self.page_size,
                pagination='remote',
                header_filters=True,
                sizing_mode='stretch_width')

        table_controls = table.controls(parameters=['page_size', 'disabled', 'layout'])
        
        view = pn.Column(
            # have panel create key input widgets automatically
            # from parameter definition and layout in a row
            pn.Row(self.param.file, self.param.page_size),
            
            # Add TAble width controls
            pn.Row(
                pn.Column('## Table Controls', table_controls, width=200),
                pn.Column('## DATASET Table', table)
            )
        )
        return view

ds = Dataset()
ds.view()

2 Likes

That’s really cool about table_controls I hadn’t seen that before. Thanks for providing all of that information and the solution which solves for manual setting, which you say is the more common use case, and I would agree with that.

So we should just add titles to our dataframe views in panel apps. Rather than having a default title attribute for dataframe parameters in panel, although that would be cool too. Do dataframes have a .name attribute? If so, panel could use that to autogenerate a title header for dataframes.

Regarding the “title” - this is more about what the Widget supports.
The param.Parameter and its subclasses like param.Dataframe actually do support .name and .label.

It’s just that some panel widgets do support showing a “Label”/“Title”/“Name”, other’s don’t. Looking through the widgets, there seem to be 3 buckets:

  1. those that have a Label & Value, and without the Label you don’t know what your’Re looking at (eg. FloatInput, IntInput, …)
  2. those where the Label/Title is actually the “value” (eg. Buttons)
  3. those without a Label/Name (eg. Tabulator, Player), …
    Although 3) would be nice to have some way to easily add a Title (and some strangely even have a .name attribute), I’m kindda ok, that they don’t. Mostly because it’s likely hard to agree on the default visuals (put it left, right, center, … ? how large the font ? … ). so you may end up having a superset of a Markdown+Layout+Widget to be flexible enough.

If you do use Tabulators (or widgets) in a certain way and combination often, it may be worth just to create your own custom-version and use that. The documentation gives some easy examples (Combine Existing Components — Panel v1.2.3)

1 Like