streamlit-nightly 1.33.1.dev20240426__py2.py3-none-any.whl → 1.33.1.dev20240429__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.
Files changed (30) hide show
  1. streamlit/elements/lib/event_utils.py +37 -0
  2. streamlit/elements/plotly_chart.py +266 -100
  3. streamlit/proto/PlotlyChart_pb2.py +7 -5
  4. streamlit/proto/PlotlyChart_pb2.pyi +61 -17
  5. streamlit/runtime/state/common.py +4 -2
  6. streamlit/runtime/state/widgets.py +1 -0
  7. streamlit/static/asset-manifest.json +15 -15
  8. streamlit/static/index.html +1 -1
  9. streamlit/static/static/css/{main.88b8fc58.css → main.3aaaea00.css} +1 -1
  10. streamlit/static/static/js/1168.fc5c673b.chunk.js +1 -0
  11. streamlit/static/static/js/{3092.d8143d1d.chunk.js → 3092.bc07c48b.chunk.js} +1 -1
  12. streamlit/static/static/js/{43.a48ac7b4.chunk.js → 43.c6749504.chunk.js} +1 -1
  13. streamlit/static/static/js/{4666.492dcf72.chunk.js → 4666.c4b22a63.chunk.js} +1 -1
  14. streamlit/static/static/js/{6013.8e80e091.chunk.js → 6013.64cd6d28.chunk.js} +1 -1
  15. streamlit/static/static/js/{6853.d999ac75.chunk.js → 6853.3cbd385e.chunk.js} +1 -1
  16. streamlit/static/static/js/{7602.6175e969.chunk.js → 7602.e8abc06b.chunk.js} +1 -1
  17. streamlit/static/static/js/{8427.88677af8.chunk.js → 8427.65ddaf36.chunk.js} +1 -1
  18. streamlit/static/static/js/{8477.e948c092.chunk.js → 8477.7419a0aa.chunk.js} +1 -1
  19. streamlit/static/static/js/{8492.f56c9d4c.chunk.js → 8492.3e609489.chunk.js} +1 -1
  20. streamlit/static/static/js/{8536.74dc408e.chunk.js → 8536.f13dff49.chunk.js} +1 -1
  21. streamlit/static/static/js/main.af77b7ba.js +2 -0
  22. {streamlit_nightly-1.33.1.dev20240426.dist-info → streamlit_nightly-1.33.1.dev20240429.dist-info}/METADATA +1 -1
  23. {streamlit_nightly-1.33.1.dev20240426.dist-info → streamlit_nightly-1.33.1.dev20240429.dist-info}/RECORD +28 -27
  24. streamlit/static/static/js/1168.1d6408e6.chunk.js +0 -1
  25. streamlit/static/static/js/main.eccc579f.js +0 -2
  26. /streamlit/static/static/js/{main.eccc579f.js.LICENSE.txt → main.af77b7ba.js.LICENSE.txt} +0 -0
  27. {streamlit_nightly-1.33.1.dev20240426.data → streamlit_nightly-1.33.1.dev20240429.data}/scripts/streamlit.cmd +0 -0
  28. {streamlit_nightly-1.33.1.dev20240426.dist-info → streamlit_nightly-1.33.1.dev20240429.dist-info}/WHEEL +0 -0
  29. {streamlit_nightly-1.33.1.dev20240426.dist-info → streamlit_nightly-1.33.1.dev20240429.dist-info}/entry_points.txt +0 -0
  30. {streamlit_nightly-1.33.1.dev20240426.dist-info → streamlit_nightly-1.33.1.dev20240429.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,37 @@
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 typing import Any, Dict
16
+
17
+
18
+ class AttributeDictionary(Dict[Any, Any]):
19
+ """
20
+ A dictionary subclass that supports attribute-style access.
21
+
22
+ This class extends the functionality of a standard dictionary to allow items to be accessed
23
+ via attribute-style dot notation in addition to the traditional key-based access. If a dictionary
24
+ item is accessed and is itself a dictionary, it is automatically wrapped in another `AttributeDictionary`,
25
+ enabling recursive attribute-style access.
26
+ """
27
+
28
+ def __getattr__(self, key):
29
+ try:
30
+ item = self.__getitem__(key)
31
+ return AttributeDictionary(item) if isinstance(item, dict) else item
32
+ except KeyError as err:
33
+ raise AttributeError(
34
+ f"'{type(self).__name__}' object has no attribute '{key}'"
35
+ ) from err
36
+
37
+ __setattr__ = dict.__setitem__
@@ -17,19 +17,37 @@
17
17
  from __future__ import annotations
18
18
 
19
19
  import json
20
- import urllib.parse
21
- from typing import TYPE_CHECKING, Any, Dict, List, Literal, Union, cast
20
+ from dataclasses import dataclass
21
+ from typing import (
22
+ TYPE_CHECKING,
23
+ Any,
24
+ Dict,
25
+ Final,
26
+ Iterable,
27
+ List,
28
+ Literal,
29
+ TypedDict,
30
+ Union,
31
+ cast,
32
+ overload,
33
+ )
22
34
 
23
35
  from typing_extensions import TypeAlias
24
36
 
25
37
  from streamlit import type_util
38
+ from streamlit.deprecation_util import show_deprecation_warning
39
+ from streamlit.elements.form import current_form_id
40
+ from streamlit.elements.lib.event_utils import AttributeDictionary
26
41
  from streamlit.elements.lib.streamlit_plotly_theme import (
27
42
  configure_streamlit_plotly_theme,
28
43
  )
29
44
  from streamlit.errors import StreamlitAPIException
30
45
  from streamlit.proto.PlotlyChart_pb2 import PlotlyChart as PlotlyChartProto
31
- from streamlit.runtime.legacy_caching import caching
32
46
  from streamlit.runtime.metrics_util import gather_metrics
47
+ from streamlit.runtime.scriptrunner import get_script_run_ctx
48
+ from streamlit.runtime.state import WidgetCallback, register_widget
49
+ from streamlit.runtime.state.common import compute_widget_id
50
+ from streamlit.type_util import Key, to_key
33
51
 
34
52
  if TYPE_CHECKING:
35
53
  import matplotlib
@@ -41,19 +59,6 @@ if TYPE_CHECKING:
41
59
  # We need to configure the Plotly theme before any Plotly figures are created:
42
60
  configure_streamlit_plotly_theme()
43
61
 
44
- SharingMode: TypeAlias = Literal["streamlit", "private", "public", "secret"]
45
-
46
- SHARING_MODES: set[SharingMode] = {
47
- # This means the plot will be sent to the Streamlit app rather than to
48
- # Plotly.
49
- "streamlit",
50
- # The three modes below are for plots that should be hosted in Plotly.
51
- # These are the names Plotly uses for them.
52
- "private",
53
- "public",
54
- "secret",
55
- }
56
-
57
62
  _AtomicFigureOrData: TypeAlias = Union[
58
63
  "go.Figure",
59
64
  "go.Data",
@@ -69,17 +74,154 @@ FigureOrData: TypeAlias = Union[
69
74
  "matplotlib.figure.Figure",
70
75
  ]
71
76
 
77
+ SelectionMode: TypeAlias = Literal["lasso", "points", "box"]
78
+ _SELECTION_MODES: Final[set[SelectionMode]] = {"lasso", "points", "box"}
79
+
80
+
81
+ class PlotlySelectionState(TypedDict, total=False):
82
+ """
83
+ A dictionary representing the current selection state of the plotly chart.
84
+
85
+ Attributes
86
+ ----------
87
+ points : list[dict[str, Any]]
88
+ The selected data points in the chart, this also includes
89
+ the the data points selected by the box and lasso mode.
90
+
91
+ point_indices : list[int]
92
+ The numerical indices of all selected data points in the chart,
93
+ this also includes the indices of the points selected by the box
94
+ and lasso mode.
95
+
96
+ box : list[dict[str, Any]]
97
+ The metadata related to the box selection. This includes the
98
+ coordinates of the selected area.
99
+
100
+ lasso : list[dict[str, Any]]
101
+ The metadata related to the lasso selection. This includes the
102
+ coordinates of the selected area.
103
+ """
104
+
105
+ points: list[dict[str, Any]]
106
+ point_indices: list[int]
107
+ box: list[dict[str, Any]]
108
+ lasso: list[dict[str, Any]]
109
+
110
+
111
+ class PlotlyState(TypedDict, total=False):
112
+ """
113
+ A dictionary representing the current selection state of the plotly chart.
114
+
115
+ Attributes
116
+ ----------
117
+ select : PlotlySelectionState
118
+ The state of the `on_select` event.
119
+ """
120
+
121
+ select: PlotlySelectionState
122
+
123
+
124
+ @dataclass
125
+ class PlotlyChartSelectionSerde:
126
+ """PlotlyChartSelectionSerde is used to serialize and deserialize the Plotly Chart selection state."""
127
+
128
+ def deserialize(self, ui_value: str | None, widget_id: str = "") -> PlotlyState:
129
+ empty_selection_state: PlotlyState = {
130
+ "select": {
131
+ "points": [],
132
+ "point_indices": [],
133
+ "box": [],
134
+ "lasso": [],
135
+ },
136
+ }
137
+
138
+ selection_state = (
139
+ empty_selection_state
140
+ if ui_value is None
141
+ else cast(PlotlyState, AttributeDictionary(json.loads(ui_value)))
142
+ )
143
+
144
+ if "select" not in selection_state:
145
+ selection_state = empty_selection_state
146
+
147
+ return cast(PlotlyState, AttributeDictionary(selection_state))
148
+
149
+ def serialize(self, selection_state: PlotlyState) -> str:
150
+ return json.dumps(selection_state, default=str)
151
+
152
+
153
+ def parse_selection_mode(
154
+ selection_mode: SelectionMode | Iterable[SelectionMode],
155
+ ) -> set[PlotlyChartProto.SelectionMode.ValueType]:
156
+ """Parse and check the user provided selection modes."""
157
+ if isinstance(selection_mode, str):
158
+ # Only a single selection mode was passed
159
+ selection_mode_set = {selection_mode}
160
+ else:
161
+ # Multiple selection modes were passed
162
+ selection_mode_set = set(selection_mode)
163
+
164
+ if not selection_mode_set.issubset(_SELECTION_MODES):
165
+ raise StreamlitAPIException(
166
+ f"Invalid selection mode: {selection_mode}. "
167
+ f"Valid options are: {_SELECTION_MODES}"
168
+ )
169
+
170
+ parsed_selection_modes = []
171
+ for selection_mode in selection_mode_set:
172
+ if selection_mode == "points":
173
+ parsed_selection_modes.append(PlotlyChartProto.SelectionMode.POINTS)
174
+ elif selection_mode == "lasso":
175
+ parsed_selection_modes.append(PlotlyChartProto.SelectionMode.LASSO)
176
+ elif selection_mode == "box":
177
+ parsed_selection_modes.append(PlotlyChartProto.SelectionMode.BOX)
178
+ return set(parsed_selection_modes)
179
+
72
180
 
73
181
  class PlotlyMixin:
74
- @gather_metrics("plotly_chart")
182
+ @overload
75
183
  def plotly_chart(
76
184
  self,
77
185
  figure_or_data: FigureOrData,
78
186
  use_container_width: bool = False,
79
- sharing: SharingMode = "streamlit",
187
+ *,
80
188
  theme: Literal["streamlit"] | None = "streamlit",
189
+ key: Key | None = None,
190
+ on_select: Literal["ignore"], # No default value here to make it work with mypy
191
+ selection_mode: SelectionMode
192
+ | Iterable[SelectionMode] = ("points", "box", "lasso"),
81
193
  **kwargs: Any,
82
194
  ) -> DeltaGenerator:
195
+ ...
196
+
197
+ @overload
198
+ def plotly_chart(
199
+ self,
200
+ figure_or_data: FigureOrData,
201
+ use_container_width: bool = False,
202
+ *,
203
+ theme: Literal["streamlit"] | None = "streamlit",
204
+ key: Key | None = None,
205
+ on_select: Literal["rerun"] | WidgetCallback = "rerun",
206
+ selection_mode: SelectionMode
207
+ | Iterable[SelectionMode] = ("points", "box", "lasso"),
208
+ **kwargs: Any,
209
+ ) -> PlotlyState:
210
+ ...
211
+
212
+ @gather_metrics("plotly_chart")
213
+ def plotly_chart(
214
+ self,
215
+ figure_or_data: FigureOrData,
216
+ use_container_width: bool = False,
217
+ *,
218
+ theme: Literal["streamlit"] | None = "streamlit",
219
+ key: Key | None = None,
220
+ on_select: Literal["rerun", "ignore"] | WidgetCallback = "ignore",
221
+ selection_mode: SelectionMode
222
+ | Iterable[SelectionMode] = ("points", "box", "lasso"),
223
+ **kwargs: Any,
224
+ ) -> DeltaGenerator | PlotlyState:
83
225
  """Display an interactive Plotly chart.
84
226
 
85
227
  Plotly is a charting library for Python. The arguments to this function
@@ -100,16 +242,33 @@ class PlotlyMixin:
100
242
  If True, set the chart width to the column width. This takes
101
243
  precedence over the figure's native `width` value.
102
244
 
103
- sharing : "streamlit", "private", "secret", or "public"
104
- Use "streamlit" to insert the plot and all its dependencies
105
- directly in the Streamlit app using plotly's offline mode (default).
106
- Use any other sharing mode to send the chart to Plotly chart studio, which
107
- requires an account. See https://plot.ly/python/chart-studio/ for more information.
108
-
109
245
  theme : "streamlit" or None
110
246
  The theme of the chart. Currently, we only support "streamlit" for the Streamlit
111
247
  defined design or None to fallback to the default behavior of the library.
112
248
 
249
+ key : str
250
+ An optional string to use as the unique key for this element when used in combination
251
+ with ```on_select```. If this is omitted, a key will be generated for the widget based
252
+ on its content. Multiple widgets of the same type may not share the same key.
253
+
254
+ on_select : "ignore" or "rerun" or callable
255
+ Controls the behavior in response to selection events on the charts. Can be one of:
256
+ - "ignore" (default): Streamlit will not react to any selection events in the chart.
257
+ - "rerun: Streamlit will rerun the app when the user selects data in the chart. In this case,
258
+ ```st.plotly_chart``` will return the selection data as a dictionary.
259
+ - callable: If a callable is provided, Streamlit will rerun and execute the callable as a
260
+ callback function before the rest of the app. The selection data can be retrieved through
261
+ session state by setting the key parameter.
262
+
263
+ selection_mode : "points", "box", "lasso" or an iterable of these
264
+ The selection mode of the table. Can be one of:
265
+ - "points": The chart will allow selections based on individual data points.
266
+ - "box": The chart will allow selections based on rectangular areas.
267
+ - "lasso": The chart will allow selections based on freeform areas.
268
+ - An iterable of the above options: The chart will allow selections based on the modes specified.
269
+
270
+ All selections modes are activated by default.
271
+
113
272
  **kwargs
114
273
  Any argument accepted by Plotly's `plot()` function.
115
274
 
@@ -144,104 +303,111 @@ class PlotlyMixin:
144
303
  height: 400px
145
304
 
146
305
  """
306
+ import plotly.io
307
+ import plotly.tools
308
+
147
309
  # NOTE: "figure_or_data" is the name used in Plotly's .plot() method
148
310
  # for their main parameter. I don't like the name, but it's best to
149
311
  # keep it in sync with what Plotly calls it.
150
312
 
151
- plotly_chart_proto = PlotlyChartProto()
152
- if theme != "streamlit" and theme != None:
313
+ if "sharing" in kwargs:
314
+ show_deprecation_warning(
315
+ "The `sharing` parameter has been deprecated and will be removed in a future release. "
316
+ "Plotly charts will always be rendered using Streamlit's offline mode."
317
+ )
318
+
319
+ if theme not in ["streamlit", None]:
153
320
  raise StreamlitAPIException(
154
321
  f'You set theme="{theme}" while Streamlit charts only support theme=”streamlit” or theme=None to fallback to the default library theme.'
155
322
  )
156
- marshall(
157
- plotly_chart_proto,
158
- figure_or_data,
159
- use_container_width,
160
- sharing,
161
- theme,
162
- **kwargs,
163
- )
164
- return self.dg._enqueue("plotly_chart", plotly_chart_proto)
165
-
166
- @property
167
- def dg(self) -> DeltaGenerator:
168
- """Get our DeltaGenerator."""
169
- return cast("DeltaGenerator", self)
170
-
171
-
172
- def marshall(
173
- proto: PlotlyChartProto,
174
- figure_or_data: FigureOrData,
175
- use_container_width: bool,
176
- sharing: SharingMode,
177
- theme: Literal["streamlit"] | None,
178
- **kwargs: Any,
179
- ) -> None:
180
- """Marshall a proto with a Plotly spec.
181
-
182
- See DeltaGenerator.plotly_chart for docs.
183
- """
184
- # NOTE: "figure_or_data" is the name used in Plotly's .plot() method
185
- # for their main parameter. I don't like the name, but its best to keep
186
- # it in sync with what Plotly calls it.
187
323
 
188
- import plotly.tools
324
+ if on_select not in ["ignore", "rerun"] and not callable(on_select):
325
+ raise StreamlitAPIException(
326
+ f"You have passed {on_select} to `on_select`. But only 'ignore', 'rerun', or a callable is supported."
327
+ )
189
328
 
190
- if type_util.is_type(figure_or_data, "matplotlib.figure.Figure"):
191
- figure = plotly.tools.mpl_to_plotly(figure_or_data)
329
+ key = to_key(key)
330
+ is_selection_activated = on_select != "ignore"
192
331
 
193
- else:
194
- figure = plotly.tools.return_figure_from_figure_or_data(
195
- figure_or_data, validate_figure=True
196
- )
332
+ if is_selection_activated:
333
+ # Run some checks that are only relevant when selections are activated
197
334
 
198
- if not isinstance(sharing, str) or sharing.lower() not in SHARING_MODES:
199
- raise ValueError("Invalid sharing mode for Plotly chart: %s" % sharing)
335
+ # Import here to avoid circular imports
336
+ from streamlit.elements.utils import (
337
+ check_cache_replay_rules,
338
+ check_callback_rules,
339
+ check_session_state_rules,
340
+ )
200
341
 
201
- proto.use_container_width = use_container_width
342
+ check_cache_replay_rules()
343
+ if callable(on_select):
344
+ check_callback_rules(self.dg, on_select)
345
+ check_session_state_rules(default_value=None, key=key, writes_allowed=False)
346
+
347
+ if type_util.is_type(figure_or_data, "matplotlib.figure.Figure"):
348
+ # Convert matplotlib figure to plotly figure:
349
+ figure = plotly.tools.mpl_to_plotly(figure_or_data)
350
+ else:
351
+ figure = plotly.tools.return_figure_from_figure_or_data(
352
+ figure_or_data, validate_figure=True
353
+ )
202
354
 
203
- if sharing == "streamlit":
204
- import plotly.io
355
+ plotly_chart_proto = PlotlyChartProto()
356
+ plotly_chart_proto.use_container_width = use_container_width
357
+ plotly_chart_proto.theme = theme or ""
358
+ plotly_chart_proto.form_id = current_form_id(self.dg)
205
359
 
206
360
  config = dict(kwargs.get("config", {}))
207
361
  # Copy over some kwargs to config dict. Plotly does the same in plot().
208
362
  config.setdefault("showLink", kwargs.get("show_link", False))
209
363
  config.setdefault("linkText", kwargs.get("link_text", False))
210
364
 
211
- proto.figure.spec = plotly.io.to_json(figure, validate=False)
212
- proto.figure.config = json.dumps(config)
213
-
214
- else:
215
- url = _plot_to_url_or_load_cached_url(
216
- figure, sharing=sharing, auto_open=False, **kwargs
365
+ plotly_chart_proto.spec = plotly.io.to_json(figure, validate=False)
366
+ plotly_chart_proto.config = json.dumps(config)
367
+
368
+ ctx = get_script_run_ctx()
369
+
370
+ # We are computing the widget id for all plotly uses
371
+ # to also allow non-widget Plotly charts to keep their state
372
+ # when the frontend component gets unmounted and remounted.
373
+ plotly_chart_proto.id = compute_widget_id(
374
+ "plotly_chart",
375
+ user_key=key,
376
+ key=key,
377
+ plotly_spec=plotly_chart_proto.spec,
378
+ plotly_config=plotly_chart_proto.config,
379
+ selection_mode=selection_mode,
380
+ is_selection_activated=is_selection_activated,
381
+ theme=theme,
382
+ form_id=plotly_chart_proto.form_id,
383
+ use_container_width=use_container_width,
384
+ page=ctx.page_script_hash if ctx else None,
217
385
  )
218
- proto.url = _get_embed_url(url)
219
- proto.theme = theme or ""
220
386
 
387
+ if is_selection_activated:
388
+ # Selections are activated, treat plotly chart as a widget:
389
+ plotly_chart_proto.selection_mode.extend(
390
+ parse_selection_mode(selection_mode)
391
+ )
221
392
 
222
- @caching.cache
223
- def _plot_to_url_or_load_cached_url(*args: Any, **kwargs: Any) -> go.Figure:
224
- """Call plotly.plot wrapped in st.cache.
225
-
226
- This is so we don't unnecessarily upload data to Plotly's SASS if nothing
227
- changed since the previous upload.
228
- """
229
- try:
230
- # Plotly 4 changed its main package.
231
- import chart_studio.plotly as ply
232
- except ImportError:
233
- import plotly.plotly as ply
234
-
235
- return ply.plot(*args, **kwargs)
236
-
393
+ serde = PlotlyChartSelectionSerde()
237
394
 
238
- def _get_embed_url(url: str) -> str:
239
- parsed_url = urllib.parse.urlparse(url)
395
+ widget_state = register_widget(
396
+ "plotly_chart",
397
+ plotly_chart_proto,
398
+ user_key=key,
399
+ on_change_handler=on_select if callable(on_select) else None,
400
+ deserializer=serde.deserialize,
401
+ serializer=serde.serialize,
402
+ ctx=ctx,
403
+ )
240
404
 
241
- # Plotly's embed URL is the normal URL plus ".embed".
242
- # (Note that our use namedtuple._replace is fine because that's not a
243
- # private method! It just has an underscore to avoid clashing with the
244
- # tuple field names)
245
- parsed_embed_url = parsed_url._replace(path=parsed_url.path + ".embed")
405
+ self.dg._enqueue("plotly_chart", plotly_chart_proto)
406
+ return cast(PlotlyState, widget_state.value)
407
+ else:
408
+ return self.dg._enqueue("plotly_chart", plotly_chart_proto)
246
409
 
247
- return urllib.parse.urlunparse(parsed_embed_url)
410
+ @property
411
+ def dg(self) -> DeltaGenerator:
412
+ """Get our DeltaGenerator."""
413
+ return cast("DeltaGenerator", self)
@@ -13,7 +13,7 @@ _sym_db = _symbol_database.Default()
13
13
 
14
14
 
15
15
 
16
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!streamlit/proto/PlotlyChart.proto\"x\n\x0bPlotlyChart\x12\r\n\x03url\x18\x01 \x01(\tH\x00\x12\x19\n\x06\x66igure\x18\x02 \x01(\x0b\x32\x07.FigureH\x00\x12\x1b\n\x13use_container_width\x18\x05 \x01(\x08\x12\r\n\x05theme\x18\x06 \x01(\tB\x07\n\x05\x63hartJ\x04\x08\x03\x10\x04J\x04\x08\x04\x10\x05\"&\n\x06\x46igure\x12\x0c\n\x04spec\x18\x01 \x01(\t\x12\x0e\n\x06\x63onfig\x18\x02 \x01(\tB0\n\x1c\x63om.snowflake.apps.streamlitB\x10PlotlyChartProtob\x06proto3')
16
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!streamlit/proto/PlotlyChart.proto\"\x98\x02\n\x0bPlotlyChart\x12\x1b\n\x13use_container_width\x18\x05 \x01(\x08\x12\r\n\x05theme\x18\x06 \x01(\t\x12\n\n\x02id\x18\x07 \x01(\t\x12\x32\n\x0eselection_mode\x18\x08 \x03(\x0e\x32\x1a.PlotlyChart.SelectionMode\x12\x0f\n\x07\x66orm_id\x18\t \x01(\t\x12\x0c\n\x04spec\x18\n \x01(\t\x12\x0e\n\x06\x63onfig\x18\x0b \x01(\t\x12\r\n\x03url\x18\x01 \x01(\tH\x00\x12\x19\n\x06\x66igure\x18\x02 \x01(\x0b\x32\x07.FigureH\x00\"/\n\rSelectionMode\x12\n\n\x06POINTS\x10\x00\x12\x07\n\x03\x42OX\x10\x01\x12\t\n\x05LASSO\x10\x02\x42\x07\n\x05\x63hartJ\x04\x08\x03\x10\x04J\x04\x08\x04\x10\x05\"&\n\x06\x46igure\x12\x0c\n\x04spec\x18\x01 \x01(\t\x12\x0e\n\x06\x63onfig\x18\x02 \x01(\tB0\n\x1c\x63om.snowflake.apps.streamlitB\x10PlotlyChartProtob\x06proto3')
17
17
 
18
18
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
19
19
  _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'streamlit.proto.PlotlyChart_pb2', globals())
@@ -21,8 +21,10 @@ if _descriptor._USE_C_DESCRIPTORS == False:
21
21
 
22
22
  DESCRIPTOR._options = None
23
23
  DESCRIPTOR._serialized_options = b'\n\034com.snowflake.apps.streamlitB\020PlotlyChartProto'
24
- _PLOTLYCHART._serialized_start=37
25
- _PLOTLYCHART._serialized_end=157
26
- _FIGURE._serialized_start=159
27
- _FIGURE._serialized_end=197
24
+ _PLOTLYCHART._serialized_start=38
25
+ _PLOTLYCHART._serialized_end=318
26
+ _PLOTLYCHART_SELECTIONMODE._serialized_start=250
27
+ _PLOTLYCHART_SELECTIONMODE._serialized_end=297
28
+ _FIGURE._serialized_start=320
29
+ _FIGURE._serialized_end=358
28
30
  # @@protoc_insertion_point(module_scope)
@@ -17,11 +17,15 @@ See the License for the specific language governing permissions and
17
17
  limitations under the License.
18
18
  """
19
19
  import builtins
20
+ import collections.abc
20
21
  import google.protobuf.descriptor
22
+ import google.protobuf.internal.containers
23
+ import google.protobuf.internal.enum_type_wrapper
21
24
  import google.protobuf.message
22
25
  import sys
26
+ import typing
23
27
 
24
- if sys.version_info >= (3, 8):
28
+ if sys.version_info >= (3, 10):
25
29
  import typing as typing_extensions
26
30
  else:
27
31
  import typing_extensions
@@ -31,46 +35,86 @@ DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
31
35
  class PlotlyChart(google.protobuf.message.Message):
32
36
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
33
37
 
34
- URL_FIELD_NUMBER: builtins.int
35
- FIGURE_FIELD_NUMBER: builtins.int
38
+ class _SelectionMode:
39
+ ValueType = typing.NewType("ValueType", builtins.int)
40
+ V: typing_extensions.TypeAlias = ValueType
41
+
42
+ class _SelectionModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[PlotlyChart._SelectionMode.ValueType], builtins.type): # noqa: F821
43
+ DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
44
+ POINTS: PlotlyChart._SelectionMode.ValueType # 0
45
+ """Point selection mode"""
46
+ BOX: PlotlyChart._SelectionMode.ValueType # 1
47
+ """Box selection mode"""
48
+ LASSO: PlotlyChart._SelectionMode.ValueType # 2
49
+ """Lasso selection mode"""
50
+
51
+ class SelectionMode(_SelectionMode, metaclass=_SelectionModeEnumTypeWrapper):
52
+ """Available selection modes:"""
53
+
54
+ POINTS: PlotlyChart.SelectionMode.ValueType # 0
55
+ """Point selection mode"""
56
+ BOX: PlotlyChart.SelectionMode.ValueType # 1
57
+ """Box selection mode"""
58
+ LASSO: PlotlyChart.SelectionMode.ValueType # 2
59
+ """Lasso selection mode"""
60
+
36
61
  USE_CONTAINER_WIDTH_FIELD_NUMBER: builtins.int
37
62
  THEME_FIELD_NUMBER: builtins.int
38
- url: builtins.str
39
- """If the user chose to send the plot to Plotly's server, then this is the
40
- URL that can be used to embed to the plot.
41
- """
42
- @property
43
- def figure(self) -> global___Figure:
44
- """If the user chose to not send the plot to Plotly's server, then we pass
45
- here the plot's dict spec as JSON.
46
- """
63
+ ID_FIELD_NUMBER: builtins.int
64
+ SELECTION_MODE_FIELD_NUMBER: builtins.int
65
+ FORM_ID_FIELD_NUMBER: builtins.int
66
+ SPEC_FIELD_NUMBER: builtins.int
67
+ CONFIG_FIELD_NUMBER: builtins.int
68
+ URL_FIELD_NUMBER: builtins.int
69
+ FIGURE_FIELD_NUMBER: builtins.int
47
70
  use_container_width: builtins.bool
48
71
  """If True, will overwrite the chart width spec to fit to container."""
49
72
  theme: builtins.str
50
73
  """override the properties with a theme. Currently, only "streamlit" or None are accepted."""
74
+ id: builtins.str
75
+ """The unique element ID of this chart."""
76
+ @property
77
+ def selection_mode(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___PlotlyChart.SelectionMode.ValueType]:
78
+ """Activate selections types on the chart."""
79
+ form_id: builtins.str
80
+ """Form ID, filled if selections are activated."""
81
+ spec: builtins.str
82
+ """JSON-serialized dict containing keys from the set {data, frames, layout}."""
83
+ config: builtins.str
84
+ """JSON-serialized dict with Plotly's config object."""
85
+ url: builtins.str
86
+ """DEPRECATED and unused."""
87
+ @property
88
+ def figure(self) -> global___Figure:
89
+ """DEPRECATED and unused."""
51
90
  def __init__(
52
91
  self,
53
92
  *,
54
- url: builtins.str = ...,
55
- figure: global___Figure | None = ...,
56
93
  use_container_width: builtins.bool = ...,
57
94
  theme: builtins.str = ...,
95
+ id: builtins.str = ...,
96
+ selection_mode: collections.abc.Iterable[global___PlotlyChart.SelectionMode.ValueType] | None = ...,
97
+ form_id: builtins.str = ...,
98
+ spec: builtins.str = ...,
99
+ config: builtins.str = ...,
100
+ url: builtins.str = ...,
101
+ figure: global___Figure | None = ...,
58
102
  ) -> None: ...
59
103
  def HasField(self, field_name: typing_extensions.Literal["chart", b"chart", "figure", b"figure", "url", b"url"]) -> builtins.bool: ...
60
- def ClearField(self, field_name: typing_extensions.Literal["chart", b"chart", "figure", b"figure", "theme", b"theme", "url", b"url", "use_container_width", b"use_container_width"]) -> None: ...
104
+ def ClearField(self, field_name: typing_extensions.Literal["chart", b"chart", "config", b"config", "figure", b"figure", "form_id", b"form_id", "id", b"id", "selection_mode", b"selection_mode", "spec", b"spec", "theme", b"theme", "url", b"url", "use_container_width", b"use_container_width"]) -> None: ...
61
105
  def WhichOneof(self, oneof_group: typing_extensions.Literal["chart", b"chart"]) -> typing_extensions.Literal["url", "figure"] | None: ...
62
106
 
63
107
  global___PlotlyChart = PlotlyChart
64
108
 
65
109
  class Figure(google.protobuf.message.Message):
110
+ """DEPRECATED and unused."""
111
+
66
112
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
67
113
 
68
114
  SPEC_FIELD_NUMBER: builtins.int
69
115
  CONFIG_FIELD_NUMBER: builtins.int
70
116
  spec: builtins.str
71
- """JSON-serialized dict containing keys from the set {data, frames, layout}."""
72
117
  config: builtins.str
73
- """JSON-serialized dict with Plotly's config object."""
74
118
  def __init__(
75
119
  self,
76
120
  *,
@@ -26,7 +26,7 @@ from typing import (
26
26
  Dict,
27
27
  Final,
28
28
  Generic,
29
- Sequence,
29
+ Iterable,
30
30
  Tuple,
31
31
  TypeVar,
32
32
  Union,
@@ -49,6 +49,7 @@ from streamlit.proto.DownloadButton_pb2 import DownloadButton
49
49
  from streamlit.proto.FileUploader_pb2 import FileUploader
50
50
  from streamlit.proto.MultiSelect_pb2 import MultiSelect
51
51
  from streamlit.proto.NumberInput_pb2 import NumberInput
52
+ from streamlit.proto.PlotlyChart_pb2 import PlotlyChart
52
53
  from streamlit.proto.Radio_pb2 import Radio
53
54
  from streamlit.proto.Selectbox_pb2 import Selectbox
54
55
  from streamlit.proto.Slider_pb2 import Slider
@@ -85,6 +86,7 @@ WidgetProto: TypeAlias = Union[
85
86
  TextArea,
86
87
  TextInput,
87
88
  TimeInput,
89
+ PlotlyChart,
88
90
  ]
89
91
 
90
92
  GENERATED_WIDGET_ID_PREFIX: Final = "$$WIDGET_ID"
@@ -180,7 +182,7 @@ SAFE_VALUES = Union[
180
182
  def compute_widget_id(
181
183
  element_type: str,
182
184
  user_key: str | None = None,
183
- **kwargs: SAFE_VALUES | Sequence[SAFE_VALUES],
185
+ **kwargs: SAFE_VALUES | Iterable[SAFE_VALUES],
184
186
  ) -> str:
185
187
  """Compute the widget id for the given widget. This id is stable: a given
186
188
  set of inputs to this function will always produce the same widget id output.
@@ -70,6 +70,7 @@ ELEMENT_TYPE_TO_VALUE_TYPE: Final[
70
70
  "time_input": "string_value",
71
71
  "component_instance": "json_value",
72
72
  "data_editor": "string_value",
73
+ "plotly_chart": "string_value",
73
74
  }
74
75
  )
75
76