MarcSkovMadsen commited on
Commit
ece1a60
1 Parent(s): 83c2d47

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +175 -119
app.py CHANGED
@@ -1,134 +1,190 @@
1
- import time
2
- from concurrent.futures import ThreadPoolExecutor
3
- from contextlib import contextmanager
4
-
5
- import numpy as np
 
 
 
6
  import panel as pn
7
  import param
8
- from asyncio import wrap_future
9
-
10
- class ProgressExtMod(pn.viewable.Viewer):
11
- """A custom component for easy progress reporting"""
12
-
13
- completed = param.Integer(default=0)
14
- bar_color = param.String(default="info")
15
- num_tasks = param.Integer(default=100, bounds=(1, None))
16
-
17
- # @param.depends('completed', 'num_tasks')
18
- @property
19
- def value(self) -> int:
20
- """Returns the progress value
21
-
22
- Returns:
23
- int: The progress value
24
- """
25
- return int(100 * (self.completed / self.num_tasks))
26
-
27
- def reset(self):
28
- """Resets the value and message"""
29
- # Please note the order matters as the Widgets updates two times. One for each change
30
- self.completed = 0
31
-
32
- def __panel__(self):
33
- return self.view
34
-
35
- @param.depends("completed", "bar_color")
36
- def view(self):
37
- """View the widget
38
- Returns:
39
- pn.viewable.Viewable: Add this to your app to see the progress reported
40
- """
41
- if self.value:
42
- return pn.widgets.Progress(
43
- active=True, value=self.value, align="center", sizing_mode="stretch_width"
44
- )
45
- return None
46
-
47
- @contextmanager
48
- def increment(self):
49
- """Increments the value
50
-
51
- Can be used as context manager or decorator
52
-
53
- Yields:
54
- None: Nothing is yielded
55
- """
56
- self.completed += 1
57
- yield
58
- if self.completed == self.num_tasks:
59
- self.reset()
60
-
61
- executor = ThreadPoolExecutor(max_workers=2) # pylint: disable=consider-using-with
62
- progress = ProgressExtMod()
63
-
64
-
65
- class AsyncComponent(pn.viewable.Viewer):
66
- """A component that demonstrates how to run a Blocking Background task asynchronously
67
- in Panel"""
68
-
69
- select = param.Selector(objects=range(10))
70
- slider = param.Number(2, bounds=(0, 10))
71
-
72
- run_blocking_task = param.Event(label="RUN")
73
- result = param.Number(0)
74
- view = param.Parameter()
75
 
76
  def __init__(self, **params):
77
  super().__init__(**params)
78
 
79
- self._layout = pn.Column(
80
- pn.pane.Markdown("## Blocking Task Running in Background"),
81
- pn.Param(
82
- self,
83
- parameters=["run_blocking_task", "result"],
84
- widgets={"result": {"disabled": True}, "run_blocking_task": {"button_type": "primary"}},
85
- show_name=False,
 
 
 
 
 
 
 
 
 
 
 
 
86
  ),
87
- progress,
88
- pn.pane.Markdown("## Other, Non-Blocked Tasks"),
 
 
 
 
 
 
 
 
 
 
 
89
  pn.Param(
90
  self,
91
- parameters=["select", "slider"],
92
- widgets={"text": {"disabled": True}},
 
 
 
 
 
 
 
 
 
93
  show_name=False,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  ),
95
- self.text
 
 
 
 
 
 
 
 
96
  )
97
 
98
- def __panel__(self):
99
- return self._layout
100
-
101
- @param.depends("slider", "select")
102
- def text(self):
103
- if self.select:
104
- select = self.select
105
- else:
106
- select = 0
107
- return f"{select} + {self.slider} = {select + self.slider}"
108
-
109
- @pn.depends("run_blocking_task", watch=True)
110
- async def _run_blocking_tasks(self, num_tasks=10):
111
- """Runs background tasks num_tasks times"""
112
- num_tasks = 20
113
- progress.num_tasks = num_tasks
114
- for _ in range(num_tasks):
115
- future = executor.submit(self._run_blocking_task)
116
- result = await wrap_future(future)
117
- self._update(result)
118
-
119
- @progress.increment()
120
- def _update(self, number):
121
- self.result += number
122
-
123
- @staticmethod
124
- def _run_blocking_task():
125
- time.sleep(np.random.randint(1, 2))
126
- return 5
127
 
128
  if pn.state.served:
129
- pn.extension()
130
-
131
- component = AsyncComponent()
132
- pn.template.FastListTemplate(
133
- site="Awesome Panel", site_url="https://awesome-panel.org", title="Async Tasks", main=[component], main_layout=None, main_max_width="400px"
134
- ).servable()
 
1
+ """
2
+ See https://awesome-panel.org/resources/classic_dashboard/
3
+ """
4
+ import pathlib
5
+
6
+ import holoviews as hv
7
+ import hvplot.pandas # pylint: disable=unused-import
8
+ import pandas as pd
9
  import panel as pn
10
  import param
11
+ from holoviews.plotting.util import process_cmap
12
+
13
+ DASHBOARD_A_PATH="https://cdn.awesome-panel.org/resources/classic_dashboard/dashboard_A.csv"
14
+ DASHBOARD_B_PATH="https://cdn.awesome-panel.org/resources/classic_dashboard/dashboard_B.csv"
15
+
16
+ COLOR_MAPS = hv.plotting.util.list_cmaps()
17
+ STYLE = """
18
+ body {
19
+ margin: 0px;
20
+ min-height: 100vh;
21
+ overflow-x: hidden;
22
+ width: 100%;
23
+ background: #f2f2f2;
24
+ }
25
+ .bk.app-body {
26
+ background: #f2f2f2;
27
+ color: #000000;
28
+ font-family: roboto, sans-serif, Verdana;
29
+ }
30
+ .bk.app-bar {
31
+ background: #212121;
32
+ border-color: white;
33
+ box-shadow: 5px 5px 20px #9E9E9E;
34
+ color: #ffffff;
35
+ z-index: 50;
36
+ }
37
+ .bk.app-container {
38
+ background: #ffffff;
39
+ border-radius: 5px;
40
+ box-shadow: 2px 2px 2px lightgrey;
41
+ color: #000000;
42
+ }
43
+
44
+ .bk.app-settings {
45
+ background: #e0e0e0;
46
+ color: #000000;
47
+ }
48
+
49
+ """
50
+
51
+ DATA_A = pd.read_csv(DASHBOARD_A_PATH, index_col=0)
52
+ DATA_B = pd.read_csv(DASHBOARD_B_PATH, index_col=0)
53
+
54
+
55
+ class Dashboard(pn.viewable.Viewer):
56
+ """This application show cases how to build a Classic Dashboard
57
+ in Panel.
58
+ """
59
+
60
+ tool = param.ObjectSelector(label="Tool", default="S1_1", objects=["S1_1", "S2_1"])
61
+ variable = param.ObjectSelector(
62
+ label="Variable",
63
+ default="Cut Distance",
64
+ objects=["Cut Distance", "Removed Volume", "Av. uncut chip thickness"],
65
+ )
66
+ color_map = param.ObjectSelector(default="rainbow", objects=COLOR_MAPS)
67
+
68
+ view = param.ClassSelector(class_=pn.Column)
 
 
 
 
 
 
 
 
 
69
 
70
  def __init__(self, **params):
71
  super().__init__(**params)
72
 
73
+ self.insert_plot_pane = pn.pane.HoloViews(
74
+ self._get_insert_plot, sizing_mode="stretch_width", margin=10, height=300
75
+ )
76
+ self.edge_plot_pane = pn.pane.HoloViews(
77
+ self._get_edge_plot, sizing_mode="stretch_width", margin=10, height=300
78
+ )
79
+ self.history_plot_pane = pn.pane.HoloViews(
80
+ self._update_history_plot, sizing_mode="stretch_width", margin=10, height=300
81
+ )
82
+ self.view = pn.Column(sizing_mode="stretch_both")
83
+ self._init_view()
84
+
85
+ def _init_view(self):
86
+ appbar = pn.Row(
87
+ pn.pane.Markdown(
88
+ "#### Classic Dashboard in Panel ",
89
+ margin=(15, 5, 5, 25),
90
+ sizing_mode="stretch_width",
91
+ align="center",
92
  ),
93
+ pn.layout.HSpacer(height=0),
94
+ pn.pane.PNG(
95
+ "https://panel.holoviz.org/_static/logo_horizontal.png",
96
+ width=200,
97
+ align="center",
98
+ sizing_mode="fixed",
99
+ margin=(10, 50, 10, 5),
100
+ embed=False,
101
+ ),
102
+ sizing_mode="stretch_width",
103
+ css_classes=["app-bar"],
104
+ )
105
+ settings_bar = pn.Column(
106
  pn.Param(
107
  self,
108
+ parameters=["tool", "variable", "color_map"],
109
+ widgets={
110
+ "tool": {"align": "center", "width": 75, "sizing_mode": "fixed"},
111
+ "variable": {
112
+ "type": pn.widgets.RadioBoxGroup,
113
+ "inline": True,
114
+ "align": "end",
115
+ "sizing_mode": "stretch_width",
116
+ },
117
+ },
118
+ default_layout=pn.Row,
119
  show_name=False,
120
+ sizing_mode="stretch_width",
121
+ ),
122
+ pn.layout.HSpacer(height=0),
123
+ sizing_mode="stretch_width",
124
+ css_classes=["app-container"],
125
+ margin=(50, 25, 25, 25),
126
+ )
127
+
128
+ self.view[:] = [ # pylint: disable=unsupported-assignment-operation
129
+ pn.Column(
130
+ appbar,
131
+ settings_bar,
132
+ pn.Row(
133
+ pn.Column(
134
+ self.insert_plot_pane,
135
+ css_classes=["app-container"],
136
+ margin=25,
137
+ sizing_mode="stretch_both",
138
+ ),
139
+ pn.Column(
140
+ self.edge_plot_pane,
141
+ css_classes=["app-container"],
142
+ margin=25,
143
+ sizing_mode="stretch_both",
144
+ ),
145
+ sizing_mode="stretch_both",
146
+ min_height=300,
147
+ ),
148
+ pn.Row(
149
+ self.history_plot_pane,
150
+ css_classes=["app-container"],
151
+ margin=25,
152
+ min_height=300,
153
+ sizing_mode="stretch_both",
154
+ ),
155
+ css_classes=["app-body"],
156
+ sizing_mode="stretch_both",
157
  ),
158
+ pn.layout.HSpacer(height=25),
159
+ ]
160
+
161
+ @pn.depends("tool", "variable", "color_map")
162
+ def _get_insert_plot(self):
163
+ plot_data = DATA_A.loc[self.tool]
164
+ data = [(plot_data["Xo"], plot_data["Yo"], plot_data[self.variable])]
165
+ return hv.Path(data, vdims=self.variable).opts(
166
+ cmap=self.color_map, color=self.variable, line_width=4, colorbar=True, responsive=True
167
  )
168
 
169
+ @pn.depends("tool", "variable", "color_map")
170
+ def _get_edge_plot(self):
171
+ plot_data = DATA_A.loc[self.tool]
172
+ return plot_data.hvplot(
173
+ x="Number", y=self.variable, kind="area", alpha=0.6, color=self._color, responsive=True
174
+ )
175
+
176
+ @pn.depends("tool", "color_map")
177
+ def _update_history_plot(self):
178
+ plot_data = DATA_B.loc[self.tool]
179
+ return plot_data.hvplot(
180
+ x="Cut Distance", y="Feed", kind="line", line_width=4, responsive=True
181
+ ).opts(color=self._color)
182
+
183
+ @property
184
+ def _color(self):
185
+ return process_cmap(self.color_map, 1)[0]
186
+
 
 
 
 
 
 
 
 
 
 
 
187
 
188
  if pn.state.served:
189
+ pn.extension(raw_css=[STYLE], design="bootstrap")
190
+ Dashboard().view.servable()