Streamlit 1.40.0の新機能紹介

## Streamlitとは

StreamlitはPython向けのWebアプリケーションフレームワークです。  

https://streamlit.io/

Streamlitを使うとフロントエンドの知識ゼロでもデータを可視化するためのWebアプリケーションを簡単に作れます。以下のような機能が特徴。  
- 変数を地の文に書くだけで、GUIに出力される(**マジックコマンド**)
- `st.radio`や`st.text_input`のようなWidget APIを呼び出すだけで、GUIコンポーネントが生成される

## Streamlitの最新バージョン

Streamlitの最新バージョンは[Changelog](https://docs.streamlit.io/library/changelog)で確認できます。  
2024/11/18時点で最新バージョンは1.40.1です。  

https://docs.streamlit.io/library/changelog

以下に記載した変更点は、1.40.0でアナウンスされたものの抜粋です。  

- ```st.pills```および```st.segmented_control```のリリース
- ```st.audio_input```のGA
- ```st.markdown```がStreamlitロゴと一部の組版記号の表示に対応
- ```st.image```のオプションの変更
- ```st.date_input```で表示される週の始まりの曜日がブラウザロケールを参照するように変更

Streamlitロゴが表示できるようになった！

## ```st.pills```および```st.segmented_control```のリリース

いずれもモダンなスマホ向けWebアプリでよく見かける要素です！  
機能的にはグループ化されたボタンという感じで、```st.multiselect```とも使い方はかなり似ています。  
```st.pills```はピルボタンと呼ばれる丸みを帯びたボタンのことで、ホームボタンや画面のナビゲーションなどで使用することが多いです。  
また、```st.segmented_control```は１つのテーマに対して複数の選択肢を提供するためのボタンで、  

- 地図アプリで表示を標準と衛星写真で切り替える
- メールアプリで「受信トレイ」と「送信トレイ」で切り替える
- データを表示するときにテーブル表示とグラフ表示を切り替える

などの用途で使われます。  

なお、```selection_mode```オプションで1つだけ選択と複数選択を設定することができます。  

![st.pills and st.segmented_control screenshot](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/200bf7ae-d0ef-f19f-75dc-eeb78f02dc64.png)

```py:pills_and_segmented_controls.py
import streamlit as st

# ピルボタン(丸みを帯びたボタン)の表示
# 例えばホームボタンや画面のナビゲーションなどで使用することが多い
result = st.pills(":material/coffee: コーヒーのオプション", ["フタ付き", "ミルク", "砂糖"], selection_mode="multi")
st.write(result)

# セグメンテッドコントロール
# １つのテーマに対して複数の選択肢を提供するためのボタン
result = st.segmented_control(":material/coffee: コーヒーのサイズ", ["S", "M", "L"])
st.write(result)
```

## ```st.audio_input```のGA

```st.audio_input```は音声を録音するための要素です。  
返り値は```st.file_uploader```とほぼ同じ形式で、
```st.audio```や```st.download_button```にそのまま渡すことができます。  
また、返り値の```getvalue```関数でバイナリデータを読み出すことも可能です。  

![st.audio_input screenshot](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/392feb0b-cb98-82b7-1d2f-0709953b4095.png)

```py:audio_input.py
import streamlit as st

# 音声を録音する。file_uploaderによるアップロードと同じ形式になっている
result = st.audio_input(label=":material/mic: 声を録音する")

# 音声を再生する。そのまま再生可能
if result is not None:
    st.audio(result)

# 音声を保存する
if result is not None:
    st.download_button(":material/download: ダウンロードする", result, "audio.wav", "audio/wav")

# そのままPythonで解析するときは例えばwaveモジュールを使う
import wave
import io
if result is not None:
    # file_uploaderによるアップロードと同じ形式になっているため、データをBytesIOを介してwaveモジュールで読み込む
    bytes_data = result.getvalue()
    wave_buffer = io.BytesIO(bytes_data)
    with wave.open(wave_buffer, "rb") as wf:
        st.write("チャンネル数：", wf.getnchannels())
        st.write("サンプル幅：", wf.getsampwidth())
        st.write("サンプリング周波数：", wf.getframerate())
        st.write("フレーム数：", wf.getnframes())
        st.write("すべてのパラメータ：", wf.getparams())
```

## ```st.markdown```がStreamlitロゴと一部の組版記号の表示に対応

まず、```st.markdown```に渡すテキストに```:streamlit:```と書くとStreamlitロゴが表示されるようになりました！  
これはStreamlitでデモを作ったときにStreamlit感をアピールするのに便利！  

また、```st.markdown```に渡すテキストに書かれている->や<-が、画面に表示するときに→や←に自動で変換されるようになりました。  
なお、公式のリリース文ではTypographical symbols(組版記号)と書かれていますが、Ligature(リガチャ)あるいは合字と呼ばれる概念に近い挙動かなと感じました。  

![st.markdown screenshot](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/304f45a6-6b9f-964d-ec0d-c8de96f73a10.png)

```py:text_rendering.py
import streamlit as st

# 共通：マテリアルアイコン一覧は[こちら](https://fonts.google.com/icons?hl=ja)を参照

# テキストのレンダリング
st.markdown("""
:streamlit: Streamlitアイコンが使えるようになった。  
一部のTypographical symbols(組版記号)も使えるようになった。具体的には以下の矢印と比較演算子。  
<- は<-で出力できる  
-> は->で出力できる  
<-> は<->で出力できる  
\-- は--で出力できる  
\>= は>=で出力できる  
<= は<=で出力できる  
\~= は~=で出力できる  
""")

st.warning("注意：行頭で--や>=などを使うと、Markdownの記法として認識されてしまう。バックスラッシュを使うことで回避できる。")
st.warning("注意：1.40.0時点では上記の組版記号はエスケープが効かない。前後にスペースなく他の文字を並べることで元々の元々の記号が表示される。")
```

## ```st.image```のオプションの変更

これまで```st.image```の幅に関するオプションとして```use_column_width```が用意されていましたが、今後は```use_container_width```を使うことが推奨されています。  
オプションの設定内容がシンプル化していて、```True```か```False```のみになっています。  
実際に試してみた感触としては、大きい画像はオプションに関係なくコンテナの横幅に自動で縮小されるので見た目は変わらないのですが、小さい画像はコンテナの横幅に合わせて拡大するかしないかがオプションで決まるため、見た目の印象がかなり変わりそうです。  

![st.image screenshot](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/e82e2de9-1b84-a650-61e4-9a65351ba85d.png)

```py:image_width.py
import streamlit as st

# 大きい画像の表示はあまり変わらない
st.caption("use_container_width=True")
st.image("img/photo1.jpg", use_container_width=True)
st.divider()

st.caption("use_container_width=False")
st.image("img/photo1.jpg", use_container_width=False)
st.divider()

# 小さい画像の表示は変わる
st.caption("use_container_width=True")
st.image("img/photo2.jpg", use_container_width=True)
st.divider()

st.caption("use_container_width=False")
st.image("img/photo2.jpg", use_container_width=False)
st.divider()
```

## ```st.date_input```で表示される週の始まりの曜日がブラウザロケールを参照するように変更

日本では週の始まりが日曜日になっているカレンダーがメジャーですが、実は国によって異なるそうです。  

- キリスト教の安息日が日曜日で、アメリカはこれを起点とするので日曜日が週の始まり
- ヨーロッパでは勤労日を起点とするので月曜日が週の始まり
- 日本はアメリカに倣う
- イスラム教の安息日は金曜日で、イスラム教圏はこれを起点とするので金曜日が週の始まり

…となることが多いようです。  

例えばブラウザがGoogle ChromeであればF12キーで開発者ツールを開いて、```Sensors```というタブを開くとロケールを一時的に変更することが可能です。  
これを使うと実際に週の始まりが変わることが確認できます。  

![st.date_input and DevTools screenshot](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/c9ece67a-808f-75f0-2d1b-3ae815d6b420.png)

```py:new_date_input.py
import datetime
import streamlit as st

# Chromeの開発者ツールで以下の値を入力して、リロードする
# | Latitude  | Longitude   | Timezone ID         | Locale |
# | --------- | ----------- | ------------------- | ------ |
# | 35.681236 | 139.767125  | Asia/Tokyo          | ja_JP  |
# | 37.774929 | -122.419416 | America/Los_Angeles | en_US  |
# | 52.520008 | 13.404954   | Europe/Berlin       | de_DE  |
# | 24.453884 | 54.377343   | Asia/Dubai          | ar_AE  |

d = st.date_input("あなたの宿泊日", value=None)
st.write("宿泊日は：", d)
```

## Streamlitを試すためには？

StreamlitはPythonライブラリなので、```pip```で簡単に導入できます。  

```sh
pip install streamlit
streamlit run (ソースコード名).py
```

過去にインストールしたバージョンからアップデートする場合は```pip install -U streamlit```を実行してください。  

また、Python環境の構築にハードルを感じる場合は、[Streamlit Playground](https://streamlit.io/playground?example=hello)で試してみるのがオススメです。こちらではブラウザ上でソースコードを入力して試すことができます。  

https://streamlit.io/playground?example=hello

2024/11/11時点ではStreamlit Playgroundの内部で使っているプログラム [stlite](https://github.com/whitphx/stlite) が1.40.0に対応していないため、今回紹介した機能が使えませんが、開発者の方がいつも爆速対応されているので、1.40.0にも近日中に対応するのではないかなと思います！  

(2024/11/18 23:55追記）
[stlite](https://github.com/whitphx/stlite) が1.40.1に対応しました！[whitphx](https://x.com/whitphx_ja)さん、ありがとうございます！  
これにより、[Streamlit Playground](https://streamlit.io/playground?example=hello)および[Stlite Sharing](https://edit.share.stlite.net/)でStreamlit 1.40.1が動かせるようになっています。  
なお、Streamlit Playgroundについては2024/11/18時点で```:streamlit:```の表示が上手くできないようなので、ご注意ください。  

![Streamlit Playground](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/782837c3-3327-25c0-de9f-4cdb760cd73a.png)