はじめに
Streamlit は、Webアプリの手早い構築とサービス展開(deploy)を可能にするフレームワークです。
Python ができることなら、たいていなんでも Web アプリ化できます。Pandas と Plotly を組みわせたデータビジュアライゼーションも、OpenAI Whisper を使った AI 文字起こしも、Streamlit でさくっと Web アプリに転換できます。お洒落なルーティング機構や HTTP/CSS は知らなくても、ほんの数行加えるだけでできあがりです。
自分の成果やアイデアを友人、共同研究者、同僚などと手軽に共有したいのなら、こんなに便利なものはありません。サーバ機能も付いてくるので、お手軽にローカル運用もできます。
感情分析の実装例は『Python+Streamlit で感情分析アプリをコミュニティクラウドにデプロイする』で取り上げたので、そちらをご覧ください。記事では、自分のコードを Github 経由でクラウドにディプロイする方法も説明しています。
イベント処理が苦手
とはいうものの、Streamlit はゲームのようにインタラクティブ性の高いアプリは得手ではありません。RPG キャラクタの移動程度であっても、ユーザ操作のイベントが発生すれば、スクリプトを先頭から再実行するからです。キャラ移動のたびにブラウザがリロードするのでは、あまり使い勝手がよいとはいえないでしょう。
状態管理には、リロードでも失わない専用の変数が使われます(Cookie のようなもの)。スクリプトは状態情報をもとに分岐することで、画面をその状態に応じてレンダリングします。そのため、状態依存性の高いアプリでは元のコードに Streamlit コマンドを加えるだけでは済まず、状態単位へのコードの分解と再構築が必要になってきます。
そのせいもあって、フリーなアプリ共有サイトの Streamlit コミュニティクラウドでセレクトされている Gallery(ギャラリー)にも、ゲームっぽいものはほとんどありません。そんな中でも、この「ダンジョン」ゲームはかなり健闘しているので、お試しあれ。
デモ
といいつつ、やってやれないわけではありません。ここではブラックジャックアプリを作り、インターネットに公開します。
ブラックジャックなのは、カードを取ったり切ったりする間合い(それぞれでイベントが発生する)が長めなので、ちょっとくらい反応が鈍くてもプレーできるからです。
本アプリは https://sat-blackjack.streamlit.app/
からアクセスできます。スリープしていたら、[Yes, get this app back up!]とある青いボタンから再稼働してください。
次に画面例を示します。

左パネル上の[Start]ボタンをクリックすれば、カードが2枚配られ、ゲームが開始します。カードを取るには[Hit]、勝負に出るには[Showdown]です。シンプルなゲームなので遊び方は自明だと思いますが、詳しい説明は右パネル上の[遊び方]プルダウンを見ください。
アプリのコード
コードは長い(空行やコメントも入れて170行くらい)ので、Github を参照してください。
外部ライブラリは使っていないので、requirements.txt
は必要ありません。これだけで動きます。
コードの説明はざくっと割愛しますが、トランプをどのように表現しているかと、ボタンのアクティブ・インアクティブを状態に応じて変化させる方法だけを説明します。
トランプの表現
Streamlit も Python も Unicode 対応なので、カードの絵柄は Unicode 文字で表示できます。コードポイントは U+1F0A0
から U+1F0F5
までの間です(Wikipedia の “Playing cards in Unicode” を参照)。わたしたちが普通に使うトランプがその順番通りに並んでいれば話が早いのですが、ナイト(JQK ではなく C!)なんていう絵札があったりして、柄(♠♡♦♧)と数値(1~13)からコードポイントを計算するのがちょいとややこしいので、本スクリプトでは直書きで52枚を並べて使っています。
cards_surface = list('🂡🂢🂣🂤🂥🂦🂧🂨🂩🂪🂫🂭🂮'+'🂱🂲🂳🂴🂵🂶🂷🂸🂹🂺🂻🂽🂾'+
'🃁🃂🃃🃄🃅🃆🃇🃈🃉🃊🃋🃍🃎'+'🃑🃒🃓🃔🃕🃖🃗🃘🃙🃚🃛🃝🃞')
ブラウザ上のフォントの標準サイズは16ポイント程度なので、そのまま表示したのでは絵柄の区別など付きません。そこで、CSSスタイルで強引に96ポイントまで上げて表示します。ついでに、♡と♦は赤、♠と♧は濃い青で色付けます。
cards_style = [f'<span style="font-size: 96px; color: {c};">'
for c in ['MidnightBlue', 'Red', 'Red', 'MidnightBlue']]
Streamlit の文字列表示は Github スタイルマークダウンが基本ですが、マークダウンでは無視される CSS を併用するには st.html()
でレンダリングさせます。
ボタンのアクティブ切り替え
リロードでも消えない状態管理の変数は st.session_state
です。これは辞書で、任意のキーと値を収容できます。本スクリプトでは、ボタンのアクティブ・インアクティブをキー state_buttons
に収容しています。値は辞書で(なので辞書の辞書)、キー Start
はスタートボタン、Hit
は1枚めくりボタン、Showdown
は勝負のボタンです。初期状態では、次のようになっています。
def set_buttons(start=False, hit=True, showdown=True):
st.session_state.states_buttons['Start'] = start
st.session_state.states_buttons['Hit'] = hit
st.session_state.states_buttons['Showdown'] = showdown
値が True
ならインアクティブなので、デフォルトでは Start
(False
)だけがアクティブで、他はインアクティブです。
スタートボタンが押されたら、次の要領で、それ自体をインアクティブに、他をアクティブにします。
set_buttons(True, False, False)
ボタン表示の Streamlit コマンドは st.button()
です。この関数には disabled
キーワード引数があり、True
を指定すれば、インアクティブになります。
st.button('Start', disabled=st.session_state.states_buttons['Start'],
on_click=clicked_start)
st.button('Hit', disabled=st.session_state.states_buttons['Hit'],
on_click=clicked_hit)
st.button('Showdown', disabled=st.session_state.states_buttons['Showdown'],
on_click=clicked_showdown)
キーワード引数の on_click
はおなじみコールバック関数登録ですが、JavaScript と違って、その関数だけが呼び出されるわけではありません。スクリプトはやはり、最初から最後まで実行されます。ただし、登録関数はスクリプトのどのラインよりも先に動作することが保証されています。
スタートボタンがクリックされれば、リロードされ、最初に clicked_start()
関数が実行されます。この関数では、上記のアクティブ・インアクティブの状態を変化させ、山札を準備し、ディーラーとプレーヤーに手札を配り、それぞれを st.session_state
に収容します。また、最下端のエリアに示すメッセージを用意します。別のボタンがクリックされればその関数が実行され、手札を変更し、スコアを計算し、メッセージを変えます。
あとはそれぞれの描画エリアに文字列を表示させるだけです。書籍の表紙のある左パネルなどはほとんど中身が変わりませんが、ボタンクリックのたびに再描画されています。
おわりに
と、駆け足で Streamlit の魅力を紹介しました。コードはほとんど説明しませんでしたが、状態管理をつうじて、ゲームのような複雑な動きも達成できることがどことなくわかってもらえれば幸いです。
本書の記事は、技術評論社の許可を得て『作ってわかる[入門]Streamlit 〜 Pythonによる実践Webサービス開発』(2024年2月刊)から理解・分解・再構築して作成したものです。
ご購入はこちらから:【Honto,amazon.co.jp,ヨドバシ,楽天ブックス】
