Skip to content

Commit 8c6a498

Browse files
author
xhlulu
committed
Add explorer app + change demo ID to be unique
1 parent be64c53 commit 8c6a498

File tree

2 files changed

+234
-2
lines changed

2 files changed

+234
-2
lines changed

demos/explorer.py

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
from importlib import import_module
2+
from inspect import getsource
3+
from copy import deepcopy
4+
import json
5+
import os
6+
7+
import dash
8+
import dash_core_components as dcc
9+
import dash_html_components as html
10+
from dash.dependencies import Input, Output, State
11+
import dash_bootstrap_components as dbc
12+
13+
14+
def prepend_recursive(component, prefix: str) -> None:
15+
"""in-place modifications"""
16+
if hasattr(component, "id"):
17+
if type(component.id) == str:
18+
component.id = prefix + component.id
19+
elif type(component.id) == dict:
20+
key = "type"
21+
if key in component.id:
22+
component.id[key] = prefix + component.id[key]
23+
24+
if hasattr(component, "children") and component.children is not None:
25+
for child in component.children:
26+
prepend_recursive(child, prefix)
27+
28+
29+
def prepend_list_of_dict(ls: list, prefix: str) -> list:
30+
new_ls = []
31+
32+
for di in ls:
33+
di = deepcopy(di)
34+
try: # is a dictionary
35+
di_id = json.loads(di["id"])
36+
key = "type"
37+
if key in di_id:
38+
di_id[key] = prefix + di_id[key]
39+
40+
di["id"] = json.dumps(di_id).replace(" ", "")
41+
42+
except ValueError: # is a string
43+
di["id"] = prefix + di["id"]
44+
45+
new_ls.append(di)
46+
return new_ls
47+
48+
49+
def prepend_callback_map(di: dict, prefix: str) -> dict:
50+
new_di = {}
51+
for k, v in di.items():
52+
v = deepcopy(v)
53+
v["inputs"] = prepend_list_of_dict(v["inputs"], prefix)
54+
v["state"] = prepend_list_of_dict(v["state"], prefix)
55+
new_di[prefix + k] = v
56+
57+
return new_di
58+
59+
60+
def prepend_callback_list(ls: list, prefix: str) -> list:
61+
new_ls = []
62+
for di in ls:
63+
di = deepcopy(di)
64+
if type(di['output']) == list:
65+
di["output"] = prepend_list_of_dict(di["output"], prefix)
66+
else:
67+
di["output"] = prefix + di["output"]
68+
di["inputs"] = prepend_list_of_dict(di["inputs"], prefix)
69+
di["state"] = prepend_list_of_dict(di["state"], prefix)
70+
71+
new_ls.append(di)
72+
73+
return new_ls
74+
75+
76+
def Header(name, app):
77+
title = html.H2(name, style={"display": "inline-block"})
78+
logo = html.Img(
79+
src=app.get_asset_url("dash-logo.png"),
80+
style={"height": 60},
81+
)
82+
link = html.A(logo, href="https://plotly.com/dash/", target="_blank")
83+
84+
return html.Div([link, title])
85+
86+
87+
def display_demo(name, layout, code):
88+
download_btn = html.A(
89+
html.Button(
90+
"Download",
91+
style={
92+
"width": "90px",
93+
"margin": "auto",
94+
"padding": "0px",
95+
"font-size": "10px",
96+
"height": "35px",
97+
"border-radius": "2px",
98+
},
99+
),
100+
href=app.get_asset_url(name + ".py"),
101+
download="app.py",
102+
style={"position": "absolute", "top": "1.5em", "right": "1.5em"},
103+
)
104+
return html.Div(
105+
[
106+
html.Div(
107+
[
108+
download_btn,
109+
dcc.Markdown(f"```\n{code}\n```"),
110+
],
111+
style={
112+
"float": "left",
113+
"width": "49%",
114+
"height": "85vh",
115+
"overflow-y": "auto",
116+
"position": "relative",
117+
"background-color": "#F7FAFC",
118+
"border": "1px solid #A1ACC3",
119+
"border-right": "none",
120+
},
121+
),
122+
html.Div(
123+
layout,
124+
style={
125+
"float": "left",
126+
"width": "48%",
127+
"padding": "5px 1% 5px 1%",
128+
"height": "calc(85vh - 10px)",
129+
"overflow-y": "auto",
130+
"border": "1px solid #A1ACC3",
131+
},
132+
),
133+
]
134+
)
135+
136+
137+
prefix_ignored = [
138+
139+
]
140+
141+
ignored_pages = [
142+
'assets',
143+
'data'
144+
]
145+
146+
147+
app = dash.Dash(__name__, suppress_callback_exceptions=True, external_stylesheets=[dbc.themes.BOOTSTRAP])
148+
server = app.server
149+
150+
app_subdomain = os.getenv("APP_SUBDOMAIN", "dash-vtk-explorer")
151+
152+
pages = [p for p in sorted(os.listdir()) if os.path.isdir(p) and p not in ignored_pages]
153+
modules = {p: import_module(f"{p}.app") for p in pages}
154+
apps = {p: m.app for p, m in modules.items()}
155+
source_codes = {p: getsource(m) for p, m in modules.items()}
156+
notfound_404 = html.Div(
157+
[
158+
html.H1("404"),
159+
"Webpage not found. Please contact us if a page is supposed to be here.",
160+
]
161+
)
162+
163+
app.layout = dbc.Container(
164+
[
165+
# dbc.Row([
166+
# dbc.Col(
167+
# Header("Dash VTK Explorer", app),
168+
# width=8
169+
# ),
170+
# dbc.Col(
171+
# dcc.Dropdown(
172+
# id="app-choice",
173+
# placeholder="Please select an app...",
174+
# style={"width": "100%"},
175+
# options=[{"label": x, "value": x} for x in pages],
176+
# ),
177+
# width=4
178+
# )
179+
# ]),
180+
# html.Hr(),
181+
dcc.Location(id="url", refresh=False),
182+
html.Div(id="display"),
183+
],
184+
fluid=True
185+
)
186+
187+
for k in apps:
188+
new_callback_map = apps[k].callback_map
189+
new_callback_list = apps[k]._callback_list
190+
191+
# Prepend to layout IDs recursively in-place
192+
# if k in prefix_ignored:
193+
# new_callback_map = apps[k].callback_map
194+
# new_callback_list = apps[k]._callback_list
195+
# else:
196+
# prepend_recursive(apps[k].layout, prefix=k + "-")
197+
# new_callback_map = prepend_callback_map(apps[k].callback_map, prefix=k + "-")
198+
# new_callback_list = prepend_callback_list(apps[k]._callback_list, prefix=k + "-")
199+
200+
app.callback_map.update(new_callback_map)
201+
app._callback_list.extend(new_callback_list)
202+
203+
204+
@app.callback(Output("url", "pathname"), Input("app-choice", "value"))
205+
def update_url(name):
206+
if name is None:
207+
return dash.no_update
208+
return f"/{app_subdomain}/{name}"
209+
210+
211+
@app.callback(Output("display", "children"), [Input("url", "pathname")])
212+
def display_content(pathname):
213+
if app_subdomain in pathname:
214+
name = pathname.split("/")[-1]
215+
216+
if name == "":
217+
return html.H6("Please select an app from the dropdown")
218+
219+
elif name in pages:
220+
# return display_demo(
221+
# name=name, layout=apps[name].layout, code=source_codes[name]
222+
# )
223+
return apps[name].layout
224+
225+
else:
226+
return notfound_404
227+
228+
return dash.no_update
229+
230+
231+
if __name__ == "__main__":
232+
app.run_server(debug=True)

demos/usage-algorithm/app.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
server = app.server
1414

1515
vtk_view = dash_vtk.View(
16-
id="vtk-view",
16+
id="geometry-view",
1717
children=[
1818
dash_vtk.GeometryRepresentation(
1919
[
@@ -77,7 +77,7 @@
7777

7878

7979
@app.callback(
80-
[Output("vtk-algorithm", "state"), Output("vtk-view", "triggerResetCamera")],
80+
[Output("vtk-algorithm", "state"), Output("geometry-view", "triggerResetCamera")],
8181
[Input("slider-resolution", "value"), Input("capping-checklist", "value")],
8282
)
8383
def update_cone(slider_val, checked_values):

0 commit comments

Comments
 (0)