From c3ce233fa802009c00344d034e62e5ed6652cbf4 Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Wed, 24 Apr 2024 12:44:42 -0700 Subject: [PATCH 1/3] add placeholder --- examples/reference/panes/Placeholder.ipynb | 139 +++++++++++++++++++++ panel/pane/placeholder.py | 48 +++++++ panel/tests/pane/test_placeholder.py | 28 +++++ 3 files changed, 215 insertions(+) create mode 100644 examples/reference/panes/Placeholder.ipynb create mode 100644 panel/pane/placeholder.py create mode 100644 panel/tests/pane/test_placeholder.py diff --git a/examples/reference/panes/Placeholder.ipynb b/examples/reference/panes/Placeholder.ipynb new file mode 100644 index 0000000000..010f18dc37 --- /dev/null +++ b/examples/reference/panes/Placeholder.ipynb @@ -0,0 +1,139 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import asyncio\n", + "import panel as pn\n", + "\n", + "pn.extension()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `Placeholder` pane serves as a placeholder for other Panel components.\n", + "It can be used to display a message while a computation is running, for\n", + "example.\n", + "\n", + "#### Parameters:\n", + "\n", + "For details on other options for customizing the component see the [layout](../../how_to/layout/index.md) and [styling](../../how_to/styling/index.md) how-to guides.\n", + "\n", + "* **``object``** (str or object): The PNG file to display. Can be a string pointing to a local or remote file, or an object with a ``_repr_png_`` method.\n", + "\n", + "___" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `Placeholder` pane can accept any Panel component as its argument, including other panes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "placeholder = pn.pane.Placeholder(\"Hello\")\n", + "placeholder" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The benefit of using a `Placeholder` is that it allows you to replace the content of the pane without being restricted to a specific type of component. This means you can replace the placeholder with any other pane type, including plots, images, and widgets." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "placeholder.object = pn.widgets.TextInput(value=\"Hello again!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is a better approach than using a layout, like `Column`, as a placeholder for single components." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "column = pn.Column(\"Hello\")\n", + "column" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "column.objects = [pn.widgets.TextInput(value=\"Hello again!\")]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you'd like to temporarily replace the contents, you can use it as a context manager." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "placeholder = pn.pane.Placeholder(\"⏳ Idle\")\n", + "placeholder" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Upon execution of the cell below, the `Placeholder` pane will display `Starting...`, `Running...`, and `Complete!` in sequence, with a 1 second pause between each message, before finally displaying `Idle` again." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with placeholder:\n", + " placeholder.object = \"🚀 Starting...\"\n", + " await asyncio.sleep(1)\n", + " placeholder.object = \"🏃 Running...\"\n", + " await asyncio.sleep(1)\n", + " placeholder.object = \"✅ Complete!\"\n", + " await asyncio.sleep(1)" + ] + } + ], + "metadata": { + "language_info": { + "name": "python", + "pygments_lexer": "ipython3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/panel/pane/placeholder.py b/panel/pane/placeholder.py new file mode 100644 index 0000000000..84268859a9 --- /dev/null +++ b/panel/pane/placeholder.py @@ -0,0 +1,48 @@ +""" +Defines the Placeholder pane which serves as a placeholder for other Panel components. +""" + +from __future__ import annotations + +import param + +from ..pane.base import ReplacementPane + + +class Placeholder(ReplacementPane): + """ + The `Placeholder` pane serves as a placeholder for other Panel components. + It can be used to display a message while a computation is running, for + example. + + Reference: https://panel.holoviz.org/reference/panes/Placeholder.html + + :Example: + + >>> with Placeholder("⏳ Idle"): + ... placeholder.object = "🏃 Running..." + """ + + def __init__(self, object=None, **params): + super().__init__(object=object, **params) + self._past_object = object # used to restore object when Placeholder is exited + self._temporary = False + if object is not None: + self._replace_panel() + + @param.depends("object", watch=True) + def _replace_panel(self): + if not self._temporary: + self._past_object = self.object + self._update_inner(self.object) + + def __enter__(self): + self._temporary = True + return self + + def __exit__(self, exc_type, exc_value, traceback): + try: + self.object = self._past_object + finally: + self._temporary = False + return False diff --git a/panel/tests/pane/test_placeholder.py b/panel/tests/pane/test_placeholder.py new file mode 100644 index 0000000000..9f80e7c765 --- /dev/null +++ b/panel/tests/pane/test_placeholder.py @@ -0,0 +1,28 @@ +import param + +from panel.pane.placeholder import Placeholder +from panel.widgets import IntInput + + +class TestPlaceholder: + + def test_update_object(self): + placeholder = Placeholder("Idle") + placeholder.object = "Running..." + assert placeholder.object == "Running..." + placeholder.object = "New" + assert placeholder.object == "New" + + def test_enter_exit(self): + placeholder = Placeholder("⏳ Idle") + with placeholder: + placeholder.object = "🏃 Running..." + assert placeholder.object == "🏃 Running..." + placeholder.object = "🚶 Walking..." + assert placeholder.object == "🚶 Walking..." + assert placeholder.object == "⏳ Idle" + + def test_param_ref(self): + int_input = IntInput(name="IntInput", start=1, end=10, value=5) + placeholder = Placeholder(int_input.param.value) + assert isinstance(placeholder.object, param.Integer) From 4c22bb4d4fb26f9789923048db71d902bdbfcb69 Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Wed, 24 Apr 2024 12:44:59 -0700 Subject: [PATCH 2/3] Add to init --- panel/pane/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/panel/pane/__init__.py b/panel/pane/__init__.py index 877ad5963d..7b97a3a9bf 100644 --- a/panel/pane/__init__.py +++ b/panel/pane/__init__.py @@ -44,6 +44,7 @@ ) from .media import Audio, Video # noqa from .perspective import Perspective # noqa +from .placeholder import Placeholder # noqa from .plot import ( # noqa YT, Bokeh, Matplotlib, RGGPlot, ) @@ -84,6 +85,7 @@ "panel", "PDF", "Perspective", + "Placeholder", "Plotly", "PNG", "ReactiveExpr", From 73b12600b4aaf04a18d7d35aefaca68ef8a29632 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Tue, 14 May 2024 15:25:53 +0200 Subject: [PATCH 3/3] Add update method --- examples/reference/panes/Placeholder.ipynb | 30 +++++++--------------- panel/pane/placeholder.py | 10 ++++++++ 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/examples/reference/panes/Placeholder.ipynb b/examples/reference/panes/Placeholder.ipynb index 010f18dc37..ffd48271f7 100644 --- a/examples/reference/panes/Placeholder.ipynb +++ b/examples/reference/panes/Placeholder.ipynb @@ -16,15 +16,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The `Placeholder` pane serves as a placeholder for other Panel components.\n", - "It can be used to display a message while a computation is running, for\n", - "example.\n", + "The `Placeholder` pane serves as a placeholder for other Panel components. It can be used to display a message while a computation is running, for example.\n", "\n", "#### Parameters:\n", "\n", "For details on other options for customizing the component see the [layout](../../how_to/layout/index.md) and [styling](../../how_to/styling/index.md) how-to guides.\n", "\n", - "* **``object``** (str or object): The PNG file to display. Can be a string pointing to a local or remote file, or an object with a ``_repr_png_`` method.\n", + "* **`object`** (Any): The Panel object to display, if object is not already a Panel object it will be converted with the `panel(...)` function.\n", "\n", "___" ] @@ -50,7 +48,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The benefit of using a `Placeholder` is that it allows you to replace the content of the pane without being restricted to a specific type of component. This means you can replace the placeholder with any other pane type, including plots, images, and widgets." + "The benefit of using a `Placeholder` is that it allows you to replace the content of the pane without being restricted to a specific type of component. This means you can replace the placeholder with any other pane type, including plots, images, and widgets. You may either use the `update` method:" ] }, { @@ -59,14 +57,14 @@ "metadata": {}, "outputs": [], "source": [ - "placeholder.object = pn.widgets.TextInput(value=\"Hello again!\")" + "placeholder.update(pn.widgets.TextInput(value=\"Hello again!\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "This is a better approach than using a layout, like `Column`, as a placeholder for single components." + "or set the `object` directly:" ] }, { @@ -75,17 +73,7 @@ "metadata": {}, "outputs": [], "source": [ - "column = pn.Column(\"Hello\")\n", - "column" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "column.objects = [pn.widgets.TextInput(value=\"Hello again!\")]" + "placeholder.object = \"Hello once more!\"" ] }, { @@ -119,11 +107,11 @@ "outputs": [], "source": [ "with placeholder:\n", - " placeholder.object = \"🚀 Starting...\"\n", + " placeholder.update(\"🚀 Starting...\")\n", " await asyncio.sleep(1)\n", - " placeholder.object = \"🏃 Running...\"\n", + " placeholder.update(\"🏃 Running...\")\n", " await asyncio.sleep(1)\n", - " placeholder.object = \"✅ Complete!\"\n", + " placeholder.update(\"✅ Complete!\")\n", " await asyncio.sleep(1)" ] } diff --git a/panel/pane/placeholder.py b/panel/pane/placeholder.py index 84268859a9..949f49c4dc 100644 --- a/panel/pane/placeholder.py +++ b/panel/pane/placeholder.py @@ -46,3 +46,13 @@ def __exit__(self, exc_type, exc_value, traceback): finally: self._temporary = False return False + + def update(self, object): + """ + Updates the object on the Placeholder. + + Arguments + --------- + object: The object to update the Placeholder with. + """ + self.object = object