Search code examples
pythondashboardholovizholoviz-panel

Python Panel Dashboard - Multi-Page app not updating as expected


I want a Python Panel app which shows different page content based on a radio button selection in the sidebar.

There's some example code for doing exactly this in the way I want, shown as "Option 3" here: https://discourse.holoviz.org/t/multi-page-app-documentation/3108/3?u=maxpower

Code below:

import panel as pn

pn.extension(sizing_mode="stretch_width")

pages = {
    "Page 1": pn.Column("# Page 1", "...bla bla bla"),
    "Page 2": pn.Column("# Page 2", "...more bla"),
}


def show(page):
    return pages[page]


starting_page = pn.state.session_args.get("page", [b"Page 1"])[0].decode()
page = pn.widgets.RadioButtonGroup(
    value=starting_page,
    options=list(pages.keys()),
    name="Page",
    sizing_mode="fixed",
    button_type="success",
)
ishow = pn.bind(show, page=page)
pn.state.location.sync(page, {"value": "page"})

ACCENT_COLOR = "#0072B5"
DEFAULT_PARAMS = {
    "site": "Panel Multi Page App",
    "accent_base_color": ACCENT_COLOR,
    "header_background": ACCENT_COLOR,
}
pn.template.FastListTemplate(
    title="As Single Page App",
    sidebar=[page],
    main=[ishow],
    **DEFAULT_PARAMS,
).servable()
```import panel as pn

pn.extension(sizing_mode="stretch_width")

pages = {
    "Page 1": pn.Column("# Page 1", "...bla bla bla"),
    "Page 2": pn.Column("# Page 2", "...more bla"),
}


def show(page):
    return pages[page]


starting_page = pn.state.session_args.get("page", [b"Page 1"])[0].decode()
page = pn.widgets.RadioButtonGroup(
    value=starting_page,
    options=list(pages.keys()),
    name="Page",
    sizing_mode="fixed",
    button_type="success",
)
ishow = pn.bind(show, page=page)
pn.state.location.sync(page, {"value": "page"})

ACCENT_COLOR = "#0072B5"
DEFAULT_PARAMS = {
    "site": "Panel Multi Page App",
    "accent_base_color": ACCENT_COLOR,
    "header_background": ACCENT_COLOR,
}
pn.template.FastListTemplate(
    title="As Single Page App",
    sidebar=[page],
    main=[ishow],
    **DEFAULT_PARAMS,
).servable()

However, it does not quite work as expected, at least not on panel v1.0.0rc6. When I click "Page 2" and then click back to "Page 1", the main content does not switch back as expected. Weirdly, the URL does switch between ...=Page+1 and ...=Page+2 as expected, but not the main content itself.

I've been trying to debug this using panel serve app.py --autoreload, which is a nice workflow, but I still haven't been able to figure out why this isn't working, or a good solution to get it working.


Solution

  • The answer given here is exactly what I was looking for, and I've confirmed works as expected in Panel's v1.0.0rc6:

    import panel as pn
    from panel.template import FastListTemplate
    
    pn.extension()
    
    # Define pages as classes
    class Page1:
        def __init__(self):
            self.content = pn.Column("# Page 1", "This is the content of page 1.")
    
        def view(self):
            return self.content
    
    
    class Page2:
        def __init__(self):
            self.content = pn.Column("# Page 2", "This is the content of page 2.")
    
        def view(self):
            return self.content
    
    # Instantiate pages and add them to the pages dictionary
    pages = {
        "Page 1": Page1(),
        "Page 2": Page2(),
    }
    
    # Function to show the selected page
    def show_page(page_instance):
        main_area.clear()
        main_area.append(page_instance.view())
    
    # Define buttons to navigate between pages
    page1_button = pn.widgets.Button(name="Page 1", button_type="primary")
    page2_button = pn.widgets.Button(name="Page 2", button_type="primary")
    
    # Set up button click callbacks
    page1_button.on_click(lambda event: show_page(pages["Page 1"]))
    page2_button.on_click(lambda event: show_page(pages["Page 2"]))
    
    # Create the sidebar
    sidebar = pn.Column(page1_button, page2_button)
    
    # Create the main area and display the first page
    main_area = pn.Column(pages["Page 1"].view())
    
    # Create the Material Template and set the sidebar and main area
    template = FastListTemplate(
        title="Multi-Page App",
        sidebar=[sidebar],
        main=[main_area],
    )
    
    # Serve the Panel app
    template.servable()