Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add example comparing a simple Streamlit app to a Panel app. #5016

Closed
MarcSkovMadsen opened this issue Jun 2, 2023 · 2 comments · Fixed by #5027
Closed

Add example comparing a simple Streamlit app to a Panel app. #5016

MarcSkovMadsen opened this issue Jun 2, 2023 · 2 comments · Fixed by #5027
Labels
type: enhancement Minor feature or improvement to an existing feature

Comments

@MarcSkovMadsen
Copy link
Collaborator

MarcSkovMadsen commented Jun 2, 2023

Its great to have the section Compare Panel and Streamlit but its very text heavy and does not indicate in any way how to code and apps compare.

Please add an end to end example comparing the two. And example that will also help the user migrate from Streamlit to Panel as I saw in a request for on Discourse. The user deleted his post. But came back later with this post https://discourse.holoviz.org/t/cannot-execute-script-with-panel/5464.

@MarcSkovMadsen MarcSkovMadsen added the type: enhancement Minor feature or improvement to an existing feature label Jun 2, 2023
@MarcSkovMadsen
Copy link
Collaborator Author

MarcSkovMadsen commented Jun 2, 2023

It could for example be this example suggested on Discourse

Streamlit

app-streamlit.mp4
import time

import streamlit as st


def calculation_a():
    time.sleep(1.5)


def calculation_b():
    time.sleep(1.5)


st.write("# Calculation Runner")

option = st.radio("Which Calculation would you like to perform?", ("A", "B"))

st.write("You chosed: ", option)
if option == "A":
    if st.button("Press to run calculation"):
        with st.spinner("running... Please wait!"):
            time_start = time.perf_counter()
            calculation_a()
            st.write("Done!")
            time_end = time.perf_counter()
            st.write(f"The function took {time_end - time_start:1.1f} seconds to complete")

    else:
        st.write("Calculation A did not run yet")

elif option == "B":
    if st.button("Press to run calculation"):
        with st.spinner("running... Please wait!"):
            time_start = time.perf_counter()
            calculation_b()
            st.write("Done!")
            time_end = time.perf_counter()
            st.write(f"The function took {time_end - time_start:1.1f} seconds to complete")
    else:
        st.write("Calculation B did not run yet")

Panel

app-panel.mp4
import time
import panel as pn

pn.extension(sizing_mode="stretch_width")


def calculation_a():
    time.sleep(1.5)


def calculation_b():
    time.sleep(1.5)


calculation = pn.widgets.RadioBoxGroup(
    name="Calculation",
    options=["Calculation A", "Calculation B"],
)

run = pn.widgets.Button(name="Press to run calculation")

indicator = pn.Column(
    pn.indicators.LoadingSpinner(value=True, width=25, height=25, visible=True),
    "Running...Please wait!",
    visible=False,
)
result = pn.pane.Markdown("")


def message(calculation):
    return f"You choosed **{calculation}**"


imessage = pn.bind(message, calculation)


@pn.depends(run, watch=True)
def run_message(_):
    run.disabled = indicator.visible = True
    result.object = ""

    time_start = time.perf_counter()
    if "A" in calculation.value:
        calculation_a()
    else:
        calculation_b()
    time_end = time.perf_counter()
    result.object = f"""Done!

The function took {time_end - time_start:1.1f} seconds to complete"""
    run.disabled = indicator.visible = False


component = pn.Column(
    pn.pane.Markdown("Calculation", margin=(0, 10)),
    pn.panel(calculation, margin=(0, 10)),
    imessage,
    run,
    indicator,
    result,
)

pn.template.BootstrapTemplate(title="Calculation Runner", main=[component]).servable()

I think there are lots of things we can learn an improve from this comparison

  • What api would we recommend (Widgets or Parameterized)?
  • What mental model for architecting the app would we teach our users? With Streamlit there is a simple linear approach. With Panel you have to plan/ think/ experiment a bit more and there are so many ways to achieve the same.
  • How will we avoid users running into bugs and issues that would make it very difficult for them
  • How can we make our api easily support dynamic interfaces where you add/ remove, hide/ unhide components dynamically and display progress? (And should we?)
  • Why do we need to write so many more code lines?

image

@philippjfr
Copy link
Member

philippjfr commented Jun 2, 2023

Just had a thought, I think if we supported generator this could be rewritten roughly like this:

import time
import panel as pn

pn.extension(sizing_mode="stretch_width", template='bootstrap')

calculation = pn.widgets.RadioBoxGroup(
    name="Calculation",
    options=["Calculation A", "Calculation B"],
)
run = pn.widgets.Button(name="Press to run calculation")

def calculation_a():
    time.sleep(2.5)

def calculation_b():
    time.sleep(1.5)

def runner(calculation, running):
    if not running:
        yield f"Calculation {calculation} did not run yet"
        return
    yield pn.Column(
        pn.indicators.LoadingSpinner(value=True, width=25, height=25),
        "Running...Please wait!",
    )
    time_start = time.perf_counter()
    (calculation_a if 'A' in calculation else calculation_b)()
    time_end = time.perf_counter()
    yield f"""Done!
    The function took {time_end - time_start:1.1f} seconds to complete"""

def message(calculation):
    return f"You chose **{calculation}**"

imessage = pn.pane.Markdown(pn.bind(message, calculation))

pn.Column(
    "# Calculation Runner",
    calculation,
    imessage,
    run,
    pn.bind(runner, calculation, run)
).servable()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement Minor feature or improvement to an existing feature
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants