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

Mature intermediate tutorials #6466

Merged
merged 21 commits into from Mar 15, 2024
Merged

Conversation

MarcSkovMadsen
Copy link
Collaborator

No description provided.

@@ -1,15 +1,14 @@
# Structure with a DataStore
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This application does not seem very robust. As soon as I drag one of the range slider the chart does not show anything.

image

And moving the range slider back in place does not display anything in the plot again.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will follow up in a new PR.

@MarcSkovMadsen MarcSkovMadsen added this to the v1.4.0 milestone Mar 10, 2024
@MarcSkovMadsen MarcSkovMadsen marked this pull request as ready for review March 10, 2024 19:50
def styles(font_size):
return {"font-size": f"{font_size}px"}

styles_rx = pn.rx(styles)(intslider)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a a small hint what rx(something01)(something02) does? I have not seen the () () notation with .rx before (but maybe it's obvious to most people?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pn.rx(some_func)(something1, something2) can be used as pn.bind(some_func, something1, something2) and is the same as pn.bind(some_func, something1, something2).rx().

Its introduced and used in the basic tutorial https://holoviz-dev.github.io/panel/tutorials/basic/pn_rx.html. That is why I don't explain it here.


:::{note}

`pn.bind` is the predecessor of `pn.rx`. We recommend using `pn.rx` over `pn.bind` as it's much more flexible and efficient. We include this example because you will find lots of examples in the Panel documentation and in the Panel community using `pn.bind`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or rather pn.rx is the successor of pn.bind`.? This is good to know by the way, I did not realize this before. Is there a separate section somewhere regarding this in the docs (starting with the pn.bind) documentation?

Something that says at the top: We recommend pn.rx instead of pn.bind from now on? And a link to a migration tutorial that covers most cases of how best to change existing code that uses pn.bind?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I will have to update that. We discussed this at HoloViz meeting today and we are not really ready to recommend transition yet.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see, still interesting to know that that is the direction going forward.

Copy link
Contributor

@Coderambling Coderambling left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice tutorial @MarcSkovMadsen ! Made 2 small comments, not vital.

@MarcSkovMadsen
Copy link
Collaborator Author

MarcSkovMadsen commented Mar 12, 2024

I would like to add Todo project using intermediate techniques. Here is my code. It almost works.

Code
from typing import Callable

import param

import panel as pn

pn.extension(sizing_mode="stretch_width", design="material")

BUTTON_WIDTH = 125

class Task(pn.viewable.Viewer):
    value: str = param.String()
    completed: bool = param.Boolean()
    
    remove: bool = param.Event()

    def __panel__(self):
        completed = pn.widgets.Checkbox.from_param(self.param.completed, name="", align="center", sizing_mode="fixed")
        content = pn.pane.Markdown(object=self.param.value)
        remove = pn.widgets.Button.from_param(self.param.remove, width=BUTTON_WIDTH, icon="trash", sizing_mode="fixed")
        return pn.Row(completed, content, remove, sizing_mode="stretch_width")

class TaskInput(pn.viewable.Viewer):
    value = param.ClassSelector(class_=Task)

    def _no_value(self, value):
        return not bool(value)

    def __panel__(self):
        text_input = pn.widgets.TextInput(name="Task", placeholder="Enter a task")
        text_input_has_value = pn.rx(self._no_value)(text_input.param.value_input)
        submit_task = pn.widgets.Button(
            name="Add",
            align="center",
            button_type="primary",
            width=BUTTON_WIDTH,
            sizing_mode="fixed",
            disabled=text_input_has_value,
        )
        
        @pn.depends(text_input, submit_task, watch=True)
        def clear_text_input(value, submit_task):
            if text_input.value:
                self.value = Task(value=text_input.value)
                text_input.value=text_input.value_input=""

        return pn.Row(
            text_input, submit_task
        )


class TaskList(pn.viewable.Viewer):
    tasks = param.List()

    remove_all_tasks = param.Event(label="Remove All")
    
    def __init__(self, **params):
        super().__init__(**params)
        for task in self.tasks:
            def remove(_, task=task):
                self.tasks = [item for item in self.tasks if not item==task]
            pn.bind(remove, task.param.remove, watch=True)
            pn.bind(self._handle_done_changed, task.param.completed, watch=True)

        self._llayout = pn.Column()

    def _remove_task(self, *args):
        task=args[0]
        self.tasks = [item for item in self.tasks if not item==task]
        
    def _remove_all_tasks(self, *args):
        self.clear()

    def _add_task(self, task):
        def remove(_, task=task):
                self.tasks = [item for item in self.tasks if not item==task]
        pn.bind(remove, task.param.remove, watch=True)
        self.tasks = [*self.tasks, task]

        return task

    @pn.depends("remove_all_tasks", watch=True)
    def _handle_remove_all_tasks(self):
        self.tasks=[]

    def _handle_done_changed(self, completed):
        self.param.trigger("tasks")

    @param.depends("tasks")
    def _layout(self):
        self._llayout[:]=self.tasks
        return self._llayout

    @param.depends("tasks")
    def status_report(self):
        total_tasks = len(self.tasks)
        completed_tasks = sum(task.completed for task in self.tasks)
        return f"{completed_tasks} of {total_tasks} tasks completed"

    @param.depends("tasks")
    def _has_tasks(self):
        return len(self.tasks)>0

    def __panel__(self):
        task_input = TaskInput()
        pn.bind(self._add_task, task_input.param.value, watch=True)
        clear = pn.widgets.Button.from_param(
            self.param.remove_all_tasks,
            button_type="primary",
            button_style="outline",
            width=BUTTON_WIDTH,
            sizing_mode="fixed",
            visible=self._has_tasks,
        )
        return pn.Column(
            "## WTG Task List",
            self.status_report,
            task_input,
            self._layout,
            pn.Row(pn.Spacer(), clear),
            max_width=500,
        )

tasks = [
    Task(value="Inspect the blades"),
    Task(value="Inspect the nacelle"),
    Task(value="Tighten the bolts"),
]

task_list = TaskList(tasks=tasks)

task_list.servable()

@philippjfr
Copy link
Member

pre-commit.ci autofix

Copy link
Member

@philippjfr philippjfr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once again merging as-is and following up with review in my final review (hopefully over the weekend).

@philippjfr philippjfr merged commit a81daea into main Mar 15, 2024
6 checks passed
@philippjfr philippjfr deleted the enhancement/intermediate-tutorials branch March 15, 2024 12:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants