【Python】簡単!PySimpleGUIの画面遷移の方法3選!

【Python】簡単!PySimpleGUIの画面遷移の方法3選!

早速ですが、今回は意外と知らない「PySimpleGUIでの画面遷移の方法」を3種類紹介していきます。

画面遷移方法について、各々の画面遷移方法のメリットデメリット、そして実際に動作するサンプルコードを併せて説明していきます。

以降で記載しているコードは全て、以下の環境で動作確認済みです。

  • macOS Big Sur version 11.2.2
  • Python 3.7.2
  • PySimpleGUI 4.34.0


それでは紹介していきます。

ウィンドウを作成する関数(クラス)を作り、イベントが押されたらウィンドウを切り替える

一番シンプルで汎用性(はんようせい)が高い方法です。
「簡単に画面遷移をしたい」、「別ウィンドウから返り値が欲しい」といった場合に オススメ の方法です。
また、実装方法を工夫することで複数の画面遷移も実装できます


方法としては、返り値がsg.Windowである、ウィンドウを生成する関数(もしくはクラス)をあらかじめ作成しておき、特定のイベントが押されたら『現在操作しているウィンドウ』を閉じて生成したウィンドウに差し替えることで画面遷移を行います。


こちらの方法のデメリットとしては、複数のウィンドウを生成できても操作できるウィンドウは1つのままということです。
ですので、画面遷移する時は操作していたウィンドウを閉じるか、故意に隠す必要があります。

方法1 まとめ

方法
返り値sg.Windowの関数を作成しておき、イベントが呼ばれたらwindowを切り替える

メリット
✅ 1対1の画面遷移が簡単にできる
✅ 実装方法を工夫すれば複数の画面遷移が可能
✅ 返り値ありのウィンドウが作成できる

デメリット
❌ 複数のウィンドウ画面を表示できるが、操作できるウィンドウは1つのまま(操作する時はウィンドウを閉じるか隠す必要あり)


では、こちらの方法を使用したコード例を紹介します。

サンプルコード 1

こちらのコードでは、「2つのウィンドウ画面が、互いに画面遷移を行う」という実装になっています。
while文を入れ子にせずに、綺麗な形で画面遷移を実装できるのが利点です。

import PySimpleGUI as sg

def make_main():
    # ------------ メインウィンドウ作成 ------------
    main_layout = [[sg.Text("メインウィンドウ")],
            [sg.Button("Open")],
            [sg.Button("Exit")]]
    return sg.Window("メインウィンドウ", main_layout, finalize=True)

def make_sub():
    # ------------ サブウィンドウ作成 ------------
    sub_layout = [[sg.Text("サブウィンドウ")],
            [sg.Button("Close")],
            [sg.Button("Exit")]]
    return sg.Window("サブウィンドウ", sub_layout, finalize=True)

# 最初に表示するウィンドウを指定する。
window = make_main()

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

    if event == sg.WIN_CLOSED or event == "Exit":
        break

    # Openボタンが押された場合
    elif event == "Open":
        # メインウィンドウを閉じて、サブウィンドウを作成して表示する
        window.close()
        window = make_sub()

    # Closeボタンが押された場合
    elif event == "Close":
        # サブウィンドウを閉じて、メインウィンドウを作成して表示する
        window.close()
        window = make_main()

# ウィンドウを終了する
window.close()

Open やCloseのイベントが押されたときに、現在操作しているウィンドウを閉じ、新しく表示したいウィンドウの関数を呼び出して画面遷移を行なっています。

きちんと画面遷移できています。


上のgifではどちらのウィンドウも真ん中に出現しています。
この出現位置を変更したい場合は、windowの引数にあるlocationを指定することで変更可能です。


次のコードは、先程のコードの関数に位置を変更するためのlocationを指定する引数(x, y)を追加しました。
何も指定しない場合(デフォルトで(None, None))だと、真ん中に表示されます。

import PySimpleGUI as sg

def make_main(x=None, y=None):
    # ------------ メインウィンドウ作成 ------------
    main_layout = [[sg.Text("メインウィンドウ")],
            [sg.Button("Open")],
            [sg.Button("Exit")]]
    return sg.Window("メインウィンドウ", main_layout, finalize=True, size=(300, 100), location=(x, y))

def make_sub(x=None, y=None):
    # ------------ サブウィンドウ作成 ------------
    sub_layout = [[sg.Text("サブウィンドウ")],
            [sg.Button("Close")],
            [sg.Button("Exit")]]
    return sg.Window("サブウィンドウ", sub_layout, finalize=True, size=(300, 100), location=(x, y))

window = make_main()

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

    if event == sg.WIN_CLOSED or event == "Exit":
        break

    # Openボタンが押された場合
    elif event == "Open":
        # 現在のwindowの位置情報を取得する
        x, y = window.current_location()
        window.close()
        # サブウィンドウを作成して表示する
        window = make_sub(x, y + 100)

    # Closeボタンが押された場合
    elif event == "Close":
        # 現在のwindowの位置情報を取得する
        x, y = window.current_location()
        window.close()
        window = make_main(x, y - 100)

# ウィンドウを終了する
window.close()

1つ前のウィンドウ(サブウィンドウが表示されている場合は、メインウィンドウを指す)の位置を、current_location()を使って位置情報を取得しています。
current_location()の返り値は、(x, y) のTuple型です。

その位置情報を関数の引数として渡すことで、前回表示されていたウィンドウの位置情報を用いて相対的に位置調整が可能になります。
指定するlocationは、左上を(0, 0) 、右下を(1920px, 1080px)として場所を指定します。
(注意:右下の座標は、PCの画面サイズによって異なります)


今回の場合は、画面遷移したときに以下のようにしています。

  • メインウィンドウからサブウィンドウを表示する時は、y軸を100px足して、少しに表示
  • サブウィンドウからメインウィンドウを表示する時は、y軸を100px引いて、少しに表示


current_locationはとても使い勝手が良いので、覚えておくとPySimpleGUIで操作できる幅が広がると思います。

サンプルコード2

次に紹介するサンプルコードでは、「特定のイベントが押されたら別ウィンドウを表示し、ボタンを押したら返り値を出力する」実装になっています。

サブウィンドウのCloseイベントが押された場合は 返り値としてTrueを返し、それ以外のボタンが押された場合はNoneを返します。

import PySimpleGUI as sg
def make_main():
    # ------------ メインウィンドウ作成 ------------
    main_layout = [[sg.Text("メインウィンドウ")],
            [sg.Button("Open")],
            [sg.Button("Exit")]]
    return sg.Window("メインウィンドウ", main_layout, finalize=True, size=(300, 100))

def active_sub_window():
    # ------------ サブウィンドウ作成 ------------
    sub_layout = [[sg.Text("サブウィンドウ")],
            [sg.Button("Close")],
            [sg.Button("Exit")]]
    sub_window = sg.Window("サブウィンドウ", sub_layout, finalize=True, size=(300, 100))

    res = None #返り値
    while True:
        event, values = sub_window.read()

        if event == sg.WIN_CLOSED or event == "Exit":
            break

        # Closeボタンが押された場合
        elif event == "Close":
            res = True # 返り値をTrueに変更する
            break
    # ウィンドウを終了する
    sub_window.close()
    return res

window = make_main()

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

    if event == sg.WIN_CLOSED or event == "Exit":
        break

    # Openボタンが押された場合
    elif event == "Open":
        # サブウィンドウを表示する。
        res = active_sub_window()
        # print文で返り値を出力する
        print("サブウィンドウから", res, "が返されました。")

# ウィンドウを終了する
window.close()

このコードを実行してメインウィンドウのOpenイベントを押すと、メインウィンドウとサブウィンドウが両方出てきます
デメリットで紹介したように、サブウィンドウが表示されている間は、メインウィンドウのOpenやExitなどのボタンが操作不能になります。


サンプルコード2のメインウィンドウを消したい場合は、次に紹介する「hideメソッドやdisappearメソッドを用いてウィンドウを表示・非表示にする」方法を応用することで擬似的な画面遷移が可能となります。

hideメソッドやdisappearメソッドを用いてウィンドウの表示・非表示を行う。

この方法はウィンドウを表示・非表示にすることで、擬似的に画面遷移したように見せる方法です。
PySimpleGUIには画面の表示・非表示を行なってくれるメソッドが2つ存在します。

  • hideメソッド & un_hideメソッド
  • disappearメソッド & reappearメソッド

緑文字のメソッドが「画面を隠す機能」、青文字のメソッドが「隠れた画面を表示する機能」になっています。

この2つの違いは、Mac環境では再現できませんでしたが、タスクバーのアイコンを非表示にするかどうかの違いです。
hideメソッドを使用するとタスクバーにあるアイコンも非表示にされますが、dissappearメソッドではタスクバーのアイコンは残ったままになります。

こちらのメソッドを用いることで、先程のようにウィンドウを終了させなくても画面を非表示にして隠すことができます。


ただし、「hideメソッドはun_hideメソッド」「disappearメソッドはreappearメソッド」で対応しているので、「hide(disappear)メソッドで画面を非表示にして、reappear(un_hide)メソッドで画面を表示する」という風に混ぜて実行することできません。

必ず対応するメソッド同士を使用して画面の表示・非表示を行いましょう。

方法2 まとめ

方法
hide(disappear)メソッドで画面を隠して、擬似的な画面遷移をする。

メリット
✅ メソッドを呼ぶだけで画面の表示・非表示が簡単にできる。
✅ 複数の画面を一括で表示・非表示ができる。
✅ 実装が簡単でわかりやすい。

デメリット
❌ 複数の画面遷移には向いていない。
❌ 対応していないメソッド同士を使用すると表示・非表示が行えない。

では、画面を表示・非表示する方法を用いたサンプルコードご紹介します。

サンプルコード

このサンプルコードでは、「メイン画面のボタン操作でサブ画面を表示・非表示する」実装になっています。
先程記載した、「hideメソッドで非表示した画面はreappearメソッドで表示することができない」事象はこちらのコードを実行して確認してみてください。

import PySimpleGUI as sg

def make_main(x=None, y=None):
    # ------------ メインウィンドウ作成 ------------
    main_layout = [[sg.Text("メインウィンドウ")],
            [sg.Button("Hide Sub"), sg.Button("UnHide Sub")],
            [sg.Button("Disappear Sub"), sg.Button("Reappear Sub")],
            [sg.Button("Exit")]]
    return sg.Window("メインウィンドウ", main_layout, finalize=True, size=(300, 120), location=(x, y))


def make_sub(x=None, y=None):
    # ------------ サブウィンドウ作成 ------------
    sub_layout = [[sg.Text("サブウィンドウ")],
                [sg.Text("サブウィンドウが現れた!")]
                ]
    return sg.Window("サブウィンドウ", sub_layout, finalize=True, size=(300, 120), location=(x, y))


main_window = make_main(500, 500)
sub_window = make_sub(800, 500)

while True:
    # 全てのウィンドウを読み込む
    window, event, values = sg.read_all_windows()

    if event == sg.WIN_CLOSED or event == "Exit":
        break

    # Hideボタンが押された場合
    elif event == "Hide Sub":
        sub_window.hide()
        print("Hideボタンで画面が隠されました")

    # UnHideボタンが押された場合
    elif event == "UnHide Sub":
        sub_window.un_hide()
        print("UnHideボタンで画面が現れました")

    # Disapparボタンが押された場合
    elif event == "Disappear Sub":
        sub_window.disappear()
        print("Disappearボタンで画面が隠されました")

    # Reappearボタンが押された場合
    elif event == "Reappear Sub":
        sub_window.reappear()
        print("Reappearボタンで画面が現れました")

実行すると以下の画像のようになります。

ウィンドウの読み込み部分ですが、サンプルコード1、2とは異なっており、sg.read_all_windows()を使用しています。
sg.read_all_windows()では全てのウィンドウのイベントや値を読み込むということをしています。読み込み前に定義したsg.Windowが全て最初に表示され、イベントや値なども全て受け取ることができます。


こちらの読み込み方法では if window == main_window: といったように、操作しているウィンドウ判定も行えるためかなり便利です。

タブ機能を用いて複数画面の移動をする。

特定のイベントなしで簡単に画面遷移したいという方には、ブラウザのように扱えるタブ機能がおすすめです。曜日ごとの画面作成や、種類別の設定画面などに向いている実装になっています。

こちらの方法はとても簡単で、タブのタイトルとレイアウトを決め、グループ化するだけで簡単に実装できます。さらに、先程紹介した画面遷移の方法と組み合わせることで、タブ画面から別の画面へ遷移することも可能です。


唯一のデメリットは、タブの画面サイズが全て共通の大きさになってしまう点です。もし各々のタブによって画面サイズが異なる場合、レイアウトが崩れて表示される可能性があります。

その場合にはレイアウト要素のサイズ調節や、かなりの工夫が必要なのでご注意ください。

方法3 まとめ

方法
タブを複数作成し、画面遷移を行う。

メリット
✅ 実装が簡単で、コードも綺麗に書ける。
✅ 1つの画面のため、タブ間でのイベント操作が簡単にできる。
  (タブ1で入力したものを、タブ2で表示するなどが簡単に行える)
✅ タブの追加や削除が容易。

デメリット
❌ タブのレイアウトがバラバラの場合、サイズの調節が難しい。

最後にタブ機能を用いたサンプルコードご紹介します。

サンプルコード

最後のサンプルコードでは、それぞれ異なるテキストが入力されているタブを3つ設定した実装となっています。

import PySimpleGUI as sg
# サイズが最大のものに統一される。この場合は(12, 5)が最大なのでタブ画面はすべて(12, 5)となる
layout_1 = [[sg.Text("タブ1", size=(12, 1))]]
layout_2 = [[sg.Text("タブ2", size=(12, 3))]]
layout_3 = [[sg.Text("タブ3", size=(12, 5))]]

# タブのグループを作成
main_layout = [[sg.TabGroup([[sg.Tab("タブ1", layout_1),
                            sg.Tab("タブ2", layout_2),
                            sg.Tab("タブ3", layout_3)]]
                            )]]

window = sg.Window("メイン", main_layout)
while True:
    event, value = window.read()
    if event == sg.WIN_CLOSED or event == "Exit":
        break

window.close()

タブ化のやり方は、「タブにしたいレイアウト」と「タブのタイトル」をsg.Tab()で設定し、それをsg.TabGroup()でグループ化しています。とても簡単ですね。


このサンプルコードを実行すると以下の画像のようになります。


グループ化する際の注意点ですが、sg.TabGroup()の引数には1行目にsg.Tabのリストを格納した二次元配列を指定する必要があります。

以下のコードのように設定しましょう。

# TabGroupを設定する時は、二次元配列の1行目にsg.Tabを格納する
sg.TabGroup([[ sg.Tab(),  sg.Tab() ]])


もし、TabGroupに渡す引数が一次元配列の場合は、エラーが発生して画面が表示されなくなります。
ですが、二次元配列で2行目以降にsg.Tabを設定してもきちんと表示されます

PySimpleGUI公式には以下のように書かれています。

「’Layout of Tabs. Different than normal layouts. ALL Tabs should be on first row’(タブのレイアウトは他のレイアウトと異なり、全てのタブは1行目にある必要がある)」

Call reference – PySimpleGUI TabGroup Elementより引用


ですので、TabGroupを使用してグループ化する際には二元配列の1行目に設定しましょう。


またデメリットの箇所で述べたように、全てのタブ画面のサイズはタブレイアウトの中で最大のサイズに統一されます。
今回では(12, 5)「12文字、5行分の意味」が最大サイズのため、(12, 5)のサイズでタブ画面が統一されているのが確認できますね。

タブサイズを変更したい場合は、レイアウトの要素サイズを変更してサイズ調節を行いましょう。

まとめ


いかがだったでしょうか!

今回は3種類の画面遷移の方法を紹介させていただきました。
簡単に実装できるのですが、意外と知らない人が多いと感じたのでまとめてみました。

PySimpleGUIで画面遷移の実装をしたことがない方は、是非この機会に画面遷移を使用して自分だけのGUIを作成してみてはいかがでしょうか。


この記事が誰かの役に立てますように。

PySimpleGUIカテゴリの最新記事

%d人のブロガーが「いいね」をつけました。