streamlit-nightly 1.34.1.dev20240512__py2.py3-none-any.whl → 1.34.1.dev20240514__py2.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.
- streamlit/delta_generator.py +4 -4
- streamlit/elements/arrow.py +250 -3
- streamlit/elements/plotly_chart.py +4 -4
- streamlit/elements/vega_charts.py +410 -47
- streamlit/proto/ArrowVegaLiteChart_pb2.py +2 -2
- streamlit/proto/ArrowVegaLiteChart_pb2.pyi +15 -2
- streamlit/proto/Arrow_pb2.py +8 -6
- streamlit/proto/Arrow_pb2.pyi +33 -1
- streamlit/runtime/state/common.py +3 -1
- streamlit/runtime/state/widgets.py +7 -5
- streamlit/static/asset-manifest.json +7 -7
- streamlit/static/index.html +1 -1
- streamlit/static/static/css/8148.49dfd2ce.chunk.css +1 -0
- streamlit/static/static/js/1168.14f7c6ff.chunk.js +1 -0
- streamlit/static/static/js/5441.1b94928f.chunk.js +1 -0
- streamlit/static/static/js/6950.70fe55c2.chunk.js +2 -0
- streamlit/static/static/js/8148.a5f74d47.chunk.js +1 -0
- streamlit/static/static/js/{main.d6101304.js → main.32c71338.js} +2 -2
- streamlit/web/bootstrap.py +5 -9
- {streamlit_nightly-1.34.1.dev20240512.dist-info → streamlit_nightly-1.34.1.dev20240514.dist-info}/METADATA +1 -1
- {streamlit_nightly-1.34.1.dev20240512.dist-info → streamlit_nightly-1.34.1.dev20240514.dist-info}/RECORD +27 -27
- streamlit/static/static/css/3092.95a45cfe.chunk.css +0 -1
- streamlit/static/static/js/1168.7452e363.chunk.js +0 -1
- streamlit/static/static/js/1955.426e67ca.chunk.js +0 -2
- streamlit/static/static/js/3092.21bb8f7b.chunk.js +0 -1
- streamlit/static/static/js/5441.5bacdeda.chunk.js +0 -1
- /streamlit/static/static/js/{1955.426e67ca.chunk.js.LICENSE.txt → 6950.70fe55c2.chunk.js.LICENSE.txt} +0 -0
- /streamlit/static/static/js/{main.d6101304.js.LICENSE.txt → main.32c71338.js.LICENSE.txt} +0 -0
- {streamlit_nightly-1.34.1.dev20240512.data → streamlit_nightly-1.34.1.dev20240514.data}/scripts/streamlit.cmd +0 -0
- {streamlit_nightly-1.34.1.dev20240512.dist-info → streamlit_nightly-1.34.1.dev20240514.dist-info}/WHEEL +0 -0
- {streamlit_nightly-1.34.1.dev20240512.dist-info → streamlit_nightly-1.34.1.dev20240514.dist-info}/entry_points.txt +0 -0
- {streamlit_nightly-1.34.1.dev20240512.dist-info → streamlit_nightly-1.34.1.dev20240514.dist-info}/top_level.txt +0 -0
streamlit/delta_generator.py
CHANGED
@@ -587,7 +587,7 @@ class DeltaGenerator(
|
|
587
587
|
... np.random.randn(50, 20),
|
588
588
|
... columns=('col %d' % i for i in range(20)))
|
589
589
|
...
|
590
|
-
>>> my_table.
|
590
|
+
>>> my_table.add_rows(df2)
|
591
591
|
>>> # Now the table shown in the Streamlit app contains the data for
|
592
592
|
>>> # df1 followed by the data for df2.
|
593
593
|
|
@@ -596,14 +596,14 @@ class DeltaGenerator(
|
|
596
596
|
|
597
597
|
>>> # Assuming df1 and df2 from the example above still exist...
|
598
598
|
>>> my_chart = st.line_chart(df1)
|
599
|
-
>>> my_chart.
|
599
|
+
>>> my_chart.add_rows(df2)
|
600
600
|
>>> # Now the chart shown in the Streamlit app contains the data for
|
601
601
|
>>> # df1 followed by the data for df2.
|
602
602
|
|
603
603
|
And for plots whose datasets are named, you can pass the data with a
|
604
604
|
keyword argument where the key is the name:
|
605
605
|
|
606
|
-
>>> my_chart = st.
|
606
|
+
>>> my_chart = st.vega_lite_chart({
|
607
607
|
... 'mark': 'line',
|
608
608
|
... 'encoding': {'x': 'a', 'y': 'b'},
|
609
609
|
... 'datasets': {
|
@@ -611,7 +611,7 @@ class DeltaGenerator(
|
|
611
611
|
... },
|
612
612
|
... 'data': {'name': 'some_fancy_name'},
|
613
613
|
... }),
|
614
|
-
>>> my_chart.
|
614
|
+
>>> my_chart.add_rows(some_fancy_name=df2) # <-- name used as keyword
|
615
615
|
|
616
616
|
"""
|
617
617
|
if self._root_container is None or self._cursor is None:
|
streamlit/elements/arrow.py
CHANGED
@@ -14,7 +14,22 @@
|
|
14
14
|
|
15
15
|
from __future__ import annotations
|
16
16
|
|
17
|
-
|
17
|
+
import json
|
18
|
+
from dataclasses import dataclass
|
19
|
+
from typing import (
|
20
|
+
TYPE_CHECKING,
|
21
|
+
Any,
|
22
|
+
Dict,
|
23
|
+
Final,
|
24
|
+
Iterable,
|
25
|
+
List,
|
26
|
+
Literal,
|
27
|
+
Set,
|
28
|
+
TypedDict,
|
29
|
+
Union,
|
30
|
+
cast,
|
31
|
+
overload,
|
32
|
+
)
|
18
33
|
|
19
34
|
from typing_extensions import TypeAlias
|
20
35
|
|
@@ -27,9 +42,15 @@ from streamlit.elements.lib.column_config_utils import (
|
|
27
42
|
process_config_mapping,
|
28
43
|
update_column_config,
|
29
44
|
)
|
45
|
+
from streamlit.elements.lib.event_utils import AttributeDictionary
|
30
46
|
from streamlit.elements.lib.pandas_styler_utils import marshall_styler
|
47
|
+
from streamlit.errors import StreamlitAPIException
|
31
48
|
from streamlit.proto.Arrow_pb2 import Arrow as ArrowProto
|
32
49
|
from streamlit.runtime.metrics_util import gather_metrics
|
50
|
+
from streamlit.runtime.scriptrunner import get_script_run_ctx
|
51
|
+
from streamlit.runtime.state import WidgetCallback, register_widget
|
52
|
+
from streamlit.runtime.state.common import compute_widget_id
|
53
|
+
from streamlit.type_util import Key, to_key
|
33
54
|
|
34
55
|
if TYPE_CHECKING:
|
35
56
|
import pyarrow as pa
|
@@ -51,9 +72,112 @@ Data: TypeAlias = Union[
|
|
51
72
|
None,
|
52
73
|
]
|
53
74
|
|
75
|
+
SelectionMode: TypeAlias = Literal[
|
76
|
+
"single-row", "multi-row", "single-column", "multi-column"
|
77
|
+
]
|
78
|
+
_SELECTION_MODES: Final[set[SelectionMode]] = {
|
79
|
+
"single-row",
|
80
|
+
"multi-row",
|
81
|
+
"single-column",
|
82
|
+
"multi-column",
|
83
|
+
}
|
84
|
+
|
85
|
+
|
86
|
+
class DataframeSelectionState(TypedDict, total=False):
|
87
|
+
"""
|
88
|
+
A dictionary representing the current selection state of the dataframe.
|
89
|
+
|
90
|
+
Attributes
|
91
|
+
----------
|
92
|
+
rows
|
93
|
+
The selected rows (numerical indices).
|
94
|
+
columns
|
95
|
+
The selected columns (column names).
|
96
|
+
"""
|
97
|
+
|
98
|
+
rows: list[int]
|
99
|
+
columns: list[str]
|
100
|
+
|
101
|
+
|
102
|
+
class DataframeState(TypedDict, total=False):
|
103
|
+
"""
|
104
|
+
A dictionary representing the current state of the dataframe.
|
105
|
+
|
106
|
+
Attributes
|
107
|
+
----------
|
108
|
+
selection : DataframeSelectionState
|
109
|
+
The state of the `on_select` event.
|
110
|
+
"""
|
111
|
+
|
112
|
+
selection: DataframeSelectionState
|
113
|
+
|
114
|
+
|
115
|
+
@dataclass
|
116
|
+
class DataframeSelectionSerde:
|
117
|
+
"""DataframeSelectionSerde is used to serialize and deserialize the dataframe selection state."""
|
118
|
+
|
119
|
+
def deserialize(self, ui_value: str | None, widget_id: str = "") -> DataframeState:
|
120
|
+
empty_selection_state: DataframeState = {
|
121
|
+
"selection": {
|
122
|
+
"rows": [],
|
123
|
+
"columns": [],
|
124
|
+
},
|
125
|
+
}
|
126
|
+
selection_state: DataframeState = (
|
127
|
+
empty_selection_state if ui_value is None else json.loads(ui_value)
|
128
|
+
)
|
129
|
+
|
130
|
+
if "selection" not in selection_state:
|
131
|
+
selection_state = empty_selection_state
|
132
|
+
|
133
|
+
return cast(DataframeState, AttributeDictionary(selection_state))
|
134
|
+
|
135
|
+
def serialize(self, editing_state: DataframeState) -> str:
|
136
|
+
return json.dumps(editing_state, default=str)
|
137
|
+
|
138
|
+
|
139
|
+
def parse_selection_mode(
|
140
|
+
selection_mode: SelectionMode | Iterable[SelectionMode],
|
141
|
+
) -> Set[ArrowProto.SelectionMode.ValueType]:
|
142
|
+
"""Parse and check the user provided selection modes."""
|
143
|
+
if isinstance(selection_mode, str):
|
144
|
+
# Only a single selection mode was passed
|
145
|
+
selection_mode_set = {selection_mode}
|
146
|
+
else:
|
147
|
+
# Multiple selection modes were passed
|
148
|
+
selection_mode_set = set(selection_mode)
|
149
|
+
|
150
|
+
if not selection_mode_set.issubset(_SELECTION_MODES):
|
151
|
+
raise StreamlitAPIException(
|
152
|
+
f"Invalid selection mode: {selection_mode}. "
|
153
|
+
f"Valid options are: {_SELECTION_MODES}"
|
154
|
+
)
|
155
|
+
|
156
|
+
if selection_mode_set.issuperset({"single-row", "multi-row"}):
|
157
|
+
raise StreamlitAPIException(
|
158
|
+
"Only one of `single-row` or `multi-row` can be selected as selection mode."
|
159
|
+
)
|
160
|
+
|
161
|
+
if selection_mode_set.issuperset({"single-column", "multi-column"}):
|
162
|
+
raise StreamlitAPIException(
|
163
|
+
"Only one of `single-column` or `multi-column` can be selected as selection mode."
|
164
|
+
)
|
165
|
+
|
166
|
+
parsed_selection_modes = []
|
167
|
+
for selection_mode in selection_mode_set:
|
168
|
+
if selection_mode == "single-row":
|
169
|
+
parsed_selection_modes.append(ArrowProto.SelectionMode.SINGLE_ROW)
|
170
|
+
elif selection_mode == "multi-row":
|
171
|
+
parsed_selection_modes.append(ArrowProto.SelectionMode.MULTI_ROW)
|
172
|
+
elif selection_mode == "single-column":
|
173
|
+
parsed_selection_modes.append(ArrowProto.SelectionMode.SINGLE_COLUMN)
|
174
|
+
elif selection_mode == "multi-column":
|
175
|
+
parsed_selection_modes.append(ArrowProto.SelectionMode.MULTI_COLUMN)
|
176
|
+
return set(parsed_selection_modes)
|
177
|
+
|
54
178
|
|
55
179
|
class ArrowMixin:
|
56
|
-
@
|
180
|
+
@overload
|
57
181
|
def dataframe(
|
58
182
|
self,
|
59
183
|
data: Data = None,
|
@@ -64,7 +188,44 @@ class ArrowMixin:
|
|
64
188
|
hide_index: bool | None = None,
|
65
189
|
column_order: Iterable[str] | None = None,
|
66
190
|
column_config: ColumnConfigMappingInput | None = None,
|
191
|
+
key: Key | None = None,
|
192
|
+
on_select: Literal["ignore"], # No default value here to make it work with mypy
|
193
|
+
selection_mode: SelectionMode | Iterable[SelectionMode] = "multi-row",
|
67
194
|
) -> DeltaGenerator:
|
195
|
+
...
|
196
|
+
|
197
|
+
@overload
|
198
|
+
def dataframe(
|
199
|
+
self,
|
200
|
+
data: Data = None,
|
201
|
+
width: int | None = None,
|
202
|
+
height: int | None = None,
|
203
|
+
*,
|
204
|
+
use_container_width: bool = False,
|
205
|
+
hide_index: bool | None = None,
|
206
|
+
column_order: Iterable[str] | None = None,
|
207
|
+
column_config: ColumnConfigMappingInput | None = None,
|
208
|
+
key: Key | None = None,
|
209
|
+
on_select: Literal["rerun"] | WidgetCallback = "rerun",
|
210
|
+
selection_mode: SelectionMode | Iterable[SelectionMode] = "multi-row",
|
211
|
+
) -> DataframeState:
|
212
|
+
...
|
213
|
+
|
214
|
+
@gather_metrics("dataframe")
|
215
|
+
def dataframe(
|
216
|
+
self,
|
217
|
+
data: Data = None,
|
218
|
+
width: int | None = None,
|
219
|
+
height: int | None = None,
|
220
|
+
*,
|
221
|
+
use_container_width: bool = False,
|
222
|
+
hide_index: bool | None = None,
|
223
|
+
column_order: Iterable[str] | None = None,
|
224
|
+
column_config: ColumnConfigMappingInput | None = None,
|
225
|
+
key: Key | None = None,
|
226
|
+
on_select: Literal["ignore", "rerun"] | WidgetCallback = "ignore",
|
227
|
+
selection_mode: SelectionMode | Iterable[SelectionMode] = "multi-row",
|
228
|
+
) -> DeltaGenerator | DataframeState:
|
68
229
|
"""Display a dataframe as an interactive table.
|
69
230
|
|
70
231
|
This command works with dataframes from Pandas, PyArrow, Snowpark, and PySpark.
|
@@ -119,6 +280,30 @@ class ArrowMixin:
|
|
119
280
|
|
120
281
|
To configure the index column(s), use ``_index`` as the column name.
|
121
282
|
|
283
|
+
key : str
|
284
|
+
An optional string to use as the unique key for this element when used in combination
|
285
|
+
with ```on_select```. If this is omitted, a key will be generated for the widget based
|
286
|
+
on its content. Multiple widgets of the same type may not share the same key.
|
287
|
+
|
288
|
+
on_select : "ignore" or "rerun" or callable
|
289
|
+
Controls the behavior in response to selection events on the table. Can be one of:
|
290
|
+
|
291
|
+
- "ignore" (default): Streamlit will not react to any selection events in the chart.
|
292
|
+
- "rerun": Streamlit will rerun the app when the user selects rows or columns in the table.
|
293
|
+
In this case, ```st.dataframe``` will return the selection data as a dictionary.
|
294
|
+
- callable: If a callable is provided, Streamlit will rerun and execute the callable as a
|
295
|
+
callback function before the rest of the app. The selection data can be retrieved through
|
296
|
+
session state by setting the key parameter.
|
297
|
+
|
298
|
+
selection_mode : "single-row", "multi-row", single-column", "multi-column", or an iterable of these
|
299
|
+
The selection mode of the table. Can be one of:
|
300
|
+
|
301
|
+
- "multi-row" (default): Multiple rows can be selected at a time.
|
302
|
+
- "single-row": Only one row can be selected at a time.
|
303
|
+
- "multi-column": Multiple columns can be selected at a time.
|
304
|
+
- "single-column": Only one column can be selected at a time.
|
305
|
+
- An iterable of the above options: The table will allow selection based on the modes specified.
|
306
|
+
|
122
307
|
Examples
|
123
308
|
--------
|
124
309
|
>>> import streamlit as st
|
@@ -186,6 +371,29 @@ class ArrowMixin:
|
|
186
371
|
"""
|
187
372
|
import pyarrow as pa
|
188
373
|
|
374
|
+
if on_select not in ["ignore", "rerun"] and not callable(on_select):
|
375
|
+
raise StreamlitAPIException(
|
376
|
+
f"You have passed {on_select} to `on_select`. But only 'ignore', 'rerun', or a callable is supported."
|
377
|
+
)
|
378
|
+
|
379
|
+
key = to_key(key)
|
380
|
+
is_selection_activated = on_select != "ignore"
|
381
|
+
|
382
|
+
if is_selection_activated:
|
383
|
+
# Run some checks that are only relevant when selections are activated
|
384
|
+
|
385
|
+
# Import here to avoid circular imports
|
386
|
+
from streamlit.elements.utils import (
|
387
|
+
check_cache_replay_rules,
|
388
|
+
check_callback_rules,
|
389
|
+
check_session_state_rules,
|
390
|
+
)
|
391
|
+
|
392
|
+
check_cache_replay_rules()
|
393
|
+
if callable(on_select):
|
394
|
+
check_callback_rules(self.dg, on_select)
|
395
|
+
check_session_state_rules(default_value=None, key=key, writes_allowed=False)
|
396
|
+
|
189
397
|
# Convert the user provided column config into the frontend compatible format:
|
190
398
|
column_config_mapping = process_config_mapping(column_config)
|
191
399
|
|
@@ -236,7 +444,46 @@ class ArrowMixin:
|
|
236
444
|
)
|
237
445
|
marshall_column_config(proto, column_config_mapping)
|
238
446
|
|
239
|
-
|
447
|
+
if is_selection_activated:
|
448
|
+
# Import here to avoid circular imports
|
449
|
+
from streamlit.elements.form import current_form_id
|
450
|
+
|
451
|
+
# If selection events are activated, we need to register the dataframe
|
452
|
+
# element as a widget.
|
453
|
+
proto.selection_mode.extend(parse_selection_mode(selection_mode))
|
454
|
+
proto.form_id = current_form_id(self.dg)
|
455
|
+
|
456
|
+
ctx = get_script_run_ctx()
|
457
|
+
proto.id = compute_widget_id(
|
458
|
+
"dataframe",
|
459
|
+
user_key=key,
|
460
|
+
data=proto.data,
|
461
|
+
width=width,
|
462
|
+
height=height,
|
463
|
+
use_container_width=use_container_width,
|
464
|
+
column_order=proto.column_order,
|
465
|
+
column_config=proto.columns,
|
466
|
+
key=key,
|
467
|
+
selection_mode=selection_mode,
|
468
|
+
is_selection_activated=is_selection_activated,
|
469
|
+
form_id=proto.form_id,
|
470
|
+
page=ctx.page_script_hash if ctx else None,
|
471
|
+
)
|
472
|
+
|
473
|
+
serde = DataframeSelectionSerde()
|
474
|
+
widget_state = register_widget(
|
475
|
+
"dataframe",
|
476
|
+
proto,
|
477
|
+
user_key=key,
|
478
|
+
on_change_handler=on_select if callable(on_select) else None,
|
479
|
+
deserializer=serde.deserialize,
|
480
|
+
serializer=serde.serialize,
|
481
|
+
ctx=ctx,
|
482
|
+
)
|
483
|
+
self.dg._enqueue("arrow_data_frame", proto)
|
484
|
+
return cast(DataframeState, widget_state.value)
|
485
|
+
else:
|
486
|
+
return self.dg._enqueue("arrow_data_frame", proto)
|
240
487
|
|
241
488
|
@gather_metrics("table")
|
242
489
|
def table(self, data: Data = None) -> DeltaGenerator:
|
@@ -114,11 +114,11 @@ class PlotlyState(TypedDict, total=False):
|
|
114
114
|
|
115
115
|
Attributes
|
116
116
|
----------
|
117
|
-
|
117
|
+
selection : PlotlySelectionState
|
118
118
|
The state of the `on_select` event.
|
119
119
|
"""
|
120
120
|
|
121
|
-
|
121
|
+
selection: PlotlySelectionState
|
122
122
|
|
123
123
|
|
124
124
|
@dataclass
|
@@ -127,7 +127,7 @@ class PlotlyChartSelectionSerde:
|
|
127
127
|
|
128
128
|
def deserialize(self, ui_value: str | None, widget_id: str = "") -> PlotlyState:
|
129
129
|
empty_selection_state: PlotlyState = {
|
130
|
-
"
|
130
|
+
"selection": {
|
131
131
|
"points": [],
|
132
132
|
"point_indices": [],
|
133
133
|
"box": [],
|
@@ -141,7 +141,7 @@ class PlotlyChartSelectionSerde:
|
|
141
141
|
else cast(PlotlyState, AttributeDictionary(json.loads(ui_value)))
|
142
142
|
)
|
143
143
|
|
144
|
-
if "
|
144
|
+
if "selection" not in selection_state:
|
145
145
|
selection_state = empty_selection_state
|
146
146
|
|
147
147
|
return cast(PlotlyState, AttributeDictionary(selection_state))
|