-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday27.py
222 lines (188 loc) · 10.6 KB
/
day27.py
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# First, we need to the following imports for our application.
import json
import streamlit as st
from pathlib import Path
# As for Streamlit Elements, we will need all these objects.
# All available objects and there usage are listed there: https://github.com/okld/streamlit-elements#getting-started
from streamlit_elements import elements, dashboard, mui, editor, media, lazy, sync, nivo
# Change page layout to make the dashboard take the whole page.
with st.sidebar:
st.title("#30DaysOfStreamlit")
st.header("Day 27 - Streamlit Elements")
st.write("Build a draggble and resizable dashboard with Streamlit elements.")
st.write("---")
# Define URL for media player
media_url = st.text_input(
"Media URL", value="https://www.youtube.com/watch?v=vIQQR_yq-8I"
)
# Initialize default data for code editor and chart
#
# For this tutorial, we will need data for a Nivo Bump Chart.
# You can get random data there, in tab 'data': https://nivo.rocks/bump/
#
# As you will see below, this session state item will be updated when our
# code editor is change, and it will be read by Nivo Bump chart to draw the data.
if "data" not in st.session_state:
st.session_state.data = Path("data.json").read_text()
# Define a default dashboard layout
# Dashboard grid has 12 columns by default
#
# For more information on available parameters:
# https://github.com/react-grid-layout/react-grid-layout#grid-item-props
layout = [
# Editor item is positioned in coordinates x=0 and y=0, and takes 6/12 columns and has a height of 3
dashboard.Item("editor", 0, 0, 6, 3),
# Chart item is positioned in coordinates x=6 and y=0, and takes 6/12 columns and has a height of 3
dashboard.Item("chart", 6, 0, 6, 3),
# Media item is positioned in coordinates x=0 and y=3, and takes 6/12 columns and has a height of 4
dashboard.Item("media", 0, 3, 6, 4),
]
# Create a frame to display elements
with elements("demo"):
# Create a new dashboard with the layout specified above
#
# draggableHandle is a CSS query selector to define the draggable part of each dashboard item
# Here, elements with a 'draggable' class name will be draggable
#
# For more information on available parameters for dashboard grid:
# https://github.com/react-grid-layout/react-grid-layout#grid-layout-props
# https://github.com/react-grid-layout/react-grid-layout#responsive-grid-layout-props
with dashboard.Grid(layout=layout, draggableHandle=".draggable"):
# Fist card, the code editor
#
# We use the 'key' parameter to identify the correct dashboard item.
#
# To make card's content automatically fill the height available, we will use CSS flexbox
# sx is a paramerter available with every Material UI widget to define CSS attributes
# For more information regarding Card, flexbox and sx:
# https://mui.com/components/cards/
# https://mui.com/system/flexbox/
# https://mui.com/system/the-sx-prop/
with mui.Card(key="editor", sx={"display": "flex", "flexDirection": "column"}):
# To make this header draggable, we just need to set its classname to 'draggable',
# as defined above in dashboard.Grid's draggableHandle.
mui.CardHeader(title="Editor", className="draggable")
# We want to make card's content take all the height available by setting flex CSS value to 1.
# We also want card's content to shrink when the card is shrinked by setting minHeight to 0.
with mui.CardContent(sx={"flex": 1, "minHeight": 0}): # sx: スタイリングプロパティ
# Here is our Monaco code editor.
#
# First, we set the default value to st.session_state.data that we initialized above.
# Second, we define the language to use, JSON here.
#
# Then, we want to retrieve changes made to editor's content.
# By checking Monaco documentation, there is an onChange property that takes a function.
# This function is called everytime a change is made, and the updated content value is passed in
# the first parameter (cf. onChange: https://github.com/suren-atoyan/monaco-react#props)
#
# Streamlit Elements provide a special sync() function. This function creates a callback that will
# automatically forward its parameters to Streamlit's session state items.
#
# Examples
# --------
# Create a callback that forwards its first parameter to a session state item called "data":
# >>> editor.Monaco(onChange=sync("data"))
# >>> print(st.session_state.data)
#
# Create a callback that forwards its second parameter to a session state item called "ev":
# >>> editor.Monaco(onChange=sync(None, "ev"))
# >>> print(st.session_state.ev)
#
# Create a callback that forwards both of its parameters to session state:
# >>> editor.Monaco(onChange=sync("data", "ev"))
# >>> print(st.session_state.data)
# >>> print(st.session_state.ev)
#
# Now, there is an issue: onChange is called everytime a change is made, which means everytime
# you type a single character, your entire Streamlit app will rerun.
#
# To avoid this issue, you can tell Streamlit Elements to wait for another event to occur
# (like a button click) to send the updated data, by wrapping your callback with lazy().
#
# For more information on available parameters for Monaco:
# https://github.com/suren-atoyan/monaco-react
# https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IStandaloneEditorConstructionOptions.html
editor.Monaco( # Monaco: Microsoftが開発したオープンソースのコードエディター。
defaultValue=st.session_state.data,
language="json",
onChange=lazy(sync("data")),
)
with mui.CardActions:
# Monaco editor has a lazy callback bound to onChange, which means that even if you change
# Monaco's content, Streamlit won't be notified directly, thus won't reload everytime.
# So we need another non-lazy event to trigger an update.
#
# The solution is to create a button that fires a callback on click.
# Our callback doesn't need to do anything in particular. You can either create an empty
# Python function, or use sync() with no argument.
#
# Now, everytime you will click that button, onClick callback will be fired, but every other
# lazy callbacks that changed in the meantime will also be called.
mui.Button("Apply Changes", onClick=sync())
# Second card, the Nivo Bump chart.
# We will use the same flexbox configuration as the first card to auto adjust the content height.
with mui.Card(key="chart", sx={"display": "flex", "flexDirection": "column"}):
# To make this header draggable, we just need to set its classname to 'draggable',
# as defined above in dashboard.Grid's draggableHandle.
mui.CardHeader(title="Chart", className="draggable")
# Like above, we want to make our content grow and shrink as the user resizes the card,
# by setting flex to 1 and minHeight to 0.
with mui.CardContent(sx={"flex": 1, "minHeight": 0}):
# This is where we will draw our Bump chart.
#
# For this exercise, we can just adapt Nivo's example and make it work with Streamlit Elements.
# Nivo's example is available in the 'code' tab there: https://nivo.rocks/bump/
#
# Data takes a dictionary as parameter, so we need to convert our JSON data from a string to
# a Python dictionary first, with `json.loads()`.
#
# For more information regarding other available Nivo charts:
# https://nivo.rocks/
nivo.Bump(
data=json.loads(st.session_state.data),
colors={"scheme": "spectral"},
lineWidth=3,
activeLineWidth=6,
inactiveLineWidth=3,
inactiveOpacity=0.15,
pointSize=10,
activePointSize=16,
inactivePointSize=0,
pointColor={"theme": "background"},
pointBorderWidth=3,
activePointBorderWidth=3,
pointBorderColor={"from": "serie.color"},
axisTop={
"tickSize": 5,
"tickPadding": 5,
"tickRotation": 0,
"legend": "",
"legendPosition": "middle",
"legendOffset": -36,
},
axisBottom={
"tickSize": 5,
"tickPadding": 5,
"tickRotation": 0,
"legend": "",
"legendPosition": "middle",
"legendOffset": 32,
},
axisLeft={
"tickSize": 5,
"tickPadding": 5,
"tickRotation": 0,
"legend": "ranking",
"legendPosition": "middle",
"legendOffset": -40,
},
margin={"top": 40, "right": 100, "bottom": 40, "left": 60},
axisRight=None,
)
# Third element of the dashboard, the media player
with mui.Card(key="media", sx={"display": "flex", "flexDirection": "column"}):
mui.CardHeader(title="Media Player", className="draggable")
with mui.CardContent(sx={"flex": 1, "minHeight": 0}):
# This element is powered by ReactPlayer, it supports many more players other
# than YouTube. You can check it out there: https://github.com/cookpete/react-player#props
media.Player(url=media_url, width="100%", height="100%", controls=True)