File size: 4,001 Bytes
9beecf3
 
 
ab6a63c
9beecf3
ab6a63c
9beecf3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ab6a63c
9beecf3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
934b31e
 
 
 
 
1a736db
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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 pn.state.served:
    pn.extension()
    
    component = AsyncComponent()
    pn.template.FastListTemplate(
        site="Awesome Panel", site_url="https://awesome-panel.org", title="Async Tasks", main=[component], main_layout=None, main_max_width="400px"
    ).servable()