st-datatables 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- st_datatables/__init__.py +139 -0
- st_datatables/example.py +237 -0
- st_datatables/frontend/build/assets/index-DHWCe3kE.js +106 -0
- st_datatables/frontend/build/assets/index-in-rVngU.css +1 -0
- st_datatables/frontend/build/bootstrap.min.css +9501 -0
- st_datatables/frontend/build/index.html +27 -0
- st_datatables/frontend/build/sample_data.csv +101 -0
- st_datatables/frontend/build/smiles1000.csv +1001 -0
- st_datatables/rdkit-example.py +194 -0
- st_datatables/sample.py +241 -0
- st_datatables-0.0.1.dist-info/METADATA +49 -0
- st_datatables-0.0.1.dist-info/RECORD +15 -0
- st_datatables-0.0.1.dist-info/WHEEL +5 -0
- st_datatables-0.0.1.dist-info/licenses/LICENSE +21 -0
- st_datatables-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import streamlit as st
|
|
4
|
+
import streamlit.components.v1 as components
|
|
5
|
+
|
|
6
|
+
_RELEASE = True
|
|
7
|
+
|
|
8
|
+
if not _RELEASE:
|
|
9
|
+
_component_func = components.declare_component(
|
|
10
|
+
"st_datatables",
|
|
11
|
+
url="http://localhost:3001",
|
|
12
|
+
)
|
|
13
|
+
else:
|
|
14
|
+
parent_dir = os.path.dirname(os.path.abspath(__file__))
|
|
15
|
+
build_dir = os.path.join(parent_dir, "frontend/build")
|
|
16
|
+
_component_func = components.declare_component("st_datatables", path=build_dir)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def st_datatables(
|
|
20
|
+
df = None,
|
|
21
|
+
id_col="ID",
|
|
22
|
+
pageLength=25,
|
|
23
|
+
lengthMenu = [10, 25, 50, 100],
|
|
24
|
+
orderable_cols=[],
|
|
25
|
+
hidden_cols = [],
|
|
26
|
+
searchable_cols=[],
|
|
27
|
+
select="single",
|
|
28
|
+
scrollX=False,
|
|
29
|
+
scrollY=False,
|
|
30
|
+
deferRender=True,
|
|
31
|
+
layout=None,
|
|
32
|
+
actions=None,
|
|
33
|
+
key=None
|
|
34
|
+
):
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
"""
|
|
38
|
+
Create a new instance of the `st_datatables` Streamlit component.
|
|
39
|
+
|
|
40
|
+
This component wraps DataTables v2 with support for selection, custom
|
|
41
|
+
action buttons, scrolling, and the modern `layout` API (e.g. colvis/csv
|
|
42
|
+
buttons). It accepts a pandas DataFrame or explicit column/data arguments.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
df : pandas.DataFrame
|
|
47
|
+
pageLength : int, default 25
|
|
48
|
+
Number of rows per page initially.
|
|
49
|
+
lengthMenu : list[int], default [10, 25, 50, 100]
|
|
50
|
+
Available options for rows per page.
|
|
51
|
+
orderable_cols : list[str], default []
|
|
52
|
+
Column names that can be sorted.
|
|
53
|
+
hidden_cols : list[str], default []
|
|
54
|
+
Column names to hide.
|
|
55
|
+
searchable_cols : list[str], default []
|
|
56
|
+
Column names that should be included in search.
|
|
57
|
+
select : {"single", "multi", False}, default "single"
|
|
58
|
+
Row selection mode: single row, multiple rows, or disabled.
|
|
59
|
+
scrollX : bool or str, default False
|
|
60
|
+
Enable horizontal scrolling, or specify a CSS width.
|
|
61
|
+
scrollY : bool or str, default False
|
|
62
|
+
Enable vertical scrolling, or specify a CSS height (e.g. "480px").
|
|
63
|
+
deferRender : bool, default True
|
|
64
|
+
Only render rows as they are displayed (improves performance with
|
|
65
|
+
images/SVGs and larger datasets).
|
|
66
|
+
layout : dict, optional
|
|
67
|
+
DataTables v2 layout configuration, passed through directly.
|
|
68
|
+
Example:
|
|
69
|
+
{
|
|
70
|
+
"top1End": { "buttons": ["colvis"] },
|
|
71
|
+
"top2End": { "buttons": ["csv"] }
|
|
72
|
+
}
|
|
73
|
+
actions : dict, optional
|
|
74
|
+
Configuration for a custom actions column. Example:
|
|
75
|
+
{
|
|
76
|
+
"insertIndex": 0,
|
|
77
|
+
"btndirection": "horizontal",
|
|
78
|
+
"buttons": [
|
|
79
|
+
{"id": "view", "title": "View", "text": "View"},
|
|
80
|
+
{"id": "delete", "title": "Delete", "svg": "<svg .../>"}
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
If used, the component will return row data with an `action` field
|
|
84
|
+
when a button is clicked.
|
|
85
|
+
"insertIndex": column number that actions column to be inserted,
|
|
86
|
+
"btndirection": Direction of action buttons. "horizontal" or "vertical",
|
|
87
|
+
|
|
88
|
+
key : str, optional
|
|
89
|
+
Streamlit widget key.
|
|
90
|
+
|
|
91
|
+
Returns
|
|
92
|
+
-------
|
|
93
|
+
dict or None
|
|
94
|
+
- On row selection:
|
|
95
|
+
{"rows": [...], "indexes": [...], "count": int}
|
|
96
|
+
- On action button click:
|
|
97
|
+
{<row data...>, "action": str, "_rowIndex": int}
|
|
98
|
+
- None if no interaction.
|
|
99
|
+
|
|
100
|
+
"""
|
|
101
|
+
columns = df.columns.tolist()
|
|
102
|
+
data = df.to_dict(orient="records")
|
|
103
|
+
|
|
104
|
+
reset_nonce = None
|
|
105
|
+
if key:
|
|
106
|
+
reset_nonce = st.session_state.get(f"{key}__reset_nonce", 0)
|
|
107
|
+
|
|
108
|
+
component_value = _component_func(
|
|
109
|
+
columns=columns,
|
|
110
|
+
data=data,
|
|
111
|
+
id_col=id_col,
|
|
112
|
+
pageLength=pageLength,
|
|
113
|
+
lengthMenu=lengthMenu,
|
|
114
|
+
orderable=orderable_cols,
|
|
115
|
+
hidden=hidden_cols,
|
|
116
|
+
searchable=searchable_cols,
|
|
117
|
+
select=select,
|
|
118
|
+
scrollX=scrollX,
|
|
119
|
+
scrollY=scrollY,
|
|
120
|
+
deferRender=deferRender,
|
|
121
|
+
layout=layout,
|
|
122
|
+
actions=actions,
|
|
123
|
+
key=key,
|
|
124
|
+
default={'rows': [], 'indexes': [], 'count': 0},
|
|
125
|
+
reset_nonce=reset_nonce,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
return component_value
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def reset_selection(key: str, *, rerun: bool = True) -> None:
|
|
132
|
+
"""
|
|
133
|
+
Signal the front-end table with given key to clear its selection.
|
|
134
|
+
This increments a nonce in session_state and (optionally) reruns immediately.
|
|
135
|
+
"""
|
|
136
|
+
nonce_key = f"{key}__reset_nonce"
|
|
137
|
+
st.session_state[nonce_key] = st.session_state.get(nonce_key, 0) + 1
|
|
138
|
+
if rerun:
|
|
139
|
+
st.rerun()
|
st_datatables/example.py
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import streamlit as st
|
|
2
|
+
|
|
3
|
+
from st_datatables import st_datatables, reset_selection
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
st.set_page_config(layout="wide")
|
|
7
|
+
|
|
8
|
+
@st.cache_data()
|
|
9
|
+
def load_data():
|
|
10
|
+
print("Data loaded")
|
|
11
|
+
df = pd.read_csv("./st_datatables/frontend/public/sample_data.csv")
|
|
12
|
+
return df
|
|
13
|
+
|
|
14
|
+
@st.cache_data
|
|
15
|
+
def load_svg():
|
|
16
|
+
print("SVG data loaded")
|
|
17
|
+
eyeSvg = '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000"><path d="M480-320q75 0 127.5-52.5T660-500q0-75-52.5-127.5T480-680q-75 0-127.5 52.5T300-500q0 75 52.5 127.5T480-320Zm0-72q-45 0-76.5-31.5T372-500q0-45 31.5-76.5T480-608q45 0 76.5 31.5T588-500q0 45-31.5 76.5T480-392Zm0 192q-146 0-266-81.5T40-500q54-137 174-218.5T480-800q146 0 266 81.5T920-500q-54 137-174 218.5T480-200Z"/></svg>'
|
|
18
|
+
editSvg = '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000"><path d="M120-120v-170l528-527q12-11 26.5-17t30.5-6q16 0 31 6t26 18l55 56q12 11 17.5 26t5.5 30q0 16-5.5 30.5T817-647L290-120H120Zm584-528 56-56-56-56-56 56 56 56Z"/></svg>'
|
|
19
|
+
delsvg ='<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000"><path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm80-160h80v-360h-80v360Zm160 0h80v-360h-80v360Z"/></svg>'
|
|
20
|
+
return eyeSvg, editSvg, delsvg
|
|
21
|
+
|
|
22
|
+
def on_detail_dismiss():
|
|
23
|
+
reset_selection("table1", rerun=False)
|
|
24
|
+
|
|
25
|
+
def on_detail_dismiss2():
|
|
26
|
+
print("on_detail_dismiss2 is running")
|
|
27
|
+
st.session_state["detail_open"] = False
|
|
28
|
+
st.session_state["detail_row"] = None
|
|
29
|
+
st.session_state["edit_open"] = False
|
|
30
|
+
st.session_state["edit_row"] = None
|
|
31
|
+
|
|
32
|
+
reset_selection("buttons_table", rerun=False)
|
|
33
|
+
if "buttons_table" in st.session_state:
|
|
34
|
+
del st.session_state["buttons_table"]
|
|
35
|
+
|
|
36
|
+
@st.dialog("Detail Dialog",width="large", on_dismiss=on_detail_dismiss)
|
|
37
|
+
def show_detail_dialog(row: dict):
|
|
38
|
+
st.image(row.get("structure_svg"), width=200)
|
|
39
|
+
st.write(row.get("ID"))
|
|
40
|
+
st.write(row.get("NAME"))
|
|
41
|
+
st.write(row.get("shape"))
|
|
42
|
+
st.write(row.get("person"))
|
|
43
|
+
st.write(row.get("created_at"))
|
|
44
|
+
del st.session_state["table1"]
|
|
45
|
+
|
|
46
|
+
@st.dialog("Detail Dialog",width="large", on_dismiss=on_detail_dismiss2)
|
|
47
|
+
def show_detail_dialog2(row: dict):
|
|
48
|
+
st.image(row.get("structure_svg"), width=200)
|
|
49
|
+
st.write(row.get("ID"))
|
|
50
|
+
st.write(row.get("NAME"))
|
|
51
|
+
st.write(row.get("shape"))
|
|
52
|
+
st.write(row.get("person"))
|
|
53
|
+
st.write(row.get("created_at"))
|
|
54
|
+
|
|
55
|
+
@st.dialog("Edit Dialog",width="large", on_dismiss=on_detail_dismiss2)
|
|
56
|
+
def show_edit_dialog(row: dict):
|
|
57
|
+
with st.form("form"):
|
|
58
|
+
st.text_input("ID",value=row.get("ID"))
|
|
59
|
+
st.text_input("Name",value=row.get("NAME"))
|
|
60
|
+
st.text_input("Shape",value=row.get("shape"))
|
|
61
|
+
st.text_input("Person",value=row.get("person"))
|
|
62
|
+
st.date_input("Date",value=row.get("created_at"))
|
|
63
|
+
submit = st.form_submit_button("submit")
|
|
64
|
+
if submit:
|
|
65
|
+
print("submitted")
|
|
66
|
+
# st.success("Updated")
|
|
67
|
+
st.session_state["edit_open"] = False
|
|
68
|
+
st.session_state["edit_row"] = None
|
|
69
|
+
# return
|
|
70
|
+
# st.rerun()
|
|
71
|
+
st.stop()
|
|
72
|
+
|
|
73
|
+
df = load_data()
|
|
74
|
+
eyeSvg, editSvg,delsvg = load_svg()
|
|
75
|
+
|
|
76
|
+
TABS = ["SingleSelectTable", "MultiSelectTable", "TableWithButtons"]
|
|
77
|
+
active = st.segmented_control(
|
|
78
|
+
"Menu",
|
|
79
|
+
options=TABS,
|
|
80
|
+
selection_mode="single",
|
|
81
|
+
default=TABS[0],
|
|
82
|
+
key="active_tab",
|
|
83
|
+
)
|
|
84
|
+
st.divider()
|
|
85
|
+
print("Edit Dialog:",(st.session_state.get("edit_open") and st.session_state.get("edit_row")))
|
|
86
|
+
|
|
87
|
+
if "table1" not in st.session_state:
|
|
88
|
+
st.session_state.table1 = {'rows': [], 'indexes': [], 'count': 0}
|
|
89
|
+
if "selected" not in st.session_state:
|
|
90
|
+
st.session_state.selected = {'rows': [], 'indexes': [], 'count': 0}
|
|
91
|
+
if st.session_state.get("detail_open") and st.session_state.get("detail_row"):
|
|
92
|
+
show_detail_dialog2(st.session_state["detail_row"])
|
|
93
|
+
elif st.session_state.get("edit_open") and st.session_state.get("edit_row"):
|
|
94
|
+
show_edit_dialog(st.session_state["edit_row"])
|
|
95
|
+
|
|
96
|
+
# --------------------------Single Row Select Table
|
|
97
|
+
if active == "SingleSelectTable":
|
|
98
|
+
st.subheader("Single select Table")
|
|
99
|
+
selected_row = st_datatables(
|
|
100
|
+
df=df,
|
|
101
|
+
pageLength=10,
|
|
102
|
+
lengthMenu=[10,25,50,100],
|
|
103
|
+
orderable_cols=["ID","NAME","created_at","person"],
|
|
104
|
+
searchable_cols=["ID","NAME","person"],
|
|
105
|
+
select="single",
|
|
106
|
+
scrollX=False,
|
|
107
|
+
scrollY="500",
|
|
108
|
+
deferRender=True,
|
|
109
|
+
layout= {
|
|
110
|
+
"top1End": {"buttons": ["colvis"],},
|
|
111
|
+
},
|
|
112
|
+
key="table1"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
st.write(selected_row)
|
|
116
|
+
if selected_row["count"] > 0:
|
|
117
|
+
show_detail_dialog(selected_row["rows"][0])
|
|
118
|
+
|
|
119
|
+
# --------------------------Multi Row Select Table
|
|
120
|
+
elif active == "MultiSelectTable":
|
|
121
|
+
st.subheader("Multiselect Table")
|
|
122
|
+
|
|
123
|
+
if st.button("Reset Row Selection"):
|
|
124
|
+
reset_selection("multiselect_table")
|
|
125
|
+
|
|
126
|
+
selected_row2 = st_datatables(
|
|
127
|
+
df=df,
|
|
128
|
+
pageLength=10,
|
|
129
|
+
lengthMenu=[10,25,50,100],
|
|
130
|
+
orderable_cols=["ID","NAME"],
|
|
131
|
+
searchable_cols=["ID","NAME"],
|
|
132
|
+
select="multi",
|
|
133
|
+
scrollX=False,
|
|
134
|
+
scrollY="500",
|
|
135
|
+
deferRender=True,
|
|
136
|
+
layout= {
|
|
137
|
+
"top1End": {"buttons": ["colvis","copy","csv"],},
|
|
138
|
+
},
|
|
139
|
+
key="multiselect_table"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
if selected_row2 != 0 and selected_row2["count"] > 0:
|
|
143
|
+
indexes = selected_row2['indexes']
|
|
144
|
+
st.write(f"Selected rows : {indexes}")
|
|
145
|
+
|
|
146
|
+
st.write(selected_row2)
|
|
147
|
+
|
|
148
|
+
# --------------------------Table With Action Buttons
|
|
149
|
+
elif active == "TableWithButtons":
|
|
150
|
+
st.subheader("Table with Action button")
|
|
151
|
+
|
|
152
|
+
actions = {
|
|
153
|
+
"insertIndex": 0,
|
|
154
|
+
"btndirection": "vertical",
|
|
155
|
+
"buttons": [
|
|
156
|
+
{"id": "detail", "title": "Detail", "text": "Detail","className": "detail-btn", "svg": eyeSvg},
|
|
157
|
+
{"id": "edit", "title": "Edit", "text": "Edit","className": "edit-btn", "svg": editSvg},
|
|
158
|
+
{"id": "delete", "title": "Delete", "text": "Delete", "className": "delete-btn", "svg": delsvg},
|
|
159
|
+
],
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
selected_row3 = st_datatables(
|
|
163
|
+
df=df,
|
|
164
|
+
pageLength=10,
|
|
165
|
+
lengthMenu=[10,25,50,100],
|
|
166
|
+
orderable_cols=["ID","NAME"],
|
|
167
|
+
searchable_cols=["ID","NAME"],
|
|
168
|
+
select=False,
|
|
169
|
+
scrollX=False,
|
|
170
|
+
scrollY="500",
|
|
171
|
+
deferRender=True,
|
|
172
|
+
layout= {
|
|
173
|
+
"top1End": {"buttons": ["colvis"],},
|
|
174
|
+
},
|
|
175
|
+
actions=actions,
|
|
176
|
+
key="buttons_table"
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
print("------------------------")
|
|
180
|
+
print(not st.session_state.get("detail_open"))
|
|
181
|
+
print(not st.session_state.get("edit_open"))
|
|
182
|
+
print(selected_row3)
|
|
183
|
+
print(isinstance(selected_row3, dict))
|
|
184
|
+
print("action" in selected_row3)
|
|
185
|
+
print("------------------------")
|
|
186
|
+
|
|
187
|
+
if (not st.session_state.get("detail_open")) and (not st.session_state.get("edit_open")) \
|
|
188
|
+
and selected_row3 and isinstance(selected_row3, dict) \
|
|
189
|
+
and ("action" in selected_row3):
|
|
190
|
+
action = selected_row3["action"]
|
|
191
|
+
row_index = selected_row3.get("_rowIndex")
|
|
192
|
+
st.write(f"{action} button of row {row_index} clicked!")
|
|
193
|
+
|
|
194
|
+
if action == "detail":
|
|
195
|
+
st.session_state["detail_row"] = selected_row3
|
|
196
|
+
st.session_state["detail_open"] = True
|
|
197
|
+
st.rerun()
|
|
198
|
+
|
|
199
|
+
elif action == "edit":
|
|
200
|
+
# show_edit_dialog(selected_row3)
|
|
201
|
+
st.session_state["edit_row"] = selected_row3
|
|
202
|
+
st.session_state["edit_open"] = True
|
|
203
|
+
st.rerun()
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
st.write(selected_row3)
|
|
207
|
+
|
|
208
|
+
st.divider()
|
|
209
|
+
st.subheader("Horizontal Button Layout")
|
|
210
|
+
actions_without_text = {
|
|
211
|
+
"insertIndex": 6,
|
|
212
|
+
"btndirection": "horizontal",
|
|
213
|
+
"buttons": [
|
|
214
|
+
{"id": "detail", "title": "Detail","className": "detail-btn", "svg":eyeSvg},
|
|
215
|
+
{"id": "edit", "title": "Edit", "className": "edit-btn", "svg": editSvg},
|
|
216
|
+
{"id": "delete", "title": "Delete", "className": "delete-btn", "svg": delsvg},
|
|
217
|
+
],
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
selected_row4 = st_datatables(
|
|
221
|
+
df=df,
|
|
222
|
+
pageLength=10,
|
|
223
|
+
lengthMenu=[10,25,50,100],
|
|
224
|
+
orderable_cols=["ID","NAME"],
|
|
225
|
+
searchable_cols=["ID","NAME"],
|
|
226
|
+
select=False,
|
|
227
|
+
scrollX=False,
|
|
228
|
+
scrollY="500",
|
|
229
|
+
deferRender=True,
|
|
230
|
+
layout= {
|
|
231
|
+
"top1End": {"buttons": ["colvis"],},
|
|
232
|
+
},
|
|
233
|
+
actions=actions_without_text,
|
|
234
|
+
key="buttons_table_without_text"
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
st.write(selected_row4)
|