tkinterで繰り返し(再起)処理(順番に表示する)


Goal

pythonのtkinterを初めて使うにあたり、試しに英単語を順番に表示するプログラムを作成したのでその記録。

tkinterとは?

python でGUIパーツなどを生成するためのライブラリ. homebrewでインストール可能.

brew install tcl-tk

pyenvを使っている場合は, 上を行った後、使用したいバージョンのpythonをアンインストール->再インストールしないといけない場合がある模様. 私はそうしないと動かなかった.

繰り返し(再起)処理

インターネット上で検索すると, カウントアップタイマーの実装例がよく出てきます.

指定したlabel(文字とか表示する区域的な部品)の中身の数字を、一秒ごとに変更していくことで実現されており、おおまかには以下のようなアルゴリズム・関数を使っているはず.

class Timer
  def __init__(self):
    - カウントする変数の初期化
    - タイマーの数字を表示するラベルの生成


  def count_up(label):
    - カウントする変数の値をラベルに表示
    - カウントのインクリメント
    - 秒後にcount_up()関数を呼ぶ(再帰的) ---(*)

window = tkinter.Tk() # window生成
count_up_timer = Timer(window)
window.mainloop()

(*)のところでは、tkinterのafter( )メソッドを使用します.
名前のまま、指定時間後に指定した関数を呼び出します.

下記では、数字の代わりに単語を表示したり、特定のところで表示をやめたりする改変を施したもの.

入力した英文の単語を順番に表示するプログラム

何を作ったの

これを実装テーマにした理由は、「Spritz」というReadingアプリを使っていたから.
このアプリはwebページ上の英文を読み込んで、フラッシュ暗算のように一単語ずつ表示していくものです.

Spritzはこちら
文章を読むのに要する時間のかなりの割合が、視点の移動に割かれているという研究結果から、一箇所で単語を順番に表示するようにしたらしい。

要はこのまがい物をtkinerを使って実装しました.(本家は各単語の一文字を赤くして視点を合わせやすくするなど、scientificな工夫がいっぱいある.)
まぁ、再起機能の実装練習としては良いのでは.

実装

webページの文章を読み込む機能は諦めました. テキストボックスに文章をコピペする形式でいきます.
UIはこんなかんじ.

Startボタンを押すと、

上のテキストボックスに入っている文章中の単語が, 1つずつ順番にボタンの下に表示されます. (画像では"Totoro"とでてますね.)

以下コード

# import tkinter 
try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk
import time

class Flash:
    def __init__(self, parent, wpm, words_list, label):
        # variable storing time
        self.count = 0
        # label displaying words
        self.wpm = wpm
        self.words_list = words_list
        # convert wpm to display interval in ms
        self.wpm_interval = int(60/self.wpm * 1000)
        self.label = label
        # self.label = tk.Label(parent, text="***", font="Arial 40", width=25)
        self.label.pack()

    def display_next_word(self):
        # finish the loop when all words are displayed
        if self.count > len(self.words_list)-1:
            self.count = 0
            self.label.destroy()
            return

        # display next word
        self.label.configure(text="%s" % self.words_list[self.count])
        # increment the count
        self.count += 1

        # let tkinter to call self.refresh after 1/wpm sec
        self.label.after(self.wpm_interval, self.display_next_word)


if __name__ == "__main__":

    # define a window
    root = tk.Tk()
    root.geometry("400x300")

    # label for english sentences
    label_text = tk.Label(root, text=u'sentences (copy and paste):')
    label_text.pack()

    # text box for english sentences
    text = tk.Entry(root)
    text.pack()
    text.insert(tk.END, 'My Neighbor Totoro') # initial value

    # label for wpm (word per minutes)
    label_wpm = tk.Label(root, text=u'wpm \n (how many words you read/understand in 1 minutes):')
    label_wpm.pack()

    # text box for wpm
    wpm_box = tk.Entry(root)
    wpm_box.pack()
    wpm_box.insert(tk.END, '250')


    def start_flash():
        # prepare label for displayin words one by one
        label_flash_words = tk.Label(root, text="***", font="Arial 40", width=25)

        # prepare sentences and wpm values
        target_text = text.get()
        print("the number of words : ", len(target_text.split()))
        wpm_int = int(wpm_box.get())

        # start flashing
        flash = Flash(root, wpm = wpm_int, words_list = target_text.split(), label=label_flash_words)
        flash.display_next_word()

    start_btn = tk.Button(root, text='start', command=start_flash)
    start_btn.pack()

    root.mainloop()

afterメソッド自体は高い時間精度を保証するものではないので, 求める場合は別途改変が必要とかなんとか.

プログラム自体の有用性はさておき, tkinterの勉強にはなりました. おしまい.

参考

RIP Tutorial, after