Reformatting Panels with Params

Sort of a basic question. But I couldn’t find the answer here https://panel.holoviz.org/user_guide/Customization.html .

Given some class the obvious way to create a panel of parameters is the following:

class Something():
             # Some Params
             str1 = param.String('1')
             str2 = param.String('2')

A = Something()
pn.Column(A.param).servable()

However, what if I want to control the layout of A.param? Or only show some of the parameters? Additionally, how could I set the “margin” and other properties of widgets for different parameters?

An Psuedo-code of what I want would be:

class Something(param.Parameterized):
             # Some Params
             str1 = param.String('1')
             str2 = param.String('2')
             pane1= pn.panel(str1,margin = (25,25,25,25))
             pane2= pn.panel(str2,margin = (25,25,25,25))
             row = pn.Row(pane1,pane2)

A = Something()
pn.Column(A.row).servable()

Hi @pyquest

Maybe “something” :slight_smile: like the below could be used as inspiration

Take a look at the Param Reference Guide and the Parameter user guide for the details.

import panel as pn
import param


class Something(param.Parameterized):
    str1 = param.String("1")
    str2 = param.String("2")
    str3 = param.String("3")


something = Something
panel = pn.Param(
    something,
    parameters=["str1", "str2"],
    widgets={"str1": {"margin": (100, 100, 100, 100)}, "str2": {"background": "lightgray"}},
)
pn.Column(
    pn.Row(
        panel, pn.Param(something.param.str3, background="yellow", margin=(100, 0, 0, 0), width=140, height=300)
    )
).servable()

Hi Marc,

Thanks for your help again! I should have been slightly more explicit. I’d like this panel to exist within the class so that the user doesn’t have to configure the panel on each instance of the class.

Imagine hundreds of params, in this case it would be nice to “select” certain params to display in a subpanel. I.e. imagine customizing a hundred parameters every time the object is created and displayed. So this would be something sorta like the “row” in my pseudo code.

Hi @pyquest

Do you mean “something” like the below?

import panel as pn
import param


class Something(param.Parameterized):
    str1 = param.String("1")
    str2 = param.String("2")
    str3 = param.String("3")

    view = param.Parameter()

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

        self.view = self._create_view()

    def _create_view(self):
        panel_left = pn.Param(
            self,
                parameters=["str1", "str2"],
                widgets={"str1": {"margin": (100, 100, 100, 100)}, "str2": {"background": "lightgray"}},
            )
        panel_right = pn.Param(self.param.str3, background="yellow", margin=(100, 0, 0, 0), width=140, height=300)
        return pn.Column(
            pn.Row(
                panel_left, panel_right
            )
        )

Something().view.servable()

Hi Marc,

Very helpful! I’m still having a bit of trouble because it feels like the API has to be cross-referenced to the right example in the gallery. But I am starting to get the gist of it.

A follow up question. If this object were a DataFrame param object

 df= param.DataFrame(pd.DataFrame())

How do I get control of it’s properties?

_create_view(self):

 panel_left = pn.Param(
        self,
            parameters=["str1", "str2"],
            widgets={"str1": {"margin": (100, 100, 100, 100)}, "str2": {"background": "lightgray"}}  )
 d = pn.Param(self.param.df,width=200)
 return pn.Column(panel_left,d)

I.e. the width =200 doesn’t seem to have any real effect causing the column to explode in width when it gets to the dataframe level. And what I really want is to use the pn.pane.DataFrame to display this parameter dataframe. ( The dataframe is for monitoring values that changes only due to interacting with the gui.)

Hi @pyquest

if don’t understand exactly what your question is. But does the below example help?

import pandas as pd
import panel as pn
import param


class Something(param.Parameterized):
    str1 = param.String("1")
    str2 = param.String("2")
    dataframe = param.DataFrame()

    view = param.Parameter()

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

        self.view = self._create_view()

    def _create_view(self):
        panel_left = pn.Param(
            self,
                parameters=["str1", "str2"],
                widgets={"str1": {"margin": (100, 100, 100, 100)}, "str2": {"background": "lightgray"}},
            )
        panel_right = pn.pane.DataFrame(
            object=self.dataframe,
            margin=(100, 0, 0, 0),
            width=200,
            height=300,
            )
        return pn.Column(
            pn.Row(
                panel_left, panel_right
            )
        )

dataframe=pd.DataFrame({"x": [1,2,3], "y": [2,4,6]})
Something(dataframe=dataframe).view.servable()

Hey Marc,

Thanks for the help again. I’m struggling to come up with a simple example here. The key thing is that I want a dataframe parameter than can be updated internally in the class (just like in our previous/earlier examples). The problem with code like:

panel_right = pn.pane.DataFrame(
            object=self.dataframe,
            margin=(100, 0, 0, 0),
            width=200,
            height=300,
            )

Is that the dataframe is no longer able to update (in the GUI) when it’s value is changed internally.

In contrast running (in _create_view)

df= pn.Param(self.param.dataframe)
panel_right = pn.panel(df,width = 200)

Allows me to both display and update the gui. However, the “width” argument is ignored.

If I tried to run

pn.pane.DataFrame(df)

I would get the error: ValueError: DataFrame pane does not support objects of type ‘Param’.


In other words, I need the object sent to the pn.pane.DataFrame to not just be an instance of the self.dataframe parameter but rather the “parameter” that updates.

Found out part of the problem and would like to report this as a bug.

This code works:

          panel_right = pn.panel(self.param.df)
          panel_right.width = 250

While this code does not:

          panel_right = pn.panel(self.param.df,width = 250)

Still would be nice to use the HTML version of the dataframe.