streamlit-nightly 1.38.1.dev20240923__py2.py3-none-any.whl → 1.38.1.dev20240925__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/__init__.py +1 -1
- streamlit/config_option.py +2 -0
- streamlit/delta_generator.py +2 -0
- streamlit/elements/deck_gl_json_chart.py +260 -36
- streamlit/elements/widgets/audio_input.py +248 -0
- streamlit/elements/widgets/button_group.py +1 -1
- streamlit/proto/AudioInput_pb2.py +28 -0
- streamlit/proto/AudioInput_pb2.pyi +58 -0
- streamlit/proto/DeckGlJsonChart_pb2.py +4 -2
- streamlit/proto/DeckGlJsonChart_pb2.pyi +40 -2
- streamlit/proto/Element_pb2.py +4 -3
- streamlit/proto/Element_pb2.pyi +9 -4
- streamlit/runtime/state/common.py +4 -0
- streamlit/runtime/state/widgets.py +3 -1
- streamlit/static/asset-manifest.json +15 -13
- streamlit/static/index.html +1 -1
- streamlit/static/static/js/195.8e0d331c.chunk.js +2 -0
- streamlit/static/static/js/238.8d3a7d25.chunk.js +1 -0
- streamlit/static/static/js/245.f99079b1.chunk.js +1 -0
- streamlit/static/static/js/266.e595f506.chunk.js +1 -0
- streamlit/static/static/js/3710.d73e609f.chunk.js +1 -0
- streamlit/static/static/js/5281.02b3ddc4.chunk.js +1 -0
- streamlit/static/static/js/{5625.fe6c22ad.chunk.js → 5625.d9509933.chunk.js} +1 -1
- streamlit/static/static/js/{6088.c137d543.chunk.js → 6088.1164e19b.chunk.js} +1 -1
- streamlit/static/static/js/708.a5252e2f.chunk.js +1 -0
- streamlit/static/static/js/8642.58110d15.chunk.js +2 -0
- streamlit/static/static/js/{8815.0284d089.chunk.js → 8815.9d336691.chunk.js} +1 -1
- streamlit/static/static/js/9943.6af344bb.chunk.js +1 -0
- streamlit/static/static/js/main.e9d8ce9e.js +28 -0
- streamlit/web/cli.py +2 -0
- {streamlit_nightly-1.38.1.dev20240923.dist-info → streamlit_nightly-1.38.1.dev20240925.dist-info}/METADATA +1 -1
- {streamlit_nightly-1.38.1.dev20240923.dist-info → streamlit_nightly-1.38.1.dev20240925.dist-info}/RECORD +40 -35
- streamlit/static/static/js/2055.bca43613.chunk.js +0 -1
- streamlit/static/static/js/245.68a062da.chunk.js +0 -1
- streamlit/static/static/js/5180.e826dd46.chunk.js +0 -1
- streamlit/static/static/js/6789.f8dde736.chunk.js +0 -2
- streamlit/static/static/js/8485.81bdf474.chunk.js +0 -1
- streamlit/static/static/js/8642.dfef7dcb.chunk.js +0 -2
- streamlit/static/static/js/9943.d18fdff1.chunk.js +0 -1
- streamlit/static/static/js/main.829dd23b.js +0 -28
- /streamlit/static/static/css/{6789.81b3d18f.chunk.css → 195.81b3d18f.chunk.css} +0 -0
- /streamlit/static/static/js/{6789.f8dde736.chunk.js.LICENSE.txt → 195.8e0d331c.chunk.js.LICENSE.txt} +0 -0
- /streamlit/static/static/js/{8642.dfef7dcb.chunk.js.LICENSE.txt → 8642.58110d15.chunk.js.LICENSE.txt} +0 -0
- /streamlit/static/static/js/{main.829dd23b.js.LICENSE.txt → main.e9d8ce9e.js.LICENSE.txt} +0 -0
- {streamlit_nightly-1.38.1.dev20240923.data → streamlit_nightly-1.38.1.dev20240925.data}/scripts/streamlit.cmd +0 -0
- {streamlit_nightly-1.38.1.dev20240923.dist-info → streamlit_nightly-1.38.1.dev20240925.dist-info}/WHEEL +0 -0
- {streamlit_nightly-1.38.1.dev20240923.dist-info → streamlit_nightly-1.38.1.dev20240925.dist-info}/entry_points.txt +0 -0
- {streamlit_nightly-1.38.1.dev20240923.dist-info → streamlit_nightly-1.38.1.dev20240925.dist-info}/top_level.txt +0 -0
streamlit/__init__.py
CHANGED
@@ -204,7 +204,6 @@ metric = _main.metric
|
|
204
204
|
multiselect = _main.multiselect
|
205
205
|
number_input = _main.number_input
|
206
206
|
page_link = _main.page_link
|
207
|
-
pills = _main.pills
|
208
207
|
plotly_chart = _main.plotly_chart
|
209
208
|
popover = _main.popover
|
210
209
|
progress = _main.progress
|
@@ -266,6 +265,7 @@ dialog = _dialog_decorator
|
|
266
265
|
fragment = _fragment
|
267
266
|
|
268
267
|
# Experimental APIs
|
268
|
+
experimental_audio_input = _main.experimental_audio_input
|
269
269
|
experimental_dialog = _experimental_dialog_decorator
|
270
270
|
experimental_fragment = _experimental_fragment
|
271
271
|
experimental_user = _UserInfoProxy()
|
streamlit/config_option.py
CHANGED
@@ -179,6 +179,8 @@ class ConfigOption:
|
|
179
179
|
self.where_defined = ConfigOption.DEFAULT_DEFINITION
|
180
180
|
self.type = type_
|
181
181
|
self.sensitive = sensitive
|
182
|
+
# infer multiple values if the default value is a list or tuple
|
183
|
+
self.multiple = isinstance(default_val, (list, tuple))
|
182
184
|
|
183
185
|
if self.replaced_by:
|
184
186
|
self.deprecated = True
|
streamlit/delta_generator.py
CHANGED
@@ -74,6 +74,7 @@ from streamlit.elements.snow import SnowMixin
|
|
74
74
|
from streamlit.elements.text import TextMixin
|
75
75
|
from streamlit.elements.toast import ToastMixin
|
76
76
|
from streamlit.elements.vega_charts import VegaChartsMixin
|
77
|
+
from streamlit.elements.widgets.audio_input import AudioInputMixin
|
77
78
|
from streamlit.elements.widgets.button import ButtonMixin
|
78
79
|
from streamlit.elements.widgets.button_group import ButtonGroupMixin
|
79
80
|
from streamlit.elements.widgets.camera_input import CameraInputMixin
|
@@ -144,6 +145,7 @@ def _maybe_print_use_warning() -> None:
|
|
144
145
|
|
145
146
|
class DeltaGenerator(
|
146
147
|
AlertMixin,
|
148
|
+
AudioInputMixin,
|
147
149
|
BalloonsMixin,
|
148
150
|
BokehMixin,
|
149
151
|
ButtonMixin,
|
@@ -15,11 +15,35 @@
|
|
15
15
|
from __future__ import annotations
|
16
16
|
|
17
17
|
import json
|
18
|
-
from
|
18
|
+
from dataclasses import dataclass
|
19
|
+
from typing import (
|
20
|
+
TYPE_CHECKING,
|
21
|
+
Any,
|
22
|
+
Dict,
|
23
|
+
Final,
|
24
|
+
Iterable,
|
25
|
+
Literal,
|
26
|
+
Mapping,
|
27
|
+
TypedDict,
|
28
|
+
cast,
|
29
|
+
overload,
|
30
|
+
)
|
31
|
+
|
32
|
+
from typing_extensions import TypeAlias
|
19
33
|
|
20
34
|
from streamlit import config
|
35
|
+
from streamlit.elements.lib.event_utils import AttributeDictionary
|
36
|
+
from streamlit.elements.lib.form_utils import current_form_id
|
37
|
+
from streamlit.elements.lib.policies import check_widget_policies
|
38
|
+
from streamlit.elements.lib.utils import Key, compute_and_register_element_id, to_key
|
39
|
+
from streamlit.errors import StreamlitAPIException
|
21
40
|
from streamlit.proto.DeckGlJsonChart_pb2 import DeckGlJsonChart as PydeckProto
|
22
41
|
from streamlit.runtime.metrics_util import gather_metrics
|
42
|
+
from streamlit.runtime.scriptrunner_utils.script_run_context import get_script_run_ctx
|
43
|
+
from streamlit.runtime.state import (
|
44
|
+
WidgetCallback,
|
45
|
+
register_widget,
|
46
|
+
)
|
23
47
|
|
24
48
|
if TYPE_CHECKING:
|
25
49
|
from pydeck import Deck
|
@@ -32,16 +56,146 @@ EMPTY_MAP: Final[Mapping[str, Any]] = {
|
|
32
56
|
"initialViewState": {"latitude": 0, "longitude": 0, "pitch": 0, "zoom": 1},
|
33
57
|
}
|
34
58
|
|
59
|
+
SelectionMode: TypeAlias = Literal["single-object", "multi-object"]
|
60
|
+
_SELECTION_MODES: Final[set[SelectionMode]] = {
|
61
|
+
"single-object",
|
62
|
+
"multi-object",
|
63
|
+
}
|
64
|
+
|
65
|
+
|
66
|
+
def parse_selection_mode(
|
67
|
+
selection_mode: SelectionMode | Iterable[SelectionMode],
|
68
|
+
) -> set[PydeckProto.SelectionMode.ValueType]:
|
69
|
+
"""Parse and check the user provided selection modes."""
|
70
|
+
if isinstance(selection_mode, str):
|
71
|
+
# Only a single selection mode was passed
|
72
|
+
selection_mode_set = {selection_mode}
|
73
|
+
else:
|
74
|
+
# Multiple selection modes were passed.
|
75
|
+
# This is not yet supported as a functionality, but the infra is here to
|
76
|
+
# support it in the future!
|
77
|
+
# @see DeckGlJsonChart.tsx
|
78
|
+
raise StreamlitAPIException(
|
79
|
+
f"Invalid selection mode: {selection_mode}. ",
|
80
|
+
"Selection mode must be a single value, but got a set instead.",
|
81
|
+
)
|
82
|
+
|
83
|
+
if not selection_mode_set.issubset(_SELECTION_MODES):
|
84
|
+
raise StreamlitAPIException(
|
85
|
+
f"Invalid selection mode: {selection_mode}. "
|
86
|
+
f"Valid options are: {_SELECTION_MODES}"
|
87
|
+
)
|
88
|
+
|
89
|
+
if selection_mode_set.issuperset({"single-object", "multi-object"}):
|
90
|
+
raise StreamlitAPIException(
|
91
|
+
"Only one of `single-object` or `multi-object` can be selected as selection mode."
|
92
|
+
)
|
93
|
+
|
94
|
+
parsed_selection_modes = []
|
95
|
+
for selection_mode in selection_mode_set:
|
96
|
+
if selection_mode == "single-object":
|
97
|
+
parsed_selection_modes.append(PydeckProto.SelectionMode.SINGLE_OBJECT)
|
98
|
+
elif selection_mode == "multi-object":
|
99
|
+
parsed_selection_modes.append(PydeckProto.SelectionMode.MULTI_OBJECT)
|
100
|
+
return set(parsed_selection_modes)
|
101
|
+
|
102
|
+
|
103
|
+
class LayerSelectionState(TypedDict, total=False):
|
104
|
+
"""
|
105
|
+
The schema for the PyDeck Layer Selection State
|
106
|
+
|
107
|
+
Attributes
|
108
|
+
----------
|
109
|
+
indices : dict[str, list[int]]
|
110
|
+
Dictionary where keys are the layer id and values are lists of indices
|
111
|
+
of selected objects.
|
112
|
+
objects : dict[str, list[dict[str, Any]]]
|
113
|
+
Dictionary where keys are the layer id and values are lists of metadata
|
114
|
+
objects for the selected items.
|
115
|
+
"""
|
116
|
+
|
117
|
+
indices: dict[str, list[int]]
|
118
|
+
objects: dict[str, list[dict[str, Any]]]
|
119
|
+
|
120
|
+
|
121
|
+
class PydeckState(TypedDict, total=False):
|
122
|
+
"""
|
123
|
+
The schema for the PyDeck State
|
124
|
+
|
125
|
+
Attributes
|
126
|
+
----------
|
127
|
+
selection : LayerSelectionState
|
128
|
+
The selection state of the PyDeck layers.
|
129
|
+
"""
|
130
|
+
|
131
|
+
selection: LayerSelectionState
|
132
|
+
|
133
|
+
|
134
|
+
@dataclass
|
135
|
+
class PydeckSelectionSerde:
|
136
|
+
"""PydeckSelectionSerde is used to serialize and deserialize the Pydeck selection state."""
|
137
|
+
|
138
|
+
def deserialize(self, ui_value: str | None, widget_id: str = "") -> PydeckState:
|
139
|
+
empty_selection_state: PydeckState = {
|
140
|
+
"selection": {
|
141
|
+
"indices": {},
|
142
|
+
"objects": {},
|
143
|
+
}
|
144
|
+
}
|
145
|
+
|
146
|
+
selection_state = (
|
147
|
+
empty_selection_state if ui_value is None else json.loads(ui_value)
|
148
|
+
)
|
149
|
+
|
150
|
+
# We have seen some situations where the ui_value was just an empty
|
151
|
+
# dict, so we want to ensure that it always returns the empty state in
|
152
|
+
# case this happens.
|
153
|
+
if "selection" not in selection_state:
|
154
|
+
selection_state = empty_selection_state
|
155
|
+
|
156
|
+
return cast(PydeckState, AttributeDictionary(selection_state))
|
157
|
+
|
158
|
+
def serialize(self, selection_state: PydeckState) -> str:
|
159
|
+
return json.dumps(selection_state, default=str)
|
160
|
+
|
35
161
|
|
36
162
|
class PydeckMixin:
|
163
|
+
@overload
|
164
|
+
def pydeck_chart(
|
165
|
+
self,
|
166
|
+
pydeck_obj: Deck | None = None,
|
167
|
+
*,
|
168
|
+
use_container_width: bool = False,
|
169
|
+
selection_mode: Literal[
|
170
|
+
"single-object"
|
171
|
+
], # Selection mode will only be activated by on_select param, this is a default value here to make it work with mypy
|
172
|
+
on_select: Literal["ignore"], # No default value here to make it work with mypy
|
173
|
+
key: Key | None = None,
|
174
|
+
) -> DeltaGenerator: ...
|
175
|
+
|
176
|
+
@overload
|
177
|
+
def pydeck_chart(
|
178
|
+
self,
|
179
|
+
pydeck_obj: Deck | None = None,
|
180
|
+
*,
|
181
|
+
use_container_width: bool = False,
|
182
|
+
selection_mode: SelectionMode = "single-object",
|
183
|
+
on_select: Literal["rerun"] | WidgetCallback = "rerun",
|
184
|
+
key: Key | None = None,
|
185
|
+
) -> PydeckState: ...
|
186
|
+
|
37
187
|
@gather_metrics("pydeck_chart")
|
38
188
|
def pydeck_chart(
|
39
189
|
self,
|
40
190
|
pydeck_obj: Deck | None = None,
|
191
|
+
*,
|
41
192
|
use_container_width: bool = False,
|
42
193
|
width: int | None = None,
|
43
194
|
height: int | None = None,
|
44
|
-
|
195
|
+
selection_mode: SelectionMode = "single-object",
|
196
|
+
on_select: Literal["rerun", "ignore"] | WidgetCallback = "ignore",
|
197
|
+
key: Key | None = None,
|
198
|
+
) -> DeltaGenerator | PydeckState:
|
45
199
|
"""Draw a chart using the PyDeck library.
|
46
200
|
|
47
201
|
This supports 3D maps, point clouds, and more! More info about PyDeck
|
@@ -92,6 +246,37 @@ class PydeckMixin:
|
|
92
246
|
Desired height of the chart expressed in pixels. If ``height`` is
|
93
247
|
``None`` (default), Streamlit sets the height of the chart to fit
|
94
248
|
its contents according to the plotting library.
|
249
|
+
on_select : "ignore" or "rerun" or callable
|
250
|
+
How the chart should respond to user selection events. This controls
|
251
|
+
whether or not the dataframe behaves like an input widget.
|
252
|
+
``on_select`` can be one of the following:
|
253
|
+
|
254
|
+
- ``"ignore"`` (default): Streamlit will not react to any selection
|
255
|
+
events in the dataframe. The dataframe will not behave like an
|
256
|
+
input widget.
|
257
|
+
- ``"rerun"``: Rerun the script when a selection is made.
|
258
|
+
- callable: A Python callable that will be called when a selection
|
259
|
+
is made. The callable will be passed the selection state as a
|
260
|
+
dictionary.
|
261
|
+
|
262
|
+
If ``on_select`` is not ``"ignore"``, ensure that the layers given
|
263
|
+
to the ``pydeck_obj`` have stable IDs so that selection state can be
|
264
|
+
maintained across reruns.
|
265
|
+
selection_mode : "single-object" or "multi-object"
|
266
|
+
The types of selections Streamlit should allow. This can be one of
|
267
|
+
the following:
|
268
|
+
|
269
|
+
- ``"single-object"`` (default): Only one object can be selected at
|
270
|
+
a time.
|
271
|
+
- ``"multi-object"``: Multiple objects can be selected at a time.
|
272
|
+
key : str
|
273
|
+
An optional string to use for giving this element a stable
|
274
|
+
identity. If ``key`` is ``None`` (default), this element's identity
|
275
|
+
will be determined based on the values of the other parameters.
|
276
|
+
|
277
|
+
Additionally, if selections are activated and ``key`` is provided,
|
278
|
+
Streamlit will register the key in Session State to store the
|
279
|
+
selection state. The selection state is read-only.
|
95
280
|
|
96
281
|
Example
|
97
282
|
-------
|
@@ -149,9 +334,79 @@ class PydeckMixin:
|
|
149
334
|
|
150
335
|
"""
|
151
336
|
pydeck_proto = PydeckProto()
|
152
|
-
|
153
|
-
|
154
|
-
|
337
|
+
|
338
|
+
ctx = get_script_run_ctx()
|
339
|
+
|
340
|
+
if pydeck_obj is None:
|
341
|
+
spec = json.dumps(EMPTY_MAP)
|
342
|
+
else:
|
343
|
+
spec = pydeck_obj.to_json()
|
344
|
+
|
345
|
+
pydeck_proto.json = spec
|
346
|
+
pydeck_proto.use_container_width = use_container_width
|
347
|
+
|
348
|
+
if width:
|
349
|
+
pydeck_proto.width = width
|
350
|
+
if height:
|
351
|
+
pydeck_proto.height = height
|
352
|
+
|
353
|
+
tooltip = _get_pydeck_tooltip(pydeck_obj)
|
354
|
+
if tooltip:
|
355
|
+
pydeck_proto.tooltip = json.dumps(tooltip)
|
356
|
+
|
357
|
+
mapbox_token = config.get_option("mapbox.token")
|
358
|
+
if mapbox_token:
|
359
|
+
pydeck_proto.mapbox_token = mapbox_token
|
360
|
+
|
361
|
+
key = to_key(key)
|
362
|
+
is_selection_activated = on_select != "ignore"
|
363
|
+
|
364
|
+
if on_select not in ["ignore", "rerun"] and not callable(on_select):
|
365
|
+
raise StreamlitAPIException(
|
366
|
+
f"You have passed {on_select} to `on_select`. But only 'ignore', 'rerun', or a callable is supported."
|
367
|
+
)
|
368
|
+
|
369
|
+
if is_selection_activated:
|
370
|
+
# Selections are activated, treat Pydeck as a widget:
|
371
|
+
pydeck_proto.selection_mode.extend(parse_selection_mode(selection_mode))
|
372
|
+
|
373
|
+
# Run some checks that are only relevant when selections are activated
|
374
|
+
is_callback = callable(on_select)
|
375
|
+
check_widget_policies(
|
376
|
+
self.dg,
|
377
|
+
key,
|
378
|
+
on_change=cast(WidgetCallback, on_select) if is_callback else None,
|
379
|
+
default_value=None,
|
380
|
+
writes_allowed=False,
|
381
|
+
enable_check_callback_rules=is_callback,
|
382
|
+
)
|
383
|
+
pydeck_proto.form_id = current_form_id(self.dg)
|
384
|
+
|
385
|
+
pydeck_proto.id = compute_and_register_element_id(
|
386
|
+
"deck_gl_json_chart",
|
387
|
+
user_key=key,
|
388
|
+
is_selection_activated=is_selection_activated,
|
389
|
+
selection_mode=selection_mode,
|
390
|
+
use_container_width=use_container_width,
|
391
|
+
spec=spec,
|
392
|
+
form_id=pydeck_proto.form_id,
|
393
|
+
)
|
394
|
+
|
395
|
+
serde = PydeckSelectionSerde()
|
396
|
+
|
397
|
+
widget_state = register_widget(
|
398
|
+
"deck_gl_json_chart",
|
399
|
+
pydeck_proto,
|
400
|
+
ctx=ctx,
|
401
|
+
deserializer=serde.deserialize,
|
402
|
+
on_change_handler=on_select if callable(on_select) else None,
|
403
|
+
serializer=serde.serialize,
|
404
|
+
)
|
405
|
+
|
406
|
+
self.dg._enqueue("deck_gl_json_chart", pydeck_proto)
|
407
|
+
|
408
|
+
return cast(PydeckState, widget_state.value)
|
409
|
+
|
155
410
|
return self.dg._enqueue("deck_gl_json_chart", pydeck_proto)
|
156
411
|
|
157
412
|
@property
|
@@ -176,34 +431,3 @@ def _get_pydeck_tooltip(pydeck_obj: Deck | None) -> dict[str, str] | None:
|
|
176
431
|
return cast(Dict[str, str], tooltip)
|
177
432
|
|
178
433
|
return None
|
179
|
-
|
180
|
-
|
181
|
-
def marshall(
|
182
|
-
pydeck_proto: PydeckProto,
|
183
|
-
pydeck_obj: Deck | None,
|
184
|
-
use_container_width: bool,
|
185
|
-
width: int | None = None,
|
186
|
-
height: int | None = None,
|
187
|
-
) -> None:
|
188
|
-
if pydeck_obj is None:
|
189
|
-
spec = json.dumps(EMPTY_MAP)
|
190
|
-
else:
|
191
|
-
spec = pydeck_obj.to_json()
|
192
|
-
|
193
|
-
pydeck_proto.json = spec
|
194
|
-
pydeck_proto.use_container_width = use_container_width
|
195
|
-
|
196
|
-
if width:
|
197
|
-
pydeck_proto.width = width
|
198
|
-
if height:
|
199
|
-
pydeck_proto.height = height
|
200
|
-
|
201
|
-
pydeck_proto.id = ""
|
202
|
-
|
203
|
-
tooltip = _get_pydeck_tooltip(pydeck_obj)
|
204
|
-
if tooltip:
|
205
|
-
pydeck_proto.tooltip = json.dumps(tooltip)
|
206
|
-
|
207
|
-
mapbox_token = config.get_option("mapbox.token")
|
208
|
-
if mapbox_token:
|
209
|
-
pydeck_proto.mapbox_token = mapbox_token
|
@@ -0,0 +1,248 @@
|
|
1
|
+
# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
from __future__ import annotations
|
16
|
+
|
17
|
+
from dataclasses import dataclass
|
18
|
+
from textwrap import dedent
|
19
|
+
from typing import TYPE_CHECKING, Union, cast
|
20
|
+
|
21
|
+
from typing_extensions import TypeAlias
|
22
|
+
|
23
|
+
from streamlit.elements.lib.form_utils import current_form_id
|
24
|
+
from streamlit.elements.lib.policies import (
|
25
|
+
check_widget_policies,
|
26
|
+
maybe_raise_label_warnings,
|
27
|
+
)
|
28
|
+
from streamlit.elements.lib.utils import (
|
29
|
+
Key,
|
30
|
+
LabelVisibility,
|
31
|
+
compute_and_register_element_id,
|
32
|
+
get_label_visibility_proto_value,
|
33
|
+
to_key,
|
34
|
+
)
|
35
|
+
from streamlit.elements.widgets.file_uploader import _get_upload_files
|
36
|
+
from streamlit.proto.AudioInput_pb2 import AudioInput as AudioInputProto
|
37
|
+
from streamlit.proto.Common_pb2 import FileUploaderState as FileUploaderStateProto
|
38
|
+
from streamlit.proto.Common_pb2 import UploadedFileInfo as UploadedFileInfoProto
|
39
|
+
from streamlit.runtime.metrics_util import gather_metrics
|
40
|
+
from streamlit.runtime.scriptrunner import ScriptRunContext, get_script_run_ctx
|
41
|
+
from streamlit.runtime.state import (
|
42
|
+
WidgetArgs,
|
43
|
+
WidgetCallback,
|
44
|
+
WidgetKwargs,
|
45
|
+
register_widget,
|
46
|
+
)
|
47
|
+
from streamlit.runtime.uploaded_file_manager import DeletedFile, UploadedFile
|
48
|
+
|
49
|
+
if TYPE_CHECKING:
|
50
|
+
from streamlit.delta_generator import DeltaGenerator
|
51
|
+
|
52
|
+
SomeUploadedAudioFile: TypeAlias = Union[UploadedFile, DeletedFile, None]
|
53
|
+
|
54
|
+
|
55
|
+
@dataclass
|
56
|
+
class AudioInputSerde:
|
57
|
+
def serialize(
|
58
|
+
self,
|
59
|
+
audio_file: SomeUploadedAudioFile,
|
60
|
+
) -> FileUploaderStateProto:
|
61
|
+
state_proto = FileUploaderStateProto()
|
62
|
+
|
63
|
+
if audio_file is None or isinstance(audio_file, DeletedFile):
|
64
|
+
return state_proto
|
65
|
+
|
66
|
+
file_info: UploadedFileInfoProto = state_proto.uploaded_file_info.add()
|
67
|
+
file_info.file_id = audio_file.file_id
|
68
|
+
file_info.name = audio_file.name
|
69
|
+
file_info.size = audio_file.size
|
70
|
+
file_info.file_urls.CopyFrom(audio_file._file_urls)
|
71
|
+
|
72
|
+
return state_proto
|
73
|
+
|
74
|
+
def deserialize(
|
75
|
+
self, ui_value: FileUploaderStateProto | None, widget_id: str
|
76
|
+
) -> SomeUploadedAudioFile:
|
77
|
+
upload_files = _get_upload_files(ui_value)
|
78
|
+
if len(upload_files) == 0:
|
79
|
+
return_value = None
|
80
|
+
else:
|
81
|
+
return_value = upload_files[0]
|
82
|
+
return return_value
|
83
|
+
|
84
|
+
|
85
|
+
class AudioInputMixin:
|
86
|
+
@gather_metrics("experimental_audio_input")
|
87
|
+
def experimental_audio_input(
|
88
|
+
self,
|
89
|
+
label: str,
|
90
|
+
*,
|
91
|
+
key: Key | None = None,
|
92
|
+
help: str | None = None,
|
93
|
+
on_change: WidgetCallback | None = None,
|
94
|
+
args: WidgetArgs | None = None,
|
95
|
+
kwargs: WidgetKwargs | None = None,
|
96
|
+
disabled: bool = False,
|
97
|
+
label_visibility: LabelVisibility = "visible",
|
98
|
+
) -> UploadedFile | None:
|
99
|
+
r"""Display a widget that returns audio recording from the user's microphone.
|
100
|
+
|
101
|
+
Parameters
|
102
|
+
----------
|
103
|
+
label : str
|
104
|
+
A short label explaining to the user what this widget is used for.
|
105
|
+
The label can optionally contain GitHub-flavored Markdown of the
|
106
|
+
following types: Bold, Italics, Strikethroughs, Inline Code, and
|
107
|
+
Links.
|
108
|
+
|
109
|
+
Unsupported Markdown elements are unwrapped so only their children
|
110
|
+
(text contents) render. Display unsupported elements as literal
|
111
|
+
characters by backslash-escaping them. E.g.,
|
112
|
+
``"1\. Not an ordered list"``.
|
113
|
+
|
114
|
+
See the ``body`` parameter of |st.markdown|_ for additional,
|
115
|
+
supported Markdown directives.
|
116
|
+
|
117
|
+
For accessibility reasons, you should never set an empty label (label="")
|
118
|
+
but hide it with label_visibility if needed. In the future, we may disallow
|
119
|
+
empty labels by raising an exception.
|
120
|
+
|
121
|
+
.. |st.markdown| replace:: ``st.markdown``
|
122
|
+
.. _st.markdown: https://docs.streamlit.io/develop/api-reference/text/st.markdown
|
123
|
+
|
124
|
+
key : str or int
|
125
|
+
An optional string or integer to use as the unique key for the widget.
|
126
|
+
If this is omitted, a key will be generated for the widget
|
127
|
+
based on its content. Multiple widgets of the same type may
|
128
|
+
not share the same key.
|
129
|
+
|
130
|
+
help : str
|
131
|
+
A tooltip that gets displayed next to the audio input.
|
132
|
+
|
133
|
+
on_change : callable
|
134
|
+
An optional callback invoked when this audio_input's value
|
135
|
+
changes.
|
136
|
+
|
137
|
+
args : tuple
|
138
|
+
An optional tuple of args to pass to the callback.
|
139
|
+
|
140
|
+
kwargs : dict
|
141
|
+
An optional dict of kwargs to pass to the callback.
|
142
|
+
|
143
|
+
disabled : bool
|
144
|
+
An optional boolean, which disables the audio input if set to
|
145
|
+
True. Default is False.
|
146
|
+
label_visibility : "visible", "hidden", or "collapsed"
|
147
|
+
The visibility of the label. If "hidden", the label doesn't show but there
|
148
|
+
is still empty space for it above the widget (equivalent to label="").
|
149
|
+
If "collapsed", both the label and the space are removed. Default is
|
150
|
+
"visible".
|
151
|
+
|
152
|
+
Returns
|
153
|
+
-------
|
154
|
+
None or UploadedFile
|
155
|
+
The UploadedFile class is a subclass of BytesIO, and therefore
|
156
|
+
it is "file-like". This means you can pass them anywhere where
|
157
|
+
a file is expected.
|
158
|
+
|
159
|
+
Examples
|
160
|
+
--------
|
161
|
+
>>> import streamlit as st
|
162
|
+
>>>
|
163
|
+
>>> audio_value = st.experimental_audio_input("Record a voice message")
|
164
|
+
>>>
|
165
|
+
>>> if audio_value:
|
166
|
+
... st.audio(audio_value)
|
167
|
+
|
168
|
+
"""
|
169
|
+
ctx = get_script_run_ctx()
|
170
|
+
return self._audio_input(
|
171
|
+
label=label,
|
172
|
+
key=key,
|
173
|
+
help=help,
|
174
|
+
on_change=on_change,
|
175
|
+
args=args,
|
176
|
+
kwargs=kwargs,
|
177
|
+
disabled=disabled,
|
178
|
+
label_visibility=label_visibility,
|
179
|
+
ctx=ctx,
|
180
|
+
)
|
181
|
+
|
182
|
+
def _audio_input(
|
183
|
+
self,
|
184
|
+
label: str,
|
185
|
+
key: Key | None = None,
|
186
|
+
help: str | None = None,
|
187
|
+
on_change: WidgetCallback | None = None,
|
188
|
+
args: WidgetArgs | None = None,
|
189
|
+
kwargs: WidgetKwargs | None = None,
|
190
|
+
*, # keyword-only arguments:
|
191
|
+
disabled: bool = False,
|
192
|
+
label_visibility: LabelVisibility = "visible",
|
193
|
+
ctx: ScriptRunContext | None = None,
|
194
|
+
) -> UploadedFile | None:
|
195
|
+
key = to_key(key)
|
196
|
+
|
197
|
+
check_widget_policies(
|
198
|
+
self.dg,
|
199
|
+
key,
|
200
|
+
on_change,
|
201
|
+
default_value=None,
|
202
|
+
writes_allowed=False,
|
203
|
+
)
|
204
|
+
maybe_raise_label_warnings(label, label_visibility)
|
205
|
+
|
206
|
+
element_id = compute_and_register_element_id(
|
207
|
+
"audio_input",
|
208
|
+
user_key=key,
|
209
|
+
form_id=current_form_id(self.dg),
|
210
|
+
label=label,
|
211
|
+
help=help,
|
212
|
+
)
|
213
|
+
|
214
|
+
audio_input_proto = AudioInputProto()
|
215
|
+
audio_input_proto.id = element_id
|
216
|
+
audio_input_proto.label = label
|
217
|
+
audio_input_proto.form_id = current_form_id(self.dg)
|
218
|
+
audio_input_proto.disabled = disabled
|
219
|
+
audio_input_proto.label_visibility.value = get_label_visibility_proto_value(
|
220
|
+
label_visibility
|
221
|
+
)
|
222
|
+
|
223
|
+
if label and help is not None:
|
224
|
+
audio_input_proto.help = dedent(help)
|
225
|
+
|
226
|
+
serde = AudioInputSerde()
|
227
|
+
|
228
|
+
audio_input_state = register_widget(
|
229
|
+
"audio_input",
|
230
|
+
audio_input_proto,
|
231
|
+
on_change_handler=on_change,
|
232
|
+
args=args,
|
233
|
+
kwargs=kwargs,
|
234
|
+
deserializer=serde.deserialize,
|
235
|
+
serializer=serde.serialize,
|
236
|
+
ctx=ctx,
|
237
|
+
)
|
238
|
+
|
239
|
+
self.dg._enqueue("audio_input", audio_input_proto)
|
240
|
+
|
241
|
+
if isinstance(audio_input_state.value, DeletedFile):
|
242
|
+
return None
|
243
|
+
return audio_input_state.value
|
244
|
+
|
245
|
+
@property
|
246
|
+
def dg(self) -> DeltaGenerator:
|
247
|
+
"""Get our DeltaGenerator."""
|
248
|
+
return cast("DeltaGenerator", self)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
3
|
+
# source: streamlit/proto/AudioInput.proto
|
4
|
+
# Protobuf Python Version: 5.26.1
|
5
|
+
"""Generated protocol buffer code."""
|
6
|
+
from google.protobuf import descriptor as _descriptor
|
7
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
8
|
+
from google.protobuf import symbol_database as _symbol_database
|
9
|
+
from google.protobuf.internal import builder as _builder
|
10
|
+
# @@protoc_insertion_point(imports)
|
11
|
+
|
12
|
+
_sym_db = _symbol_database.Default()
|
13
|
+
|
14
|
+
|
15
|
+
from streamlit.proto import LabelVisibilityMessage_pb2 as streamlit_dot_proto_dot_LabelVisibilityMessage__pb2
|
16
|
+
|
17
|
+
|
18
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n streamlit/proto/AudioInput.proto\x1a,streamlit/proto/LabelVisibilityMessage.proto\"\x8b\x01\n\nAudioInput\x12\n\n\x02id\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x0c\n\x04help\x18\x03 \x01(\t\x12\x0f\n\x07\x66orm_id\x18\x04 \x01(\t\x12\x10\n\x08\x64isabled\x18\x05 \x01(\x08\x12\x31\n\x10label_visibility\x18\x06 \x01(\x0b\x32\x17.LabelVisibilityMessageB/\n\x1c\x63om.snowflake.apps.streamlitB\x0f\x41udioInputProtob\x06proto3')
|
19
|
+
|
20
|
+
_globals = globals()
|
21
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
22
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'streamlit.proto.AudioInput_pb2', _globals)
|
23
|
+
if not _descriptor._USE_C_DESCRIPTORS:
|
24
|
+
_globals['DESCRIPTOR']._loaded_options = None
|
25
|
+
_globals['DESCRIPTOR']._serialized_options = b'\n\034com.snowflake.apps.streamlitB\017AudioInputProto'
|
26
|
+
_globals['_AUDIOINPUT']._serialized_start=83
|
27
|
+
_globals['_AUDIOINPUT']._serialized_end=222
|
28
|
+
# @@protoc_insertion_point(module_scope)
|