MarcSkovMadsen's picture
Insert code
9beecf3
raw
history blame
3.91 kB
import time
from concurrent.futures import ThreadPoolExecutor
from contextlib import contextmanager
import numpy as np
import panel as pn
import param
from asyncio import wrap_future
class ProgressExtMod(pn.viewable.Viewer):
"""A custom component for easy progress reporting"""
completed = param.Integer(default=0)
bar_color = param.String(default="info")
num_tasks = param.Integer(default=100, bounds=(1, None))
# @param.depends('completed', 'num_tasks')
@property
def value(self) -> int:
"""Returns the progress value
Returns:
int: The progress value
"""
return int(100 * (self.completed / self.num_tasks))
def reset(self):
"""Resets the value and message"""
# Please note the order matters as the Widgets updates two times. One for each change
self.completed = 0
def __panel__(self):
return self.view
@param.depends("completed", "bar_color")
def view(self):
"""View the widget
Returns:
pn.viewable.Viewable: Add this to your app to see the progress reported
"""
if self.value:
return pn.widgets.Progress(
active=True, value=self.value, align="center", sizing_mode="stretch_width"
)
return None
@contextmanager
def increment(self):
"""Increments the value
Can be used as context manager or decorator
Yields:
None: Nothing is yielded
"""
self.completed += 1
yield
if self.completed == self.num_tasks:
self.reset()
executor = ThreadPoolExecutor(max_workers=2) # pylint: disable=consider-using-with
progress = ProgressExtMod()
class AsyncComponent(pn.viewable.Viewer):
"""A component that demonstrates how to run a Blocking Background task asynchronously
in Panel"""
select = param.Selector(objects=range(10))
slider = param.Number(2, bounds=(0, 10))
run_blocking_task = param.Event(label="RUN")
result = param.Number(0)
view = param.Parameter()
def __init__(self, **params):
super().__init__(**params)
self._layout = pn.Column(
pn.pane.Markdown("## Blocking Task Running in Background"),
pn.Param(
self,
parameters=["run_blocking_task", "result"],
widgets={"result": {"disabled": True}, "run_blocking_task": {"button_type": "primary"}},
show_name=False,
),
progress,
pn.pane.Markdown("## Other, Non-Blocked Tasks"),
pn.Param(
self,
parameters=["select", "slider"],
widgets={"text": {"disabled": True}},
show_name=False,
),
self.text
)
def __panel__(self):
return self._layout
@param.depends("slider", "select")
def text(self):
if self.select:
select = self.select
else:
select = 0
return f"{select} + {self.slider} = {select + self.slider}"
@pn.depends("run_blocking_task", watch=True)
async def _run_blocking_tasks(self, num_tasks=10):
"""Runs background tasks num_tasks times"""
num_tasks = 20
progress.num_tasks = num_tasks
for _ in range(num_tasks):
future = executor.submit(self._run_blocking_task)
result = await wrap_future(future)
self._update(result)
@progress.increment()
def _update(self, number):
self.result += number
@staticmethod
def _run_blocking_task():
time.sleep(np.random.randint(1, 2))
return 5
if __name__.startswith("bokeh"):
pn.extension(template="fast")
pn.pane.Markdown(__doc__).servable()
AsyncComponent().servable() # pylint: disable=no-value-for-parameter