コンポーネント配置とイベント処理

プログラムの紹介

まずは簡単なプログラムの紹介。

gui_easy.png

コンポーネントとその配置

いくつかの部品が並んでいます。

  • Text: テキストを表示するだけ
  • Input: テキスト入力をする場所
    • 状態としてテキストを持つ
  • Button: ボタン
    • ボタンを押された際に event 処理
  • ListBox: 複数項目を表示
    • 状態として複数のテキスト要素を持つ
    • 行選択時に event 処理可能
  • MultiLine: 複数行を表示
    • 状態として複数行にわたるテキストを持つ

各部品は、TkEasyGUI のコンポーネントで、こんな感じに作成可能。

import TkEasyGUI as eg

my_input = eg.Input('ccc')

一方で、複数要素を縦横に並べる際は、layout に部品の並べ方を指定し、window にします。 my_input, my_listbox, my_multiline は、後で関数から参照するので、変数名を割り当ててあります。

class SampleA:
    my_time = eg.Text(str(datetime.datetime.now()))
    my_input = eg.Input('ccc')
    my_listbox = eg.Listbox(values=['aaa', 'bbb'],
                            expand_x=False, expand_y=True, size=(30, 10))
    my_multiline = eg.Multiline('', size=(30, 15), background_color='lightblue')

    layout1 = [
        [eg.Text('Add entry:'), my_input,  # my_input は上で作成した入力欄
         eg.Button('add', key='btn_add', background_color='blue', color='white')],
         # add ボタンの event は btn_add
        [my_listbox],  # 先ほど作った listbox

        [eg.Button('remove', key='btn_remove', background_color='orange', color='white')],
        # remove ボタンの event は btn_add
        [eg.Text('log:')],
        [my_multiline]  # 先ほど作った multiline
    ]
    window = eg.Window('SampleA', layout1)

Event 処理

上に述べたように、いくつかのコンポーネントは、コンポーネント内で起こった Event を処理することができます。 key は 後で event を識別するために使います。こちらは、add ボタンの例です。

eg.Button('add', key='btn_add', background_color='blue', color='white')

で、Event の処理ですが、window 毎に GUI 処理ループを書いて、そこで処理する形になっています。

self.window.read() は、event が来るのを寝てまっている感じです。 テキスト入力待ちの input() などと一緒ですね。 event で、起きた event を識別します。先ほどの add ボタンでは btn_add'としましたよね。

event に関連した情報(例えば、listbox でクリックされたのは、何番目の要素か?とか)は、values に入っています。プログラムを実行して、print() の結果みてみてください。

    def main(self):
        while True:
            event, values = self.window.read()  # Event 待ち
            if event == eg.WIN_CLOSED:
                break
            print(f"event='{event}', code={ord(event[0])}, values={values}\n")
            if event == 'btn_add':
                self.add_line()
            if event == 'btn_remove':
                self.del_lines()

        self.window.close()

各メソッドがイベント処理として何をしているのかは、一例だけ紹介します。 add_line() は、要素を追加するためのボタンを押したときの処理ですね。

    def add_line(self):
        word = self.my_input.get()
        lines = self.my_listbox.values
        lines.append(word)
        self.my_listbox.update(lines)
        self.add_log('add: ' + word)
  1. my_inputのテキスト(word)を取得し
  2. my_listbox の要素を取得して、word を追加
  3. my_listbox に反映させる
  4. ついでに、ログ表示に追記

my_input, my_listbox を変数に割り当ててたのは、こんな感じに関数からのアクセスを容易にするためです。

コンポーネントの配置2

部品が増えてくると、レイアウトも複雑にしたいですよね。 simpleC.py は、simpleA と、次ページで紹介する描画系simpleB を window にまとめたモノです。

Frame というのを使って階層的に構成すれば、あまり苦労しなくても大丈夫です。

    layout1 = [ .... さきほどと一緒 ....]
    layout2 = [ .... 同様 ....]
    layout = [
        [eg.Frame('frameA', layout1), eg.Frame('frameB', layout2)]
    ]
    window = eg.Window('SampleC', layout)

定期的処理

GUI のプログラムを書いていると、定期的におこないたい処理などもあります。 例えば、時計を進めるとか、敵キャラを動かすとか。

TkEasyGUI では、event 待ちに time out 設定をすることで、GUI event が起きない時も、 代わりに timeout event を起こして、その処理を書くことができます。以下では、1秒ごとに時間表示を更新しています。正確に1秒ごとに実行される訳ではないですが、気になるなら timeout を適時調整してもらってもよいかと。

event, values = self.window.read(timeout=1000, timeout_key='timeout')  # Event 待ち

if event == 'timeout':
    self.my_time.update(str(datetime.datetime.now()))