【Streamlit】3段階通知のタイマーアプリを作る

Streamlit
この記事は約12分で読めます。

こんにちは、JS2IIUです。
この記事では、PythonのStreamlitを使って、3段階で通知音を鳴らすタイマーアプリを作成する方法を紹介します。このアプリは、指定した時間にベル音を鳴らすことで、作業時間や休憩時間を知らせてくれます。

Streamlitとは?

Streamlitは、Pythonでデータサイエンスや機械学習のWebアプリケーションを簡単に作成できるオープンソースのフレームワークです。Streamlitを使うことで、Web開発の知識がなくても、PythonのコードだけでインタラクティブなWebアプリを構築できます。

Streamlitの特徴は以下の点が挙げられます。

  • シンプルで直感的なAPI: Pythonのスクリプトに数行のコードを追加するだけで、Webアプリを作成できます。
  • インタラクティブなウィジェット: スライダー、ボタン、テキスト入力などのウィジェットを簡単に追加して、ユーザーとのインタラクションを実現できます。
  • 高速なプロトタイピング: コードを変更するとすぐにブラウザに反映されるため、高速な開発が可能です。

参考記事

Streamlitに関する入門的な内容の記事をいくつか紹介します。ぜひ覗いてみて下さい。

タイマーアプリの作り方

それでは、Streamlitを使って3段階通知のタイマーアプリを作成していきましょう。

必要なライブラリ

まず、以下のライブラリをインストールする必要があります。

Bash
pip install streamlit playsound
  • streamlit: Streamlitの本体です。
  • playsound: 音声ファイルを再生するためのライブラリです。

playsound周りの警告、エラー

playsoundのインストールで警告やエラーが出る場合があります。

Plaintext
  Getting requirements to build wheel ... error
  error: subprocess-exited-with-error
  
  × Getting requirements to build wheel did not run successfully.
  │ exit code: 1
  ╰─> [28 lines of output]
Plaintext
      OSError: could not get source code
      [end of output]
  
  note: This error originates from a subprocess, and is likely not a problem with pip.
error: subprocess-exited-with-error

× Getting requirements to build wheel did not run successfully.
│ exit code: 1
╰─> See above for output.

この場合は、pip install --upgrade wheelを実行してみます。

Bash
pip install --upgrade wheel

これでplaysoundのインストールは正常に行われるはずです。

さらに、playsoundを実行させたときに以下の警告メッセージが表示される場合があります。

Plaintext
playsound is relying on a python 2 subprocess. Please use `pip3 install PyObjC` if you want playsound to run more efficiently.

メッセージにある通り、pip3 install PyObjCを実行します。

Bash
pip3 install PyObjC

これでエラーや警告が出ることなく実行できるようになりました。

コードの解説

以下がタイマーアプリの完全なコードです。

なお、チーン、というベルの音の音声ファイルは別途ご用意ください

Bash
import streamlit as st
import time
from playsound import playsound

# 初期設定
if "start_time" not in st.session_state:
    st.session_state.start_time = 0
if "pause_time" not in st.session_state:
    st.session_state.pause_time = 0
if "remaining_time" not in st.session_state:
    st.session_state.remaining_time = 0
if "running" not in st.session_state:
    st.session_state.running = False
if "first_bell" not in st.session_state:
    st.session_state.first_bell = True
if "second_bell" not in st.session_state:
    st.session_state.second_bell = True
if "third_bell" not in st.session_state:
    st.session_state.third_bell = True

# ページ設定
st.set_page_config(page_title='Bell Timer', page_icon=':clock3:')

# タイマー時間の設定
first_bell_time = st.sidebar.number_input("最初のベル (分)", min_value=1, value=5)
second_bell_time = st.sidebar.number_input("2番目のベル (分)", min_value=first_bell_time, value=10)
third_bell_time = st.sidebar.number_input("3番目のベル (分)", min_value=second_bell_time, value=15)

# ボタンの配置
col1, col2, col3 = st.columns(3)
start_button = col1.button("開始")
pause_button = col2.button("一時停止")
reset_button = col3.button("リセット")

# 開始ボタン
if start_button:
    if not st.session_state.running:
        st.session_state.start_time = time.time() - st.session_state.remaining_time
        st.session_state.running = True

# 一時停止ボタン
if pause_button:
    if st.session_state.running:
        st.session_state.pause_time = time.time()
        st.session_state.remaining_time = time.time() - st.session_state.start_time
        st.session_state.running = False

# リセットボタン
if reset_button:
    st.session_state.start_time = 0
    st.session_state.pause_time = 0
    st.session_state.remaining_time = 0
    st.session_state.running = False
    st.session_state.first_bell = True
    st.session_state.second_bell = True
    st.session_state.third_bell = True

# タイマーの実行
if st.session_state.running:
    placeholder = st.empty() 

    while st.session_state.running:
        elapsed_time = time.time() - st.session_state.start_time
        remaining_time_sec = int(third_bell_time * 60 - elapsed_time)

        with placeholder.container():  
            st.header(f"残り時間: {remaining_time_sec // 60:02d}:{remaining_time_sec % 60:02d}")

        if elapsed_time >= first_bell_time * 60 and st.session_state.first_bell:
            playsound("main2/bell.mp3")
            st.session_state.first_bell = False
        if elapsed_time >= second_bell_time * 60 and st.session_state.second_bell:
            playsound("main2/bell.mp3")
            st.session_state.second_bell = False
        if elapsed_time >= third_bell_time * 60 and st.session_state.third_bell:
            playsound("main2/bell.mp3")
            st.session_state.third_bell = False
            st.session_state.running = False
            st.success("タイマー終了!")

        time.sleep(1)  

# 停止中
elif st.session_state.remaining_time > 0:
    remaining_time_sec = int(st.session_state.remaining_time)
    st.header(f"残り時間: {remaining_time_sec // 60:02d}:{remaining_time_sec % 60:02d}")

プログラムの詳細

  1. 初期設定: st.session_state を使用して、アプリの状態を管理します。タイマーの開始時間、一時停止時間、残り時間、実行状態、各ベルの鳴動状態を保持します。
  2. ページ設定: st.set_page_config でページのタイトルとアイコンを設定します。
  3. タイマー時間の設定: st.sidebar.number_input を使用して、ユーザーが3つのベルの時間を設定できるようにします。min_value を設定することで、入力値に制限を加えています。
  4. ボタンの配置: st.columns を使用して、開始、一時停止、リセットボタンを並べて配置します。
  5. ボタンの処理: 各ボタンが押されたときの処理を記述します。
    • 開始ボタン: タイマーを開始します。
    • 一時停止ボタン: タイマーを一時停止します。
    • リセットボタン: タイマーをリセットします。
  6. タイマーの実行: st.session_state.running が True の間、タイマーを実行します。
    • st.empty() でプレースホルダーを作成し、残り時間を表示します。
    • while ループで残り時間を更新し、設定された時間になったら playsound でベル音を鳴らします。
  7. 停止中: タイマーが停止中の場合、残り時間を表示します。

参考

補足

  • bell.mp3 などの音声ファイルを main2 フォルダに配置してください。
  • このコードを timer_app.py などのファイル名で保存し、ターミナルで streamlit run timer_app.py を実行すると、ブラウザでアプリを開くことができます。

このブログ記事が、Streamlitを使ったタイマーアプリ開発の参考になれば幸いです。

Pythonに関する書籍のです。

24年9月に出版された「ハイパーモダンPython-信頼性の高いワークフローを構築するモダンテクニック」、Claudio Jolowicz著、嶋田、鈴木訳。開発環境の構築、プロジェクトの管理、テストに関して実践的な内容でとても参考になる一冊です。

最後まで読んでいただきありがとうございました。

コメント

タイトルとURLをコピーしました