Lazily load Tab content, and keep it dynamically updated

Here, I want to grab code from the first tab’s code editor, execute it, and display the results on the second tab, but I don’t want it to execute eagerly until the user clicks on it.

Solution 1

Use pn.param.ParamFunction(func, lazy=True) alongside pn.Tabs(dynamic=True)

import time
import panel as pn
from panel.io.mime_render import exec_with_return

pn.extension("codeeditor")


async def execute():
    print("EXECUTING NOW...")
    yield pn.indicators.LoadingSpinner(value=True, align="center", width=50, height=50)
    time.sleep(1.28)
    result = exec_with_return(code_editor.value)
    yield result

code_editor = pn.widgets.CodeEditor(value="'Hello, World!'", language="python")
result = pn.param.ParamFunction(execute, lazy=True)
tabs = pn.Tabs(("Python Code", code_editor), ("Result", result), dynamic=True)
tabs

1st_demo

However, this only runs once meaning if you update the code editor, the second tab will not reflect the new code.

Solution 2

Use pn.bind and key on active, with a cache if you want more efficiency.

import time
import panel as pn

from panel.io.mime_render import exec_with_return

pn.extension("codeeditor")


def execute(code, active):
    if active != 1:
        return placeholder_spinner.clone()
    
    if code in pn.state.cache:
        result = pn.state.cache[code]
    else:
        time.sleep(1.28)  # Simulate a long running computation
        result = pn.state.cache[code] = exec_with_return(code)
    return result


code_editor = pn.widgets.CodeEditor(value="'Hello, World!'", language="python")
placeholder_spinner = pn.indicators.LoadingSpinner(
    value=True, align="center", width=50, height=50
)
result = pn.Column()
tabs = pn.Tabs(("Python Code", code_editor), ("Result", result))
result.objects = [pn.bind(execute, code_editor.param.value, tabs.param.active)]
tabs

Now if you update the code, the second tab will also update accordingly!

demo

Credits to Philipp for explaining this.

3 Likes