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

Support easy match layout #7571

Open
MarcSkovMadsen opened this issue Dec 24, 2024 · 2 comments
Open

Support easy match layout #7571

MarcSkovMadsen opened this issue Dec 24, 2024 · 2 comments
Labels
type: discussion Requiring community discussion

Comments

@MarcSkovMadsen
Copy link
Collaborator

With React and Reflex its easy to layout based on a match https://reflex.dev/docs/library/dynamic-rendering/match/:

Image

With Panel you have to write some boiler plat code breaking the layout into more pieces. For example the match function below:

# https://reflex.dev/docs/library/dynamic-rendering/cond/

import param
import panel as pn

pn.extension()

class MatchState(param.Parameterized):
    cat_breed: str = param.String("persian")
    animal_options: list[str] = param.List([
        "persian",
        "siamese",
        "maine coon",
        "ragdoll",
        "pug",
        "corgi",
    ])

def match(cond, *match_cases):
    if isinstance(cond, param.Parameter):
        return pn.bind(match, cond, *match_cases)

    default = pn.pane.Str("No Match")
    
    for item in match_cases:
        if isinstance(item, (list, tuple)):
            key, value = item
            if cond==key:
                return value
        else:
            default = item
    
    return default

state = MatchState()

def match_demo():
    return pn.Column(
        pn.widgets.Select.from_param(state.param.cat_breed, options=state.param.animal_options),
        match(
            state.param.cat_breed,
             ("persian", pn.pane.Str("Persian cat selected.")),
            ("siamese", pn.pane.Str("Siamese cat selected.")),
            (
                "maine coon",
                pn.pane.Str("Maine Coon cat selected."),
            ),
            ("ragdoll", pn.pane.Str("Ragdoll cat selected.")),
            pn.pane.Str("Unknown cat breed selected."),
        )
    )


pn.Column(
    match_demo
).servable()

Image

This makes it harder to use Panel. Furthermore there is no .rx.match function. I think there should be?

@MarcSkovMadsen
Copy link
Collaborator Author

MarcSkovMadsen commented Dec 24, 2024

Alternative Implementations

Reactive pipe and get (Recommended?)

This one is using reactive expressions

# https://reflex.dev/docs/library/dynamic-rendering/cond/

import param
import panel as pn

pn.extension()

class MatchState(param.Parameterized):
    cat_breed: str = param.String("persian")
    animal_options: list[str] = param.List([
        "persian",
        "siamese",
        "maine coon",
        "ragdoll",
        "pug",
        "corgi",
    ])

state = MatchState()

def match_demo():
    return pn.Column(
        pn.widgets.Select.from_param(state.param.cat_breed, options=state.param.animal_options),
        pn.rx([
            ("persian", pn.pane.Str("Persian cat selected.")),
            ("siamese", pn.pane.Str("Siamese cat selected.")),
            (
                "maine coon",
                pn.pane.Str("Maine Coon cat selected."),
            ),
            ("ragdoll", pn.pane.Str("Ragdoll cat selected.")),
        ]).rx.pipe(dict).get(state.param.cat_breed,  pn.pane.Str("Unknown cat breed selected.")),
    )

pn.Column(
    match_demo
).servable()

It works. It might not be clear to users as its not documented. The specific match function is easier to read. But I think this is OK and we should just document it?

The simplified version below is probably the one that should end up in the documentation

import param
import panel as pn

pn.extension()

class MatchState(param.Parameterized):
    cat_breed: str = param.Selector(objects=[
        "persian",
        "siamese",
        "maine coon",
        "ragdoll",
        "pug",
        "corgi",
    ])

state = MatchState()

def match_demo():
    return pn.Column(
        pn.widgets.Select.from_param(state.param.cat_breed),
        pn.rx({
            "persian": pn.pane.Str("Persian cat selected."),
            "siamese": pn.pane.Str("Siamese cat selected."),
            "maine coon": pn.pane.Str("Maine Coon cat selected."),
            "ragdoll": pn.pane.Str("Ragdoll cat selected."),
        }).get(state.param.cat_breed,  pn.pane.Str("Unknown cat breed selected.")),
    )

match_demo().servable()

Here is the christmas version

import param
import panel as pn

pn.extension(design="bootstrap")

class HolidayMatchState(param.Parameterized):
    holiday_item: str = param.Selector(objects=[
        "🎄 Christmas Tree",
        "🎅 Santa Claus",
        "🎁 Gift",
        "🦌 Reindeer",
        "❄️ Snowflake",
        "🌟 Star",
    ])

state = HolidayMatchState()

def holiday_match_demo():
    return pn.Column(
        pn.widgets.Select.from_param(state.param.holiday_item, name="Choose Your Holiday Favorite 🎉"),
        pn.rx({
            "🎄 Christmas Tree": pn.pane.Str("You picked the festive 🎄 Christmas Tree!"),
            "🎅 Santa Claus": pn.pane.Str("Ho ho ho! 🎅 Santa is here!"),
            "🎁 Gift": pn.pane.Str("A 🎁 Gift? What could it be? 🎀"),
            "🦌 Reindeer": pn.pane.Str("The 🦌 Reindeer are ready to fly!"),
            "🌟 Star": pn.pane.Str("The 🌟 Star shines bright this holiday season!"),
        }).get(state.param.holiday_item, pn.pane.Str("Unknown holiday favorite selected! 🎅")),
    )

holiday_match_demo().servable()

For some unknown reason it works as a server app. But it did not work for me in jupyter or vs code notebooks using different python environments! When I select another value nothing happens.

@philippjfr
Copy link
Member

The notebook version does work for me as well and yes, the .get approach is what I've used in the past.

@philippjfr philippjfr added the type: discussion Requiring community discussion label Jan 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: discussion Requiring community discussion
Projects
None yet
Development

No branches or pull requests

2 participants