18
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

PySimpleGUIを使ってプレゼン用カウントダウンタイマーを作った

Last updated at Posted at 2021-02-13

#序論
男もすなるPySimpleGUIといふものを女もしてみむとてするなり(大嘘)

初投稿です。

#背景・目的
研究室の卒論修論発表会でzoomに表示するためのプレゼンタイマーが必要でした。
指定の発表時間が終わるとリセットされ、質問時間として継続するものです。
既存のものにはjavascriptによるブラウザ表示のもの(こちらのサイト)があったため、使わせていただきました。
本稿では覚えたてのPythonとGUIを実装するためのPySimpleGUIライブラリを使い、PC用のカウントダウン型のプレゼン用タイマーアプリを作ることを目的としました。

※zoomにて画面共有を行う場合、ブラウザ上のツールバーや通知など公の場では好ましくない(かもしれない)ものが映り得ます。上記のリンク先にて示されているように画面共有にはOBSを使用し画面の一部を切り抜いて映す、という動画配信者の如き手間がかかります。

#実装
not 情報系 and C入門挫折マンなので可読性とか諸々についてはごめんなさい。

環境:Windows10, python3.9.0
(LinuxMint20 mateでの動作を確認(2021/02/17))

始めに実装に用いるライブラリを確認しておきます。
手元にアラーム音源があればplaysoundライブラリを使って流すこともできます。無ければ章末のAppendix.を参照してください。

pip install PySimpleGUI, playsound

【アラーム音を使いたい場合】
「alarm」検索でフォーカスされるコメントアウトを解除し、while文冒頭該当箇所に音源のパスを入れてください。
以下に記載する音無し版はここ(Github)にも上げています。

PresentationTimer_nosound.py
#! python3.9.0
import PySimpleGUI as sg
import time
# 時間切れ時に手元のalarm音源mp3 or wmvを流す際は以下と節々のコメントアウト解除
# from playsound import playsound

def time_as_int():
    return int(round(time.time() * 100))

# GUIテーマ。sg.theme_previewer()で使えるテーマを確認できる。
sg.theme('Reddit')

# tab1(表示部)
layout1 = [
    # 表示テキスト font設定は各々の環境を参照してください。
    [sg.Text('', size=(10,1), font=('Bauhaus 93',80), justification='c', pad=((30,30),(20,0)), key='text')],
    [sg.Text('', size=(28,1), font=('Helvetica',30), justification='c', pad=((0,0),(0,10)) ,key='add_text')],
    # 開始/停止・リセット・終了ボタン
    [sg.Button('Run', button_color=('white', 'Blue'), key='-RUN-PAUSE-'),
    sg.Button('Reset', button_color=('white', 'Green'), key='-RESET-'),
    sg.Exit(button_color=('white', 'Darkred'), key='-QUIT1-')]]

# tab2(設定部)
layout2 = [
    # 発表時間設定・質問時間設定。後者が要らなければそのままでおk
    # 発表部。デフォルトはカップ麺の待ち時間
    [sg.Text('*OPTIONAL*\nInput the time title:',size=(15,2), justification='c', pad=((0,0),(10,10))), sg.InputText(default_text='Presentation time' ,key='first_title')],
    [sg.Combo([i for i in range(0,60)] + [''], default_value='3',size=(5,7) , font=('Helvetica', 10), pad=((125,0),(0,0)), key='min'),
    sg.Text('min.', font=('Helvetica', 10)),
    sg.Combo([i for i in range(0,60)] + [''], default_value='0',size=(5,7), font=('Helvetica', 10), key='sec'),
    sg.Text('sec.', font=('Helvetica', 10))],
    [sg.Text('')],
    # 質問部。要らなくてもそのままでいい
    [sg.Text('*OPTIONAL*\nInput the additionnal time title:',size=(15,3) , justification='c', pad=((0,0),(0,0))), sg.InputText(default_text='Question time' ,key='second_title')],
    [sg.Combo([i for i in range(0,60)] + [''], default_value='0',size=(5,5) , font=('Helvetica', 10), pad=((125,0),(0,0)), key='add_min'),
    sg.Text('min.', font=('Helvetica', 10)),
    sg.Combo([i for i in range(0,60)] + [''], default_value='0',size=(5,5), font=('Helvetica', 10), key='add_sec'),
    sg.Text('sec.', font=('Helvetica', 10))],
    [sg.Exit(button_color=('white', 'Darkred'), key='-QUIT2-', pad=((0,0),(20,0)))]
    ]

#タブ化
layout = [[sg.TabGroup([[sg.Tab('Timer', layout1), sg.Tab('Settings', layout2)]])]]

# 表示。タイトル無し、ボタンリサイズ無し、最前面表示、どこ掴んでもいい
window = sg.Window('Running Timer', layout,
    no_titlebar=True,
    auto_size_buttons=False,
    keep_on_top=True,
    grab_anywhere=True,
    element_padding=(0, 0))

# 変数の初期化
start_time, paused_time, paused = 0, 0, True
first_flag = True
add_word = ''
hold_time, hold_flag = 0, True
# alarm_flag = False

while True:
#    if alarm_flag:
#        playsound('alarm.mp3')) # ※音源のパスが必要※
#        alarm_flag = not alarm_flag

    if not paused:  # Run状態 10msで読み込み、与えた時間とその変化を記述・追加時間がある場合の制御
        event, values = window.read(timeout=10)
        current_time = values['min'] * 100 * 60 + values['sec'] * 100
        add_time = values['add_min'] * 100 * 60 + values['add_sec'] * 100
        current_time += start_time - time_as_int()
        if current_time < 0: # 監視している時間変数が0を下回ると追加時間を加算
            if hold_flag:
                hold_time = current_time
                hold_flag = not hold_flag
            current_time += add_time
            add_word = values['second_title']
            if current_time > 0: # 追加時間があれば時間説明の書き換え
                if current_time - add_time == hold_time:
                    add_word = values['second_title']
                    current_time = 0
                    # alarm_flag = not alarm_flag
            else: # 追加時間がなければ0とする
                event = '-RUN-PAUSE-'
                current_time = 0
                # alarm_flag = not alarm_flag

    else:   # 待機(pause)状態 起動時の諸々を処理
        event, values = window.read(timeout=20)
        if first_flag:
            add_word = values['first_title']
            current_time = values['min'] * 100 * 60 + values['sec'] * 100
            window['text'].update('{:02d}:{:02d}'.format((current_time // 100) // 60, (current_time // 100) % 60))

    window['text'].update('{:02d}:{:02d}'.format((current_time // 100) // 60, (current_time // 100) % 60))
    window['add_text'].update(add_word)

    if event in (sg.WIN_CLOSED, '-QUIT1-'):    # QUIT押下
        break
    if event in (sg.WIN_CLOSED, '-QUIT2-'):
        break

    if event == '-RESET-':  # RESET押下
        paused_time = start_time = time_as_int()
        current_time = values['min'] * 100 * 60 + values['sec'] * 100
        add_word = values['first_title']
        # alarm_flag = False

    elif event == '-RUN-PAUSE-':    # 0秒 or Run/Pause押下
        if first_flag: # 起動時の諸々を処理
            paused_time = time_as_int()
            window['-RUN-PAUSE-'].update('Pause')
            paused = not paused
            first_flag = not first_flag
            start_time = time_as_int()
            continue

        paused = not paused
        if paused:
            paused_time = time_as_int()
        else:
            start_time += time_as_int() - paused_time
        window['-RUN-PAUSE-'].update('Run' if paused else 'Pause')

window.close()

タイマーってすごいんだなあ

#結果
##イメージ
tab1.jpg
tab2.jpg
##動作
timer.gif
という感じに作ってみました。
もう少し簡単になるんじゃないですかね(他人事)

###アラーム音を加える場合の注意
現状音が鳴っている間は00:00表示なので、追加時間がある場合はその時間だけ遅れて表示されます。
(e.g. 追加設定時間1分 and 音源長さ2秒 => 0:58から表示)
この辺りのアレンジはお任せします。

#参考文献
PySimpleGUI公式のカウントアップタイマーデモ(Github)
実装部のベースは上記のカウントアップタイマーを参照しました。

PySimpleGUIの基本的な使用方法(Qiita)
細かいPySimpleGUIの設定は上記を参照しました。

Pythonでサウンドを扱う(Qiita)
そうなんだ

#Appendix.
次のフリー効果音素材サイトにアラーム音あり
効果音ラボ

18
26
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
18
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?