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

プログラムの紹介

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

gui_simple.png

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

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

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

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

import PySimpleGUI as sg

my_input = sg.Input('ccc')

一方で、複数要素を縦横に並べる際は、layout に部品の並べ方を指定し、window にします。

    layout1 = [
        [sg.Text('Add entry:')],
        [my_input, sg.Button('add', key='btn_add', bind_return_key=True)],
        # my_input は上で作成した入力欄
        [my_listbox],  # 同様に作った listbox
        [sg.Button('remove', key='btn_remove')],
        [my_multiline]  # 同様に作った multiline
    ]
    window = sg.Window('Image', layout1)

Event 処理

上に述べたように、いくつかのコンポーネントは、コンポーネント内で起こった Event を処理することができます。 コンポーネント作成時に、オプションをつけて、event の有効化 (enable_events=True)と event の key を設定しています。 key は 後で event を識別するために使います。以下はListBox の場合。 上のボタンの例では、event は元々有効になっていて、key のみ設定していました。

    my_listbox = sg.Listbox(values=['aaa', 'bbb'],
                            expand_x=False, expand_y=True, size=(30, 10),
                            enable_events=True, key='listbox')

で、Event の処理ですが、window 毎に GUI 処理ループを書いて、そこで処理する形になっています。 注意してほしいのは、self.window.read() は、別に event が来るのを寝てまっている感じです。テキスト入力待ちの input() などと一緒ですね。 event に関連した情報(例えば、listbox でクリックされたのは、何番目の要素か?とか)は、values に入っています。プログラムを実行して、print() の結果みてみてください。

    def main(self):
        while True:
            event, values = self.window.read()  # Event 待ち
            if event == sg.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()
            if event == 'listbox':
                self.listbox_click(values['listbox'])

        self.window.close()

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

    def add_line(self):
        vals = self.my_listbox.Values
        vals.append(self.my_input.get())
        self.my_listbox.update(vals)
        self.my_listbox.set_vscroll_position(100.0)
  1. 現在の my_listbox の要素を取得し(vals: list)、
  2. my_inputのテキストも取得して、vals に加え
  3. my_listbox の要素更新をおこない、
  4. スクロールバーも最後に

my_input, my_listbox を変数に割り当ててたのは、アクセスを容易にするためです。

コンポーネントの配置2

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

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

    layout1 = [ .... さきほどと一緒 ....]
    layout2 = [ .... 同様 ....]
    layout = [
        [sg.Frame('frameX', layout1), sg.Frame('frameY', layout2)]
    ]
    window = sg.Window('Image', layout)