見出し画像

時間切れ後もカウントするデスクトップタイマー作ってみた


著者 arata

ユニバ株式会社で週2日働いているOtasuketaiの一員。肩幅が自慢である。プログラミングの部類に興味が強い。AI開発やゲーム開発などを中心に行なっている。仕事はなんでも受けたいと思っている派。

前書き

社内オンラインミーティングの司会進行をOtasuketaiはサポートしている。
時間内に出された議題を効率よく処理するために時間管理をしているが、議題毎に設定していた時間をオーバーしてしまうことがよくあるそうだ。その際に、「タイマーが切れた後にどれほど時間がオーバーしたのかチェックできるデスクトップタイマー」があれば嬉しいという要望をMTGサポートをしているメンバーから聞き、早速そのようなタイマーを作ってみることにした。この記事では、その開発の過程をゆるく語ってみたいと思う。

開発要件

・MacOS High Sierra 10.13.6
・Homebrew版 Python 3.10.7
・pyinstaller 5.4.1
・PySimpleGUI 4.60.3

完成物


Otasuketaiが作ったタイマー。その名も「OtasukeTimer


プログラミングツールの選び方

最近流行りのPythonを用いた。主はAIやスクレイピングでしか使ったことがなかった。デスクトップアプリへの適性を調べるため、Pythonを選んだ。基本的なデスクトップアプリ用の標準搭載のライブラリとしてtkinterがあるが、今回はそれをよりシンプルにしているPySimpleGUIというものを用いた。

PySimpleGUIについて

sg.Tやらsg.Bやらのようにsgの後に名前をつければ、テキストやボタンを配置できる。とても簡単で、UI方面の組み立てはほとんど苦労しないだろう。しかし、ただ並べるだけだ。改行とスペースくらいしか使えないので、デザイン性は非常に低い。今回のタイマーもデザイン性はほとんどない仕上がりとなっている。

タイマーの機能紹介

START/STOPボタン

こちらはタイマースタートするまではスタートボタン、スタート後はストップボタンとして機能している。イベントを設定し、ボタンを押すことによってそのイベントが動くというプログラムにしている。ちなみに、ストップ時にはアラーム音が終了するようになっている。

SETボタン

こちらを押すと、新たなWindowが開くように設定してある。こちらは押された時のイベントとして、関数が呼ばれてWindowが開くのである。
そのWindowは、以下のようになっており、時間とアラーム音の設定をすることができる。これを変数として保持しておくことによって、タイマー画面に設定するのだ。


アラーム音はミーティングの雰囲気や気分に合わせて選ぶことができる



RESETボタン

こちらを押すと、タイマーをSETした時間に戻すことができる。SETした時の時間の変数にアクセスするようになっている。

タイマーの処理の仕方

セットされた時間から経過時間を引いていくことによって、残り時間を表示している。経過時間の方が大きくなった時、その結果の絶対値を取ることによって、時間終了後もカウントを続けてくれる仕様となっている。
何分、何秒は60で割った余りを処理することによって実現。
残り時間で音を鳴らしたり、文字の色を変えるというところをif分によって実現している。
SETで設定した音楽によって、再生する音楽を変えているというのが長ったらしいif分の正体である。
時間切れ後には、sg.popupを扱ってポップアップを表示している。引数などの詳細は調べてみてほしい。

if started:
            remaining_time = abs(end_time - time.time())
            current_min, current_sec = divmod(remaining_time, 60)
            update_display()
            if 62 > end_time - time.time() > 61:
                if run_A == 0:
                    mixer.init()     
                    mixer.music.load("shortAlarm.mp3")
                    mixer.music.play(1)
                    sg.popup_auto_close('1 minute left!')
                    run_A = 1

            if 32 > end_time - time.time() > 31:
                if run_B == 0:
                    mixer.init()     
                    mixer.music.load("shortAlarm.mp3")
                    mixer.music.play(1)
                    sg.popup_auto_close('30 seconds left!')
                    run_B = 1
                
            if 0 > end_time - time.time():
                keika = "Over"
                red = True
                if user_mus:
                    if run_once == 0:
                        if user_mus == ['爽やか']:
                            mixer.init()     
                            mixer.music.load("sawayaka.mp3")
                            mixer.music.play(1)
                            sg.popup_no_buttons('Times up!', auto_close=True, font=[None,320], line_width=200)
                            run_once = 1
                        if user_mus == ['銅鐸']:
                            mixer.init()     
                            mixer.music.load("doutaku.mp3")
                            mixer.music.play(1)
                            sg.popup_no_buttons('Times up!', auto_close=True, font=[None,320], line_width=200)
                            run_once = 1
                        if user_mus == ['鶏']:
                            mixer.init()     
                            mixer.music.load("niwatori.mp3")
                            mixer.music.play(1)
                            sg.popup_no_buttons('Times up!', auto_close=True, font=[None,320], line_width=200)
                            run_once = 1
                        if user_mus == ['恐怖']:
                            mixer.init()     
                            mixer.music.load("alarm.mp3")
                            mixer.music.play(1)
                            sg.popup_no_buttons('Times up!', auto_close=True, font=[None,320], line_width=200)
                            run_once = 1
                        if user_mus == ['小鳥']:
                            mixer.init()     
                            mixer.music.load("kotori.mp3")
                            mixer.music.play(1)
                            sg.popup_no_buttons('Times up!', auto_close=True, font=[None,320], line_width=200)
                            run_once = 1
                else:
                    if run_once == 0:
                        mixer.init()     
                        mixer.music.load("sawayaka.mp3")
                        mixer.music.play(1)
                        sg.popup_no_buttons('Times up!', auto_close=True, font=[None,320], line_width=200)
                        run_once = 1

上の処理のまま時間を引いていくことによって、タイマーがゼロになった後もカウントを続けることができる。カウントしている時は以下のようになる。

文字が赤くなり、Leftの文字がOverに変わる。

上のif文にもある通り、タイマーが残り1分と30秒になった際にpopupの表示とシンプルな音の再生を行うようになっている。
これにより、タイマー切れの前にミーティング参加者が時間を意識しやすくなる。


アイコン

アイコンはOtasuketaiのHayuruさんに作成してもらった。Otasuketai.comのカラーにあわせた配色で、Otasuketaiキャラクターのドントウォーリーズと時計を掛け合わせたデザインだ。

OtasukeTimer アイコン


まとめ

今回はこのようにして、時間切れ後もカウントを続けるタイマーを開発した。プログラム的な部分は非常に単純だが、PySimpleGUIという新たな領域に踏み込めたことは良かった。ただし、やってみて思ったのはPySimpleGUIは簡単な分、自由度が低いということだ。デスクトップアプリを作る際には、別の言語を用いた方がよさそうだ。

OtasukeTimerの使い方まとめ

  1. Setボタンより、タイマーで測りたい時間と好きなアラーム音を設定する

  2. Start/Stopボタンより、タイマーをスタートさせる

  3. 一時停止させたい時はStart/Stopボタン

  4. タイマーの時間が終了し、タイマーを終了したい時はStart/Stopボタン

  5. 最初からスタートさせたい時はResetボタン


こちらのアプリを使用する方法(※uniba社内の方のみ)

今の所Macでしか対応していない。こちらのURLからzipファイルの中にある。ダウンロードを開始すると、破棄するかということを聞かれるが、小さな上矢印から継続を選択してダウンロードを完成させる。OtasukeTimerというアプリケーションをcontrol+クリックで「開く」を選択して開く。これでこの記事で紹介したアプリを使用できる。このアプリは、一度開いた後はPC内のどこに移動していただいても構わない。Applicationフォルダなどに自由に移動して扱いやすいようにして欲しい。移動した後もcontrol+クリックで開くことが必要だ。


現在は社内利用のみ可能だが、ユニバメンバーにはミーティングの時間管理に活用していただければ幸いだ。
他にもこんなアプリが欲しいなど要望があればリクエストください!

アップデート情報

・アラーム音が小さくなりました
・アラーム音が短くなりました
・1分と30秒の通知を無くしました
・残り時間が1分以内の時にタイマーの色が変わるようになりました
・Time's upのスペルの間違いを修正しました