PySimpleGUI イベント処理 〜応用編〜

PySimpleGUI

久しぶりのPySimpleGUI記事です。

イベント処理については、過去に以下の記事で基本的な考え方や実装方法を紹介していました。

先日上記の記事に開発者の方からコメントがあり、かっこいいイベント処理の方法を教えていただきましたので、こちらの記事で共有したいと思います。

主に以下の2点についてお話します。

  1. 各Elementのkeyに、str以外(タプルやobject)を設定する
  2. objectをkeyとして受けとってイベント実行する

実行環境 Python 3.8.3 PySimpleGUI 4.40

keyの設定

過去記事でkeyには文字列を設定すると記載したのですが、objectやタプルを設定することが可能です。

keyにobjectを設定することで、イベント処理の実装がとても柔軟かつ簡潔になりますので、いくつか例を紹介します。

まずはおさらい(str型)

まずは過去記事のおさらいです。

sg.Buttonのkeyに文字列(str)を設定しておき、イベント受け取り時に、keyの文字列を判定してイベントを振り分けます。

判定にはif文やdictを使うことができます。(dictによる振り分けの詳細は過去記事をご覧ください)

import PySimpleGUI as sg


def str_event():
    print("str1")


def str_event2():
    print("str2")


def main():
    layout = [
        [sg.Text('以前のイベント処理の方法')],
        [sg.Button('keyがstr型', key="str_key")],
        [sg.Button('keyがstr型2', key="str_key2")]

    ]

    window = sg.Window('Sample', layout=layout)

    while True:
        event, value = window.read()

        if event is None:
            break

        # keyの文字列を判定してイベント処理を分岐させる
        elif event == "str_key":
            str_event()
        
        elif event == "str_key2":
            str_event2()

        else:
            continue

    window.close()


if __name__ == "__main__":
    main()

keyに関数を設定する

Pythonでは関数もobjectですので、例えば関数を直接keyに設定することもできます。

window.read()でkeyを受け取れますので、例えばif callable(event):として、関数として実行可能なobjectを受け取った際に、そのままeventを実行するような処理も可能です。

イベントの数が増えてくると、keyとイベントの対応関係を整理するのが大変ですし、key自体の重複へのケアも必要になります。

keyに直接event用の関数を設定することで、そういった複雑性をある程度解消できそうですね。

import PySimpleGUI as sg


def str_event():
    print("str1")


def str_event2():
    print("str2")


def main():
    layout = [
        [sg.Text('新しいイベント処理の方法')],
        [sg.Button('keyがfunction', key=str_event)],
        [sg.Button('keyがfuction2', key=str_event2)]
    ]

    window = sg.Window('Sample', layout=layout)

    while True:
        event, value = window.read()

        if event is None:
            break

        # keyに関数が設定されている場合、設定されている関数をそのまま実行する
        elif callable(event):
            event()

        else:
            continue

    window.close()


if __name__ == "__main__":
    main()

classを作成して、keyにinstanceを設定する

classやそのinstanceもobjectですので、やはりkeyに設定可能です。

classベースでイベントを振り分ける方法は色々考えられますが、以下の用にイベント用のclassを作成してしまうのは1案になるかなと思います。

イベント自体が複雑な場合や、似たようなイベントを複数作成する必要があるときに役立つかもしれません。

イベントが多数ある場合は、タイプごとに基底classを分けておいて、イベント振り分け時に引数の細かい調整などを行うことも可能だと思います。

import abc

import PySimpleGUI as sg


class Event(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def run(self):
        pass


class Event1(Event):
    def run(self):
        print("event1")


class Event2(Event):
    def __init__(self, name: str):
        self.name = name

    def run(self):
        print(self.name)


def main():
    layout = [
        [sg.Text('新しいイベント処理の方法')],
        [sg.Button('keyがinstance', key=Event1())],
        [sg.Button('keyがinstance2', key=Event2("named event"))]
    ]

    window = sg.Window('Sample', layout=layout)

    while True:
        event, value = window.read()

        if event is None:
            break

        # keyにEventのinstanceが設定されている場合にinstanceのメソッドを実行する
        elif isinstance(event, Event):
            event.run()

        else:
            continue

    window.close()


if __name__ == "__main__":
    main()

まとめ

keyの設定と、判定・実行の方法について3つ紹介してみました。

他にはタプルも使えるので、例えばタプルに関数を複数持たせておいて、それぞれ実行する。など、より柔軟なイベント管理ができそうですね。

使い分け

色々方法があると結局どれを使えばいいんだ?というのが必然の悩みになりますが、作成するアプリの規模や好みで使い分ければ良いのかなと思っています。

小さめのアプリであればkeyを文字列として受け取るのがシンプルですし、恐らく実装で困ることも少ないかなと思います。

より複雑なことをしたくなって、文字列のkeyを使ったイベント管理に限界を感じる場合は、今回紹介したような関数・class・タプルなどをkeyに使う方法でのイベント管理を検討してみると良さそうです。

まとめ

  • Elementのkeyにobjectが使える
  • keyにobjectを設定しておくと、window.read()でobjectが受け取れる
  • keyに文字列(str)を使うよりも、柔軟・複雑なことが可能になる
  • 作成するアプリの規模に応じて使い分けると良さそう

久しぶりにPySimpleGUIを触りましたが、色々機能も追加されていてとても楽しかったです。また1つアプリ作りたいなと思います。

コメント

タイトルとURLをコピーしました