Starting with panel

Hi!

I would like to start using panel library by building hopefully simple dashboards based on a template (let’s say FastListTemplate) to understand what can be done with panel.

While there are examples online to get you started, I noticed that most of the examples are based on using panel within a Notebook. Personally I want to learn to use it as a standalone app, launched for instance using panel serve dashboard.py

Here is a use case I have in mind to start with:

  • One button to read a dataframe from a local file
  • One button to update the dataframe which was read (act more like a filtering button)
  • One tabulator with the dataframe (first 50 rows for instance)
  • One input field which accepts only strings
  • One input field which accepts only integers
    The two input fields will be used to either return the dataframe with rows containing certain string, or rown which have a column with higher values.

I know this is vague without some data provided, but what I hope for is a starting point as I am stuck to even get started. I also fail to understand how multiple elements can interact with each other and pass values between them.

Hints are welcomed!

Thanks!

1 Like

Hi @digi

Welcome to the community. I hope you will quickly be able to build some apps and tools that can help you.

Something a bit like what you describe is built using the code below.

script.py

import pandas as pd
import panel as pn

pn.extension("tabulator")

FILE_NAME = "out.csv"

# Helper Functions

def _save_file():
    df = pd.DataFrame({
        "A": [1,2,3,4,5],
        "B": ["hvplot", "holoviews", "panel", "param", "datashader"]
    })
    df.to_csv(FILE_NAME, index=False)

_save_file()

def _read_file():
    return pd.read_csv(FILE_NAME)

def _filter_data(data:pd.DataFrame, str_value, int_value):
    condition = ((data["A"]>=int_value) & (data["B"].str.contains(str_value)))
    return data[condition].head(50)

# Widgets

source_table = pn.widgets.Tabulator()
filtered_table = pn.widgets.Tabulator(index=False)
str_input = pn.widgets.TextInput(name="B contains", value="")
int_input = pn.widgets.IntInput(name="A >=", value=0)
read_button = pn.widgets.Button(name="Read", button_type="primary")
filter_button = pn.widgets.Button(name="Filter", align="end", button_type="success")

filter_widget = pn.Row(int_input, str_input, filter_button)

container = pn.Column(
    read_button,
)

# Actions. This is what should happen when the buttons are clicked.

def read_file(_=None):
    source_table.value = _read_file()
    container[:]=[filter_widget, filtered_table]

    filter_file()

def filter_file(_=None):
    data = source_table.value
    if data is None:
        filtered_table.value = data
    
    filtered_table.value = _filter_data(data, str_input.value, int_input.value)

# Bindings
# Set watch=True to automatically run functions when Button is clicked

pn.bind(read_file, read_button, watch=True)

pn.bind(filter_file, filter_button, watch=True)

# Mark it .servable to show in your app

container.servable()

Start the server with --autoreload

panel serve script.py --autoreload

Open http://localhost:5006

Feel free to ask questions :+1:

And this is a slightly extended example where the app is wrapped in the FastListTemplate.

import pandas as pd
import panel as pn

pn.extension("tabulator", sizing_mode="stretch_width")

FILE_NAME = "out.csv"

# Helper Functions

def _save_file():
    df = pd.DataFrame({
        "A": [1,2,3,4,5],
        "B": ["hvplot", "holoviews", "panel", "param", "datashader"]
    })
    df.to_csv(FILE_NAME, index=False)

_save_file()

def _read_file():
    return pd.read_csv(FILE_NAME)

def _filter_data(data:pd.DataFrame, str_value, int_value):
    condition = ((data["A"]>=int_value) & (data["B"].str.contains(str_value)))
    return data[condition].head(50)

# Widgets

source_table = pn.widgets.Tabulator()
filtered_table = pn.widgets.Tabulator(index=False)
str_input = pn.widgets.TextInput(name="B contains", value="")
int_input = pn.widgets.IntInput(name="A >=", value=0)
read_button = pn.widgets.Button(name="Read", button_type="primary")
filter_button = pn.widgets.Button(name="Filter", align="end", button_type="success")

filter_widget = pn.Row(int_input, str_input, filter_button)

container = pn.Column(
    read_button,
)

# Actions. This is what should happen when the buttons are clicked.

def read_file(_=None):
    source_table.value = _read_file()
    container[:]=[filter_widget, filtered_table]

    filter_file()

def filter_file(_=None):
    data = source_table.value
    if data is None:
        filtered_table.value = data
    
    filtered_table.value = _filter_data(data, str_input.value, int_input.value)

# Bindings
# Set watch=True to automatically run functions when Button is clicked

pn.bind(read_file, read_button, watch=True)

pn.bind(filter_file, filter_button, watch=True)

# Layout it out in a template

pn.template.FastListTemplate(
    title="Starting with Panel",
    main=[container]
).servable()

1 Like

@Marc, thanks for your reply.
I was about to ask about wrapping it into a template, when you already answered.
Interesting your example - I was under the impression that almost always there should be a class A(pn.viewable.Viewable) or class A(param.Parameterized) in order to link the elements as a best practice. Honestly, I am not even sure what are the pros and cons between the different methods - but that’s a question for another topic. I will try to build-up on your example and probably will ask more questions.

1 Like

There is more than one way to do things with Panel because it needs to support different types of developers and different types of development environments.

I chose to target a “beginning pythonista” that does not know (much) about classes and annotations :slight_smile: . Thus I stayed away from Parameterized or Viewable classes. I also stayed away from the @pn.depends notation.

Personally I like the class based approach - especially as my app grows or I want to build reusable components.

1 Like