streamlit-nightly 1.39.1.dev20241031__py2.py3-none-any.whl → 1.39.1.dev20241102__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 (49) hide show
  1. streamlit/commands/execution_control.py +7 -2
  2. streamlit/commands/logo.py +3 -3
  3. streamlit/commands/navigation.py +48 -3
  4. streamlit/commands/page_config.py +8 -3
  5. streamlit/elements/html.py +6 -4
  6. streamlit/elements/image.py +28 -443
  7. streamlit/elements/layouts.py +12 -7
  8. streamlit/elements/lib/image_utils.py +440 -0
  9. streamlit/elements/markdown.py +6 -0
  10. streamlit/elements/media.py +22 -9
  11. streamlit/elements/metric.py +8 -5
  12. streamlit/elements/progress.py +2 -1
  13. streamlit/elements/pyplot.py +3 -5
  14. streamlit/elements/text.py +1 -1
  15. streamlit/elements/widgets/audio_input.py +12 -11
  16. streamlit/elements/widgets/button.py +28 -19
  17. streamlit/elements/widgets/button_group.py +146 -121
  18. streamlit/elements/widgets/camera_input.py +13 -11
  19. streamlit/elements/widgets/chat.py +2 -2
  20. streamlit/elements/widgets/checkbox.py +30 -24
  21. streamlit/elements/widgets/color_picker.py +15 -13
  22. streamlit/elements/widgets/file_uploader.py +12 -12
  23. streamlit/elements/widgets/multiselect.py +33 -31
  24. streamlit/elements/widgets/number_input.py +15 -12
  25. streamlit/elements/widgets/radio.py +15 -12
  26. streamlit/elements/widgets/select_slider.py +15 -12
  27. streamlit/elements/widgets/selectbox.py +19 -14
  28. streamlit/elements/widgets/slider.py +15 -12
  29. streamlit/elements/widgets/text_widgets.py +33 -27
  30. streamlit/elements/widgets/time_widgets.py +33 -25
  31. streamlit/hello/{Animation_Demo.py → animation_demo.py} +9 -10
  32. streamlit/hello/{Dataframe_Demo.py → dataframe_demo.py} +9 -15
  33. streamlit/hello/{Hello.py → hello.py} +7 -12
  34. streamlit/hello/{Mapping_Demo.py → mapping_demo.py} +10 -13
  35. streamlit/hello/{Plotting_Demo.py → plotting_demo.py} +9 -10
  36. streamlit/hello/streamlit_app.py +24 -6
  37. streamlit/proto/Image_pb2.pyi +1 -1
  38. streamlit/static/asset-manifest.json +3 -3
  39. streamlit/static/index.html +1 -1
  40. streamlit/static/static/js/3224.925d380f.chunk.js +1 -0
  41. streamlit/static/static/js/{main.754d974e.js → main.61a89bda.js} +2 -2
  42. {streamlit_nightly-1.39.1.dev20241031.dist-info → streamlit_nightly-1.39.1.dev20241102.dist-info}/METADATA +1 -1
  43. {streamlit_nightly-1.39.1.dev20241031.dist-info → streamlit_nightly-1.39.1.dev20241102.dist-info}/RECORD +48 -47
  44. streamlit/static/static/js/3224.919d670d.chunk.js +0 -1
  45. /streamlit/static/static/js/{main.754d974e.js.LICENSE.txt → main.61a89bda.js.LICENSE.txt} +0 -0
  46. {streamlit_nightly-1.39.1.dev20241031.data → streamlit_nightly-1.39.1.dev20241102.data}/scripts/streamlit.cmd +0 -0
  47. {streamlit_nightly-1.39.1.dev20241031.dist-info → streamlit_nightly-1.39.1.dev20241102.dist-info}/WHEEL +0 -0
  48. {streamlit_nightly-1.39.1.dev20241031.dist-info → streamlit_nightly-1.39.1.dev20241102.dist-info}/entry_points.txt +0 -0
  49. {streamlit_nightly-1.39.1.dev20241031.dist-info → streamlit_nightly-1.39.1.dev20241102.dist-info}/top_level.txt +0 -0
@@ -21,69 +21,26 @@
21
21
 
22
22
  from __future__ import annotations
23
23
 
24
- import io
25
- import os
26
- import re
27
- from enum import IntEnum
28
- from typing import TYPE_CHECKING, Final, Literal, Sequence, Union, cast
24
+ from typing import TYPE_CHECKING, Literal, Union, cast
29
25
 
30
26
  from typing_extensions import TypeAlias
31
27
 
32
- from streamlit import runtime, url_util
33
28
  from streamlit.deprecation_util import show_deprecation_warning
29
+ from streamlit.elements.lib.image_utils import (
30
+ Channels,
31
+ ImageFormatOrAuto,
32
+ ImageOrImageList,
33
+ WidthBehavior,
34
+ marshall_images,
35
+ )
34
36
  from streamlit.errors import StreamlitAPIException
35
37
  from streamlit.proto.Image_pb2 import ImageList as ImageListProto
36
- from streamlit.runtime import caching
37
38
  from streamlit.runtime.metrics_util import gather_metrics
38
- from streamlit.type_util import NumpyShape
39
39
 
40
40
  if TYPE_CHECKING:
41
- from typing import Any
42
-
43
- import numpy.typing as npt
44
- from PIL import GifImagePlugin, Image, ImageFile
45
-
46
41
  from streamlit.delta_generator import DeltaGenerator
47
42
 
48
- # This constant is related to the frontend maximum content width specified
49
- # in App.jsx main container
50
- # 730 is the max width of element-container in the frontend, and 2x is for high
51
- # DPI.
52
- MAXIMUM_CONTENT_WIDTH: Final[int] = 2 * 730
53
-
54
- PILImage: TypeAlias = Union[
55
- "ImageFile.ImageFile", "Image.Image", "GifImagePlugin.GifImageFile"
56
- ]
57
- AtomicImage: TypeAlias = Union[PILImage, "npt.NDArray[Any]", io.BytesIO, str, bytes]
58
- ImageOrImageList: TypeAlias = Union[AtomicImage, Sequence[AtomicImage]]
59
43
  UseColumnWith: TypeAlias = Union[Literal["auto", "always", "never"], bool, None]
60
- Channels: TypeAlias = Literal["RGB", "BGR"]
61
- ImageFormat: TypeAlias = Literal["JPEG", "PNG", "GIF"]
62
- ImageFormatOrAuto: TypeAlias = Literal[ImageFormat, "auto"]
63
-
64
-
65
- # @see Image.proto
66
- # @see WidthBehavior on the frontend
67
- class WidthBehaviour(IntEnum):
68
- """
69
- Special values that are recognized by the frontend and allow us to change the
70
- behavior of the displayed image.
71
- """
72
-
73
- ORIGINAL = -1
74
- COLUMN = -2
75
- AUTO = -3
76
- MIN_IMAGE_OR_CONTAINER = -4
77
- MAX_IMAGE_OR_CONTAINER = -5
78
-
79
-
80
- WidthBehaviour.ORIGINAL.__doc__ = """Display the image at its original width"""
81
- WidthBehaviour.COLUMN.__doc__ = (
82
- """Display the image at the width of the column it's in."""
83
- )
84
- WidthBehaviour.AUTO.__doc__ = """Display the image at its original width, unless it
85
- would exceed the width of its column in which case clamp it to
86
- its column width"""
87
44
 
88
45
 
89
46
  class ImageMixin:
@@ -106,30 +63,28 @@ class ImageMixin:
106
63
 
107
64
  Parameters
108
65
  ----------
109
- image : numpy.ndarray, [numpy.ndarray], BytesIO, str, or [str]
66
+ image : numpy.ndarray, [numpy.ndarray], BytesIO, str, [str], Path, or [Path]
110
67
  Monochrome image of shape (w,h) or (w,h,1)
111
68
  OR a color image of shape (w,h,3)
112
69
  OR an RGBA image of shape (w,h,4)
113
70
  OR a URL to fetch the image from
114
- OR a path of a local image file
71
+ OR a path of a local image file (str or Path object)
115
72
  OR an SVG XML string like `<svg xmlns=...</svg>`
116
73
  OR a list of one of the above, to display multiple images.
117
74
  caption : str or list of str
118
75
  Image caption. If displaying multiple images, caption should be a
119
76
  list of captions (one for each image).
120
77
  width : int or None
121
- Image width. None means use the image width,
122
- but do not exceed the width of the column.
123
- Should be set for SVG images, as they have no default image width.
78
+ Image width. If this is ``None`` (default), Streamlit will use the
79
+ image's native width, up to the width of the parent container.
80
+ When using an SVG image without a default width, you should declare
81
+ ``width`` or use ``use_container_width=True``.
124
82
  use_column_width : "auto", "always", "never", or bool
125
83
  If "auto", set the image's width to its natural size,
126
84
  but do not exceed the width of the column.
127
85
  If "always" or True, set the image's width to the column width.
128
86
  If "never" or False, set the image's width to its natural size.
129
87
  Note: if set, `use_column_width` takes precedence over the `width` parameter.
130
- .. deprecated::
131
- The `use_column_width` parameter has been deprecated and will be removed in a future release.
132
- Please utilize the `use_container_width` parameter instead.
133
88
  clamp : bool
134
89
  Clamp image pixel values to a valid range ([0-255] per channel).
135
90
  This is only meaningful for byte array images; the parameter is
@@ -148,14 +103,15 @@ class ImageMixin:
148
103
  Defaults to "auto" which identifies the compression type based
149
104
  on the type and format of the image argument.
150
105
  use_container_width : bool
151
- Whether to override the figure's native width with the width of the
152
- parent container. If ``use_container_width`` is ``True``, Streamlit
153
- sets the width of the figure to match the width of the parent
106
+ Whether to override ``width`` with the width of the parent
154
107
  container. If ``use_container_width`` is ``False`` (default),
155
- Streamlit sets the width of the image to its natural width, up to
156
- the width of the parent container.
157
- Note: if `use_container_width` is set to `True`, it will take
158
- precedence over the `width` parameter
108
+ Streamlit sets the image's width according to ``width``. If
109
+ ``use_container_width`` is ``True``, Streamlit sets the width of
110
+ the image to match the width of the parent container.
111
+
112
+ .. deprecated::
113
+ ``use_column_width`` is deprecated and will be removed in a future
114
+ release. Please use the ``use_container_width`` parameter instead.
159
115
 
160
116
  Example
161
117
  -------
@@ -175,7 +131,7 @@ class ImageMixin:
175
131
  )
176
132
 
177
133
  image_width: int = (
178
- WidthBehaviour.ORIGINAL if (width is None or width <= 0) else width
134
+ WidthBehavior.ORIGINAL if (width is None or width <= 0) else width
179
135
  )
180
136
 
181
137
  if use_column_width is not None:
@@ -185,21 +141,21 @@ class ImageMixin:
185
141
  )
186
142
 
187
143
  if use_column_width == "auto":
188
- image_width = WidthBehaviour.AUTO
144
+ image_width = WidthBehavior.AUTO
189
145
  elif use_column_width == "always" or use_column_width is True:
190
- image_width = WidthBehaviour.COLUMN
146
+ image_width = WidthBehavior.COLUMN
191
147
  elif use_column_width == "never" or use_column_width is False:
192
- image_width = WidthBehaviour.ORIGINAL
148
+ image_width = WidthBehavior.ORIGINAL
193
149
 
194
150
  else:
195
151
  if use_container_width is True:
196
- image_width = WidthBehaviour.MAX_IMAGE_OR_CONTAINER
152
+ image_width = WidthBehavior.MAX_IMAGE_OR_CONTAINER
197
153
  elif image_width is not None and image_width > 0:
198
154
  # Use the given width. It will be capped on the frontend if it
199
155
  # exceeds the container width.
200
156
  pass
201
157
  elif use_container_width is False:
202
- image_width = WidthBehaviour.MIN_IMAGE_OR_CONTAINER
158
+ image_width = WidthBehavior.MIN_IMAGE_OR_CONTAINER
203
159
 
204
160
  image_list_proto = ImageListProto()
205
161
  marshall_images(
@@ -218,374 +174,3 @@ class ImageMixin:
218
174
  def dg(self) -> DeltaGenerator:
219
175
  """Get our DeltaGenerator."""
220
176
  return cast("DeltaGenerator", self)
221
-
222
-
223
- def _image_may_have_alpha_channel(image: PILImage) -> bool:
224
- return image.mode in ("RGBA", "LA", "P")
225
-
226
-
227
- def _image_is_gif(image: PILImage) -> bool:
228
- return image.format == "GIF"
229
-
230
-
231
- def _validate_image_format_string(
232
- image_data: bytes | PILImage, format: str
233
- ) -> ImageFormat:
234
- """Return either "JPEG", "PNG", or "GIF", based on the input `format` string.
235
-
236
- - If `format` is "JPEG" or "JPG" (or any capitalization thereof), return "JPEG"
237
- - If `format` is "PNG" (or any capitalization thereof), return "PNG"
238
- - For all other strings, return "PNG" if the image has an alpha channel,
239
- "GIF" if the image is a GIF, and "JPEG" otherwise.
240
- """
241
- format = format.upper()
242
- if format in {"JPEG", "PNG"}:
243
- return cast(ImageFormat, format)
244
-
245
- # We are forgiving on the spelling of JPEG
246
- if format == "JPG":
247
- return "JPEG"
248
-
249
- pil_image: PILImage
250
- if isinstance(image_data, bytes):
251
- from PIL import Image
252
-
253
- pil_image = Image.open(io.BytesIO(image_data))
254
- else:
255
- pil_image = image_data
256
-
257
- if _image_is_gif(pil_image):
258
- return "GIF"
259
-
260
- if _image_may_have_alpha_channel(pil_image):
261
- return "PNG"
262
-
263
- return "JPEG"
264
-
265
-
266
- def _PIL_to_bytes(
267
- image: PILImage,
268
- format: ImageFormat = "JPEG",
269
- quality: int = 100,
270
- ) -> bytes:
271
- """Convert a PIL image to bytes."""
272
- tmp = io.BytesIO()
273
-
274
- # User must have specified JPEG, so we must convert it
275
- if format == "JPEG" and _image_may_have_alpha_channel(image):
276
- image = image.convert("RGB")
277
-
278
- image.save(tmp, format=format, quality=quality)
279
-
280
- return tmp.getvalue()
281
-
282
-
283
- def _BytesIO_to_bytes(data: io.BytesIO) -> bytes:
284
- data.seek(0)
285
- return data.getvalue()
286
-
287
-
288
- def _np_array_to_bytes(array: npt.NDArray[Any], output_format: str = "JPEG") -> bytes:
289
- import numpy as np
290
- from PIL import Image
291
-
292
- img = Image.fromarray(array.astype(np.uint8))
293
- format = _validate_image_format_string(img, output_format)
294
-
295
- return _PIL_to_bytes(img, format)
296
-
297
-
298
- def _4d_to_list_3d(array: npt.NDArray[Any]) -> list[npt.NDArray[Any]]:
299
- return [array[i, :, :, :] for i in range(0, array.shape[0])]
300
-
301
-
302
- def _verify_np_shape(array: npt.NDArray[Any]) -> npt.NDArray[Any]:
303
- shape: NumpyShape = array.shape
304
- if len(shape) not in (2, 3):
305
- raise StreamlitAPIException("Numpy shape has to be of length 2 or 3.")
306
- if len(shape) == 3 and shape[-1] not in (1, 3, 4):
307
- raise StreamlitAPIException(
308
- "Channel can only be 1, 3, or 4 got %d. Shape is %s"
309
- % (shape[-1], str(shape))
310
- )
311
-
312
- # If there's only one channel, convert is to x, y
313
- if len(shape) == 3 and shape[-1] == 1:
314
- array = array[:, :, 0]
315
-
316
- return array
317
-
318
-
319
- def _get_image_format_mimetype(image_format: ImageFormat) -> str:
320
- """Get the mimetype string for the given ImageFormat."""
321
- return f"image/{image_format.lower()}"
322
-
323
-
324
- def _ensure_image_size_and_format(
325
- image_data: bytes, width: int, image_format: ImageFormat
326
- ) -> bytes:
327
- """Resize an image if it exceeds the given width, or if exceeds
328
- MAXIMUM_CONTENT_WIDTH. Ensure the image's format corresponds to the given
329
- ImageFormat. Return the (possibly resized and reformatted) image bytes.
330
- """
331
- from PIL import Image
332
-
333
- pil_image: PILImage = Image.open(io.BytesIO(image_data))
334
- actual_width, actual_height = pil_image.size
335
-
336
- if width < 0 and actual_width > MAXIMUM_CONTENT_WIDTH:
337
- width = MAXIMUM_CONTENT_WIDTH
338
-
339
- if width > 0 and actual_width > width:
340
- # We need to resize the image.
341
- new_height = int(1.0 * actual_height * width / actual_width)
342
- # pillow reexports Image.Resampling.BILINEAR as Image.BILINEAR for backwards
343
- # compatibility reasons, so we use the reexport to support older pillow
344
- # versions. The types don't seem to reflect this, though, hence the type: ignore
345
- # below.
346
- pil_image = pil_image.resize((width, new_height), resample=Image.BILINEAR) # type: ignore[attr-defined]
347
- return _PIL_to_bytes(pil_image, format=image_format, quality=90)
348
-
349
- if pil_image.format != image_format:
350
- # We need to reformat the image.
351
- return _PIL_to_bytes(pil_image, format=image_format, quality=90)
352
-
353
- # No resizing or reformatting necessary - return the original bytes.
354
- return image_data
355
-
356
-
357
- def _clip_image(image: npt.NDArray[Any], clamp: bool) -> npt.NDArray[Any]:
358
- import numpy as np
359
-
360
- data = image
361
- if issubclass(image.dtype.type, np.floating):
362
- if clamp:
363
- data = np.clip(image, 0, 1.0)
364
- else:
365
- if np.amin(image) < 0.0 or np.amax(image) > 1.0:
366
- raise RuntimeError("Data is outside [0.0, 1.0] and clamp is not set.")
367
- data = data * 255
368
- else:
369
- if clamp:
370
- data = np.clip(image, 0, 255)
371
- else:
372
- if np.amin(image) < 0 or np.amax(image) > 255:
373
- raise RuntimeError("Data is outside [0, 255] and clamp is not set.")
374
- return data
375
-
376
-
377
- def image_to_url(
378
- image: AtomicImage,
379
- width: int,
380
- clamp: bool,
381
- channels: Channels,
382
- output_format: ImageFormatOrAuto,
383
- image_id: str,
384
- ) -> str:
385
- """Return a URL that an image can be served from.
386
- If `image` is already a URL, return it unmodified.
387
- Otherwise, add the image to the MediaFileManager and return the URL.
388
-
389
- (When running in "raw" mode, we won't actually load data into the
390
- MediaFileManager, and we'll return an empty URL.)
391
- """
392
- import numpy as np
393
- from PIL import Image, ImageFile
394
-
395
- image_data: bytes
396
-
397
- # Strings
398
- if isinstance(image, str):
399
- if not os.path.isfile(image) and url_util.is_url(
400
- image, allowed_schemas=("http", "https", "data")
401
- ):
402
- # If it's a url, return it directly.
403
- return image
404
-
405
- if image.endswith(".svg") and os.path.isfile(image):
406
- # Unpack local SVG image file to an SVG string
407
- with open(image) as textfile:
408
- image = textfile.read()
409
-
410
- # Following regex allows svg image files to start either via a "<?xml...>" tag
411
- # eventually followed by a "<svg...>" tag or directly starting with a "<svg>" tag
412
- if re.search(r"(^\s?(<\?xml[\s\S]*<svg\s)|^\s?<svg\s|^\s?<svg>\s)", image):
413
- if "xmlns" not in image:
414
- # The xmlns attribute is required for SVGs to render in an img tag.
415
- # If it's not present, we add to the first SVG tag:
416
- image = image.replace(
417
- "<svg", '<svg xmlns="http://www.w3.org/2000/svg" ', 1
418
- )
419
- # Convert to base64 to prevent issues with encoding:
420
- import base64
421
-
422
- image_b64_encoded = base64.b64encode(image.encode("utf-8")).decode("utf-8")
423
- # Return SVG as data URI:
424
- return f"data:image/svg+xml;base64,{image_b64_encoded}"
425
-
426
- # Otherwise, try to open it as a file.
427
- try:
428
- with open(image, "rb") as f:
429
- image_data = f.read()
430
- except Exception:
431
- # When we aren't able to open the image file, we still pass the path to
432
- # the MediaFileManager - its storage backend may have access to files
433
- # that Streamlit does not.
434
- import mimetypes
435
-
436
- mimetype, _ = mimetypes.guess_type(image)
437
- if mimetype is None:
438
- mimetype = "application/octet-stream"
439
-
440
- url = runtime.get_instance().media_file_mgr.add(image, mimetype, image_id)
441
- caching.save_media_data(image, mimetype, image_id)
442
- return url
443
-
444
- # PIL Images
445
- elif isinstance(image, (ImageFile.ImageFile, Image.Image)):
446
- format = _validate_image_format_string(image, output_format)
447
- image_data = _PIL_to_bytes(image, format)
448
-
449
- # BytesIO
450
- # Note: This doesn't support SVG. We could convert to png (cairosvg.svg2png)
451
- # or just decode BytesIO to string and handle that way.
452
- elif isinstance(image, io.BytesIO):
453
- image_data = _BytesIO_to_bytes(image)
454
-
455
- # Numpy Arrays (ie opencv)
456
- elif isinstance(image, np.ndarray):
457
- image = _clip_image(
458
- _verify_np_shape(image),
459
- clamp,
460
- )
461
-
462
- if channels == "BGR":
463
- if len(cast(NumpyShape, image.shape)) == 3:
464
- image = image[:, :, [2, 1, 0]]
465
- else:
466
- raise StreamlitAPIException(
467
- 'When using `channels="BGR"`, the input image should '
468
- "have exactly 3 color channels"
469
- )
470
-
471
- # Depending on the version of numpy that the user has installed, the
472
- # typechecker may not be able to deduce that indexing into a
473
- # `npt.NDArray[Any]` returns a `npt.NDArray[Any]`, so we need to
474
- # ignore redundant casts below.
475
- image_data = _np_array_to_bytes(
476
- array=cast("npt.NDArray[Any]", image), # type: ignore[redundant-cast]
477
- output_format=output_format,
478
- )
479
-
480
- # Raw bytes
481
- else:
482
- image_data = image
483
-
484
- # Determine the image's format, resize it, and get its mimetype
485
- image_format = _validate_image_format_string(image_data, output_format)
486
- image_data = _ensure_image_size_and_format(image_data, width, image_format)
487
- mimetype = _get_image_format_mimetype(image_format)
488
-
489
- if runtime.exists():
490
- url = runtime.get_instance().media_file_mgr.add(image_data, mimetype, image_id)
491
- caching.save_media_data(image_data, mimetype, image_id)
492
- return url
493
- else:
494
- # When running in "raw mode", we can't access the MediaFileManager.
495
- return ""
496
-
497
-
498
- def marshall_images(
499
- coordinates: str,
500
- image: ImageOrImageList,
501
- caption: str | npt.NDArray[Any] | list[str] | None,
502
- width: int | WidthBehaviour,
503
- proto_imgs: ImageListProto,
504
- clamp: bool,
505
- channels: Channels = "RGB",
506
- output_format: ImageFormatOrAuto = "auto",
507
- ) -> None:
508
- """Fill an ImageListProto with a list of images and their captions.
509
-
510
- The images will be resized and reformatted as necessary.
511
-
512
- Parameters
513
- ----------
514
- coordinates
515
- A string indentifying the images' location in the frontend.
516
- image
517
- The image or images to include in the ImageListProto.
518
- caption
519
- Image caption. If displaying multiple images, caption should be a
520
- list of captions (one for each image).
521
- width
522
- The desired width of the image or images. This parameter will be
523
- passed to the frontend.
524
- Positive values set the image width explicitly.
525
- Negative values has some special. For details, see: `WidthBehaviour`
526
- proto_imgs
527
- The ImageListProto to fill in.
528
- clamp
529
- Clamp image pixel values to a valid range ([0-255] per channel).
530
- This is only meaningful for byte array images; the parameter is
531
- ignored for image URLs. If this is not set, and an image has an
532
- out-of-range value, an error will be thrown.
533
- channels
534
- If image is an nd.array, this parameter denotes the format used to
535
- represent color information. Defaults to 'RGB', meaning
536
- `image[:, :, 0]` is the red channel, `image[:, :, 1]` is green, and
537
- `image[:, :, 2]` is blue. For images coming from libraries like
538
- OpenCV you should set this to 'BGR', instead.
539
- output_format
540
- This parameter specifies the format to use when transferring the
541
- image data. Photos should use the JPEG format for lossy compression
542
- while diagrams should use the PNG format for lossless compression.
543
- Defaults to 'auto' which identifies the compression type based
544
- on the type and format of the image argument.
545
- """
546
- import numpy as np
547
-
548
- channels = cast(Channels, channels.upper())
549
-
550
- # Turn single image and caption into one element list.
551
- images: Sequence[AtomicImage]
552
- if isinstance(image, (list, set, tuple)):
553
- images = list(image)
554
- elif isinstance(image, np.ndarray) and len(cast(NumpyShape, image.shape)) == 4:
555
- images = _4d_to_list_3d(image)
556
- else:
557
- images = [image] # type: ignore
558
-
559
- if isinstance(caption, list):
560
- captions: Sequence[str | None] = caption
561
- elif isinstance(caption, str):
562
- captions = [caption]
563
- elif isinstance(caption, np.ndarray) and len(cast(NumpyShape, caption.shape)) == 1:
564
- captions = caption.tolist()
565
- elif caption is None:
566
- captions = [None] * len(images)
567
- else:
568
- captions = [str(caption)]
569
-
570
- assert isinstance(
571
- captions, list
572
- ), "If image is a list then caption should be as well"
573
- assert len(captions) == len(images), "Cannot pair %d captions with %d images." % (
574
- len(captions),
575
- len(images),
576
- )
577
-
578
- proto_imgs.width = int(width)
579
- # Each image in an image list needs to be kept track of at its own coordinates.
580
- for coord_suffix, (image, caption) in enumerate(zip(images, captions)):
581
- proto_img = proto_imgs.imgs.add()
582
- if caption is not None:
583
- proto_img.caption = str(caption)
584
-
585
- # We use the index of the image in the input image list to identify this image inside
586
- # MediaFileManager. For this, we just add the index to the image's "coordinates".
587
- image_id = "%s-%i" % (coordinates, coord_suffix)
588
-
589
- proto_img.url = image_to_url(
590
- image, width, clamp, channels, output_format, image_id
591
- )
@@ -369,7 +369,9 @@ class LayoutsMixin:
369
369
  Creates a tab for each string in the list. The first tab is selected
370
370
  by default. The string is used as the name of the tab and can
371
371
  optionally contain GitHub-flavored Markdown of the following types:
372
- Bold, Italics, Strikethroughs, Inline Code, and Links.
372
+ Bold, Italics, Strikethroughs, Inline Code, Links, and Images.
373
+ Images display like icons, with a max height equal to the font
374
+ height.
373
375
 
374
376
  Unsupported Markdown elements are unwrapped so only their children
375
377
  (text contents) render. Display unsupported elements as literal
@@ -476,7 +478,8 @@ class LayoutsMixin:
476
478
  label : str
477
479
  A string to use as the header for the expander. The label can optionally
478
480
  contain GitHub-flavored Markdown of the following types: Bold, Italics,
479
- Strikethroughs, Inline Code, and Links.
481
+ Strikethroughs, Inline Code, Links, and Images. Images display like
482
+ icons, with a max height equal to the font height.
480
483
 
481
484
  Unsupported Markdown elements are unwrapped so only their children
482
485
  (text contents) render. Display unsupported elements as literal
@@ -595,8 +598,9 @@ class LayoutsMixin:
595
598
  label : str
596
599
  The label of the button that opens the popover container.
597
600
  The label can optionally contain GitHub-flavored Markdown of the
598
- following types: Bold, Italics, Strikethroughs, Inline Code, and
599
- Links.
601
+ following types: Bold, Italics, Strikethroughs, Inline Code, Links,
602
+ and Images. Images display like icons, with a max height equal to
603
+ the font height.
600
604
 
601
605
  Unsupported Markdown elements are unwrapped so only their children
602
606
  (text contents) render. Display unsupported elements as literal
@@ -631,8 +635,8 @@ class LayoutsMixin:
631
635
  font library.
632
636
 
633
637
  disabled : bool
634
- An optional boolean, which disables the popover button if set to
635
- True. The default is False.
638
+ An optional boolean that disables the popover button if set to
639
+ ``True``. The default is ``False``.
636
640
 
637
641
  use_container_width : bool
638
642
  Whether to expand the button's width to fill its parent container.
@@ -729,7 +733,8 @@ class LayoutsMixin:
729
733
  label : str
730
734
  The initial label of the status container. The label can optionally
731
735
  contain GitHub-flavored Markdown of the following types: Bold, Italics,
732
- Strikethroughs, Inline Code, and Links.
736
+ Strikethroughs, Inline Code, Links, and Images. Images display like
737
+ icons, with a max height equal to the font height.
733
738
 
734
739
  Unsupported Markdown elements are unwrapped so only their children
735
740
  (text contents) render. Display unsupported elements as literal