1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Streamlitで長時間処理も安心!ユーザーにやさしい途中経過表示術

Posted at

user:

Streamlitで、長い計算を行うときがあります。結果が返ってくるまで何も応答がないのは辛いので、途中報告を一時的に出力する良い方法はありませんか?

assistant:

Streamlitで長い計算を行う際に、途中経過(進捗)をユーザーに伝える方法はいくつかあります。以下におすすめの方法を紹介します:


✅ 方法1: st.status を使って進捗ログを表示(おすすめ)

Streamlit 1.25以降では st.status を使うことで、処理の途中経過ログや状態の表示ができます。

import streamlit as st
import time

with st.status("計算を開始します...", expanded=True) as status:
    for i in range(5):
        st.write(f"{i+1} 秒目の処理中...")
        time.sleep(1)
    status.update(label="計算が完了しました ✅", state="complete")

特徴

  • 見た目が整っていて、ログが残る
  • 状態の更新 (status.update()) が可能
  • 非同期には対応していない(長い処理が止まっている間はStreamlit側はブロックされる)

✅ 方法2: st.progress + st.text の組み合わせでプログレスバー表示

import streamlit as st
import time

progress = st.progress(0)
status_text = st.empty()

for i in range(100):
    status_text.text(f"処理中... {i+1}%")
    progress.progress(i + 1)
    time.sleep(0.05)

特徴

  • ビジュアルで進捗が分かりやすい
  • %ベースで進捗を出すのに適している

✅ 方法3: st.empty() を使って任意の情報を更新

st.empty() を使うと、表示内容を後から上書き・更新できます。

import streamlit as st
import time

placeholder = st.empty()

for i in range(5):
    placeholder.text(f"ステップ {i+1} 実行中...")
    time.sleep(1)

placeholder.text("すべての処理が完了しました ✅")

特徴

  • 自由度が高く、任意の表示に切り替え可能
  • コンパクトに情報を伝えられる

✅ 方法4: st.spinner で処理中のアイコン表示

簡易な「読み込み中」表示だけでいい場合はこちら。

import streamlit as st
import time

with st.spinner("計算中です...しばらくお待ちください"):
    time.sleep(5)

st.success("計算が完了しました!")

🔁 注意点

Streamlitは同期的にコードが実行されるため、処理の途中でUIに何度もフィードバックを返したい場合は、上記のようにループ内で明示的に表示を更新し、time.sleep()などで進捗を可視化する必要があります。


💡まとめ

方法 向いているケース
st.status ログを含む進捗表示(最もおすすめ)
st.progress %で進捗を見せたい場合
st.empty() カスタムな進捗メッセージを出したい場合
st.spinner 簡易な「処理中」表示だけでよい場合

必要であれば、バックグラウンド処理や非同期処理(例:threadingconcurrent.futures)も応用可能ですが、Streamlitとの相性を考えると、上記の方法を工夫するのが最も安定します。

さらに進んだ実装(例:長時間の処理とUI分離、マルチページ構成など)にも興味があれば、お知らせください。

user:

ありがとうございます。計算途中に、途中結果の図を見せることは可能ですか?

assistant:

はい、Streamlitでは計算途中に図(プロットなど)を表示・更新することも可能です!
以下のように st.empty() を使えば、**図の「差し替え」や「更新」**ができます。


✅ 例:途中経過として図を段階的に更新

import streamlit as st
import matplotlib.pyplot as plt
import numpy as np
import time

# 空の表示枠(図の更新用)
placeholder = st.empty()

# 長い計算の代わりにループで処理
for i in range(1, 6):
    # ダミーデータ生成
    x = np.linspace(0, 10, 100)
    y = np.sin(x + i)

    # プロット作成
    fig, ax = plt.subplots()
    ax.plot(x, y)
    ax.set_title(f"Step {i} の途中結果")

    # 図を更新表示
    placeholder.pyplot(fig)

    # ちょっと待つ(本来はここで計算)
    time.sleep(1)

# 最終結果を別に表示してもよい
st.success("計算完了!")

✅ 応用ポイント

  • st.empty() を使うことで 何度でも同じ場所に上書き表示 できます。
  • Matplotlibの他に Plotly, Altair, Bokeh なども使えます(例:placeholder.plotly_chart(fig))。
  • グラフだけでなく st.table()st.image() などの他のコンテンツにも応用可能です。

💡注意点

  • Streamlitは1つのスクリプトを逐次的に実行するため、リアルタイムで更新したい場合はループ内で明示的に描画・待機(time.sleep()など)を行う必要があります。
  • 複数の途中結果を残したい場合は、st.container()st.columns() を使うと柔軟なレイアウトが可能です。

📌まとめ

やりたいこと 方法
図をリアルタイムに更新 st.empty().pyplot(fig)
図を段階的に追加表示 st.container()st.write()
インタラクティブな図 PlotlyやAltairの使用

必要であれば、PlotlyやAltairを使った例や、複数の図を保存して並べて見せる方法もご紹介できます。気軽にどうぞ!

user:

ありがとうございます。st.container()を使う例を教えていただけますか?

assistant:

もちろんです!
st.container() は、Streamlitで複数のコンポーネントをまとめて配置したいときや、途中結果を「追記する」ように表示したいときに便利です。


st.container() の基本的な使い方

import streamlit as st
import matplotlib.pyplot as plt
import numpy as np
import time

# コンテナを用意
container = st.container()

# 繰り返し途中結果を追記表示
for i in range(1, 6):
    with container:
        # ダミーデータ作成
        x = np.linspace(0, 10, 100)
        y = np.sin(x + i)

        # グラフ作成
        fig, ax = plt.subplots()
        ax.plot(x, y)
        ax.set_title(f"Step {i} の途中結果")

        # グラフ表示(追記)
        st.pyplot(fig)
        st.write(f"Step {i} のメモ: sin(x + {i}) の計算結果")
    
    time.sleep(1)

st.success("すべてのステップが完了しました!")

🔍 実行結果のイメージ

  • 毎回のループで 新しい図が下に追加される
  • ログのように「蓄積表示」される(st.empty() とは逆に「上書き」しない)

st.container() vs st.empty() の違い

機能 st.container() st.empty()
表示の追加 追記されていく 上書きされる
用途 ログや履歴の蓄積 状態のリアルタイム更新
処理ごとの図を全部残したい 1つの図を常に最新に保ちたい

💡補足:複数コンテナで構造を整えることも可能

例えば、以下のように st.columns() と組み合わせて、「途中結果を横に並べる」ようなレイアウトも可能です:

col1, col2 = st.columns(2)

with col1:
    st.write("左側の進捗")
    # グラフやテキスト表示

with col2:
    st.write("右側の進捗")
    # 別の情報を表示

✅ まとめ

  • st.container()「ログ風の出力」や「逐次結果の蓄積表示」 に最適。
  • st.empty()「ひとつの表示を上書き更新」 したい場合に向いています。
  • 両者を組み合わせれば、進捗バー + ログ + 図の更新といった構成も可能です。

ご希望があれば、st.container() を使って 表・画像・テキストを同時に追加する複雑な例 もご紹介できます!

user:

すみません、追記されていくのであれば、通常の表示と何が違うんでしょうか?

assistant:

とても良い質問です!

実は、st.container()振る舞い自体は「通常の表示」とほとんど同じで、その場所(コンテナ)にまとめて出力できるだけです。


🔍 結論から言うと:

st.container() の特徴は「明示的に表示の“まとまり”を作れる」ことで、通常の st.write() などとは「機能的な違い」よりも「構造化・レイアウト面の意図を明確に示せる」点にあります。


st.container() と「通常表示」の違いを整理

項目 通常の st.write() など st.container()
表示位置 コードの実行順にそのまま表示される 特定のブロック(コンテナ)内にまとめて表示
明示的な領域分け ❌ できない with container: によるブロック構造
再利用 ❌ 不可 container.empty() で上書きや差し替え可能
レイアウト制御 やや難しい 他の要素(columnsやexpander等)と組み合わせやすい

✅ 例:st.container() が便利なケース

1. 繰り返し出力を「まとまり」として管理したい

for i in range(3):
    with st.container():
        st.write(f"Step {i+1}")
        st.line_chart(np.random.randn(10))

上記では、各ループで 1つのまとまった表示ブロック が作られ、他と混ざりません。


2. 後から表示内容を更新したい(更新用ハンドルとして)

container = st.container()

for i in range(5):
    with container:
        st.write(f"現在のステップ: {i}")
    time.sleep(1)

これは st.empty() に似た使い方で、コンテナ内を上書きして使うこともできます。


✅ 逆に「通常表示」でいいケース

  • 単に上から順に表示していけばよい
  • 特定のブロックとして管理する必要がない
  • レイアウトや後からの更新の必要がない

💡補足:名前付きで管理したい場合も便利

my_container = st.container()
my_container.write("ここにまとめて表示")

という形で、「この部分にだけ図を出す」や「ログを出す」という設計がしやすくなります。


✅ まとめ

  • st.container() は、**「通常表示と同じ表示ができるが、表示のまとまりを構造的に分けられる」**のが利点。
  • 特に複数の部分表示を整理したいときや、後から更新・再利用したいときに有効。
  • 単に「出力を追記する」だけなら st.write() で十分なことも多い。

「なんとなく機能が被ってない?」と感じるのは自然です。
ただ、UIを整理したり、動的に操作したいときには強力な味方になります!

必要なら、「複数のst.container()を使ってログと図を分ける例」もお見せできます。

user:

ありがとうございます。では、st.empty() を使ってリアルタイム更新した後、計算終了後に表示を消すことはできますか?

assistant:

はい、st.empty() を使えば、表示をリアルタイム更新した後に「消す」こともできます!


✅ 基本の考え方

st.empty() で作ったプレースホルダ(表示枠)は、

  • .write().pyplot() などで内容を表示
  • .empty() メソッドで**中身をクリア(=非表示に)**できます。

✅ 例:計算中に進捗メッセージを表示 → 計算後に非表示

import streamlit as st
import time

# プレースホルダ作成
status = st.empty()

# リアルタイム進捗表示
for i in range(5):
    status.write(f"ステップ {i+1}/5 を計算中...")
    time.sleep(1)

# 計算完了 → 表示を消す
status.empty()

# 別の完了メッセージを表示
st.success("計算が完了しました!")

✅ 図やチャートでも同様に使える

import matplotlib.pyplot as plt
import numpy as np

chart_area = st.empty()

# プロット表示(途中経過)
x = np.linspace(0, 10, 100)
for i in range(3):
    y = np.sin(x + i)
    fig, ax = plt.subplots()
    ax.plot(x, y)
    ax.set_title(f"ステップ {i+1}")
    chart_area.pyplot(fig)
    time.sleep(1)

# 最後に表示をクリア
chart_area.empty()

✅ 注意点

  • .empty() で消せるのは st.empty() で作った表示領域だけです。
  • 直接 st.write() などで表示したものは削除できません。

✅ 応用:一時的にメッセージを出して自動で消す

message = st.empty()
message.warning("しばらくお待ちください...")
time.sleep(2)
message.empty()

✅ まとめ

やりたいこと 方法
表示をリアルタイム更新 st.empty().write() など
最後に非表示にする .empty() を呼び出す
表示枠を再利用する 同じ st.empty() インスタンスに書き換える

もし「最後に別のメッセージや図に差し替えたい」という場合も、.empty() を使えば柔軟にコントロールできますよ!必要ならその例もご紹介します。

user:

ありがとうございました。今日の会話をブログに残したいので、タイトル案をいくつか考えていただけませんか?

assistant:

もちろんです!
Streamlitの「計算中の進捗表示」や「途中結果のグラフ表示」「st.empty()st.container()の使い分け」に関する内容から、以下のようなブログタイトル案をご提案します。


✨ タイトル案(汎用・初心者向け)

  1. 「Streamlitで重い計算中に進捗表示をする方法まとめ」
  2. st.empty() vs st.container() 使い分け完全ガイド」
  3. 「Streamlitで計算途中のグラフやログを表示するテクニック」
  4. 「止まって見えるStreamlitアプリを救う!進捗と途中出力のベストプラクティス」
  5. 「Streamlitで長時間処理も安心!ユーザーにやさしい途中経過表示術」

🔧 技術者・実装重視の方向け

  1. 「Streamlitアプリで非同期っぽく見せるためのst.empty()活用術」
  2. 「Streamlitでリアルタイム更新&ログ表示を両立するには?」
  3. 「Streamlitの途中出力:st.empty()st.container()の実戦活用」
  4. 「計算途中のプロットもOK!Streamlitでリアルタイム表示する方法」

💡少しキャッチー・読みやすさ重視

  1. 「そのStreamlitアプリ、フリーズしてませんか?ユーザーの不安を減らす工夫」
  2. 「進捗を見せるだけで好印象!Streamlitでの“途中経過表示”のコツ」
  3. 「ただのst.write()じゃもったいない!Streamlitの表示をもっと自在に」

📌 サブタイトルの付け方例

  • メインタイトル + : or で説明を加えると読みやすくなります。

例:

  • 「Streamlitで重い処理中に進捗を見せる方法:st.empty()st.container()の使い方」
  • 「リアルタイム更新するStreamlitアプリの作り方 – 図・ログ・進捗を丁寧に見せる」

ご希望の文体(カジュアル/まじめ/読者層など)に合わせて、さらに絞り込むこともできます!お気軽にどうぞ。

user:

「Streamlitで長時間処理も安心!ユーザーにやさしい途中経過表示術」で行きたいと思います。最後に、今回のまとめを書いてください。

assistant:

素敵なタイトルですね!
それでは、**「Streamlitで長時間処理も安心!ユーザーにやさしい途中経過表示術」**というブログ用に、今回の会話内容をもとにしたまとめ文をご用意します。


📝 まとめ:Streamlitで長時間処理も安心!ユーザーにやさしい途中経過表示術

Streamlitはとても手軽にインタラクティブなWebアプリを作れますが、計算に時間がかかる処理では「画面が固まったように見えてしまう」ことがユーザー体験を損なう原因になります。
今回ご紹介したのは、長時間処理の間に途中経過や進捗、図などをユーザーに丁寧に見せる方法です。


st.empty() を使ったリアルタイム更新

  • st.empty() は一時的な表示領域を作るためのプレースホルダ
  • .write().pyplot() などで中身を表示し、.empty() で消すことができます。
  • 進捗メッセージや途中のグラフ表示に最適です。
progress = st.empty()
for i in range(5):
    progress.write(f"処理中: ステップ {i+1}/5")
    time.sleep(1)
progress.empty()
st.success("処理が完了しました!")

st.container() で表示ブロックをまとめる

  • st.container() は、複数の表示要素をひとまとまりのブロックとして管理できる仕組み。
  • ループ内で複数の図やログを出力したいときに便利です。
  • with container: のブロック内に複数の表示をまとめられます。
for i in range(3):
    with st.container():
        st.write(f"ステップ {i+1}")
        st.line_chart(np.random.randn(10))

✅ 表示をユーザーにやさしく

  • リアルタイムに状況がわかるだけで、ユーザーの安心感は段違いです。

  • 特に、以下のような情報を見せるのが効果的:

    • 現在の進捗状況
    • 処理の対象(例:ファイル名、行数など)
    • 途中結果のグラフや統計量
    • 最後に「完了」メッセージや処理時間

✅ 表示をクリアする・差し替える

  • st.empty() を使えば、一時的に表示 → 消す、あるいは差し替えることも自由自在。
  • 「途中メッセージを消して、最後に結果だけ表示したい」などの場面で活躍します。

🔚 おわりに

Streamlitでの長時間処理も、ちょっとした工夫でぐっと使いやすいアプリに変身します
st.empty()st.container() を使いこなして、ユーザーにやさしい、気配りのあるアプリを目指しましょう!


ご希望に応じて、このまとめ文をMarkdown形式にしたり、コードをより整えて見やすくすることもできます。お気軽にどうぞ!

user:

ありがとうございました。これで大丈夫です。また今後もよろしくお願いします!

assistant:

こちらこそ、ありがとうございました!
とても丁寧にご質問いただき、やり取りも楽しかったです 😊

ブログ執筆やStreamlitの開発でまた何かあれば、いつでも気軽にお声がけください。
今後もどうぞよろしくお願いします!✨

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?