How to create nested panel?

I am investigating on how to use panel with param on nested parameters and how to properly display the parameters.

Consider the minimum working example below:

import panel as pn
import param


pn.extension()


class Person(param.Parameterized):
    first_name = param.String(label="First Name")
    last_name = param.String(label="Last Name")


class Family(param.Parameterized):
    father = param.ClassSelector(class_=Person, label="Father")
    mother = param.ClassSelector(class_=Person, label="Mother")
    children = param.List(item_type=Person, label="Children")


family = Family(
    father=Person(first_name="John", last_name="Doe"),
    mother=Person(first_name="Jane", last_name="Doe"),
    children=[Person(first_name="Alice"), Person(first_name="Bob")],
)

pn.Column(pn.panel(family.param, expand_button=False, expand=True)).show()

The resulting HTML looks like this:

In pseudo style the image above β€œtranslates” to:

Family
β”œβ”€β”€ Father (cryptic line edit)
β”œβ”€β”€ Mother (cryptic line edit)
└── Children (cryptic line edit)
    β”œβ”€β”€ <Cryptic reference Father>
    β”‚   β”œβ”€β”€ First name
    β”‚   └── Last name
    └── <Cryptic reference Mother>
        β”œβ”€β”€ First name
        └── Last name

Is there a way to realize the following style:

Family
β”œβ”€β”€ Father (Header only)
β”‚   β”œβ”€β”€ First name
β”‚   └── Last name
β”œβ”€β”€ Mother (Header only)
β”‚   β”œβ”€β”€ First name
β”‚   └── Last name
β”œβ”€β”€ Child 1 (Header only)
β”‚   β”œβ”€β”€ First name
β”‚   └── Last name
└── Child 2 (Header only)
    β”œβ”€β”€ First name
    └── Last name

Thank you in advance for any help or guidance.

1 Like

Hi @nknavasto

Welcome to the community.

For example you can use the Viewer base class to create custom views of your Parameterized classes:

import panel as pn
import param

pn.extension()


class Person(pn.viewable.Viewer):
    first_name = param.String(label="First Name")
    last_name = param.String(label="Last Name")

    def __panel__(self):
        return pn.Row(self.param.first_name, self.param.last_name)


class Family(pn.viewable.Viewer):
    father = param.ClassSelector(class_=Person, label="Father")
    mother = param.ClassSelector(class_=Person, label="Mother")
    children = param.List(item_type=Person, label="Children")

    def __panel__(self):
        child_items = []
        for item, child in enumerate(self.children):
            child_items.append(f"## Child {item}")
            child_items.append(child)

        return pn.Column("## Father", self.father, "## Mother", self.mother, *child_items)


family = Family(
    father=Person(first_name="John", last_name="Doe"),
    mother=Person(first_name="Jane", last_name="Doe"),
    children=[Person(first_name="Alice"), Person(first_name="Bob")],
)

pn.Column(family).servable()

1 Like

Hi Marc,

thank you very much for your answer. This helped me a lot.

1 Like

If you will update the children dynamically you will need to do something like below

import panel as pn
import param

pn.extension()


class Person(pn.viewable.Viewer):
    first_name = param.String(label="First Name")
    last_name = param.String(label="Last Name")

    def __panel__(self):
        return pn.Row(self.param.first_name, self.param.last_name)


class Family(pn.viewable.Viewer):
    father = param.ClassSelector(class_=Person, label="Father")
    mother = param.ClassSelector(class_=Person, label="Mother")
    children = param.List(item_type=Person, label="Children")

    @pn.depends("children")
    def _child_view(self):
        child_items = []
        for item, child in enumerate(self.children):
            child_items.append(f"## Child {item}")
            child_items.append(child)
        return pn.Column(*child_items, margin=0)

    def __panel__(self):
        child_items = []
        for item, child in enumerate(self.children):
            child_items.append(f"## Child {item}")
            child_items.append(child)

        return pn.Column("## Father", self.father, "## Mother", self.mother, self._child_view)


family = Family(
    father=Person(first_name="John", last_name="Doe"),
    mother=Person(first_name="Jane", last_name="Doe"),
    children=[Person(first_name="Alice"), Person(first_name="Bob")],
)

pn.Column(family).servable()
1 Like