LoginSignup
5
7

More than 3 years have passed since last update.

[PySimpleGUI]Tab切り替え時に画像を切り替える

Last updated at Posted at 2021-04-01

はじめに

PySimpleGUIでのアプリケーション作成時に
複数タブ切り替え時に、どのタブを選択したか参照したい時にちょっと手間取ったのでメモ

PySimpleGUIとは

PythonのGUI作成ライブラリ
PythonではGUI作成を目的とした標準ライブラリとしてtkinterが組み込まれており、PySimpleGUIはそのラッパー

tkinterと比べ直感的にコーディングすることができ、記述量も短いのが特徴
個人的には、公式リファレンスがとても見やすくサンプルも豊富で情報量が多いのが助かります

やりたいこと

  • 1つのアプリケーション内で複数機能が存在、機能ごとにタブでレイアウトを切り替える
  • どんな機能なのかを表す画像をいい感じに表示、タブ切り替え時に画像も切り替えたい

下図のようなイメージです

とりあえず動かしてみる

自分で1からコーディングするのは手間なので、公式リファレンスのCookbookから参考にできそうなサンプルを動かしてみます

import PySimpleGUI as sg

tab1_layout = [[sg.T('This is inside tab 1')]]
tab2_layout = [
    [sg.T('This is inside tab 2')],
    [sg.In(key='in')]
]

layout = [
    [sg.TabGroup([[
        sg.Tab('Tab 1', tab1_layout),
        sg.Tab('Tab 2', tab2_layout)
    ]])],
    [sg.Button('Read')]
]

window = sg.Window('My window with tabs', layout)

while True:
    event, values = window.read()
    print(event,values)
    if event == sg.WIN_CLOSED:           # always,  always give a way out!
        break

動作させるとこんな感じ

タブの切り替えができていますね
上記コードはTabgroup要素を使った最小構成です

これをちょっといじってアプリケーションを作ります

できたもの

import PySimpleGUI as sg
from PIL import Image
import io

# 画像をリサイズ、bytes形式で返す
def load_bytesImg(img_path, size_ratio=1):
    img = Image.open(img_path)
    resize_img = img.resize((int(img.width*size_ratio), int(img.height*size_ratio)))

    output = io.BytesIO()
    resize_img.save(output, format="png")
    return output.getvalue()

# タブごとのレイアウト
text_width, text_height = 40, 5
tab1_layout = [[sg.Text( 'tab1です', size=(text_width, text_height) )]]
tab2_layout = [[sg.Text( 'tab2です', size=(text_width, text_height) )]]
tab3_layout = [[sg.Text( 'tab3です', size=(text_width, text_height) )]]

# 画像データをbytes形式で用意
img = load_bytesImg("./images/func01.png", size_ratio=0.3)

# 全体のレイアウト
layout = [
    [sg.Image(data=img, pad=((220, 10), (0, 0)), key="image")],
    [sg.TabGroup([[
        sg.Tab('Tab 1', tab1_layout, key="tab1"),
        sg.Tab('Tab 2', tab2_layout, key="tab2"),
        sg.Tab('Tab 3', tab3_layout, key="tab3")
    ]], key="tab_group", enable_events=True)],
    [sg.Output( size=(40, 5) )]
]

window = sg.Window('TabGroup sample', layout, margins=(20,20))

# イベントループ
while True:
    event, values = window.read()

    # ウィンドウを閉じたら終了
    if event == sg.WIN_CLOSED:
        break

    # タブ切り替え
    elif event=="tab_group":
        select_tab = values["tab_group"]
        img_name = ""

        if select_tab=="tab1":
            print(f"select tab1")
            img_name = "func01.png"

        elif select_tab=="tab2":
            print(f"select tab2")
            img_name = "func02.png"

        elif select_tab=="tab3":
            print(f"select tab3")
            img_name = "func03.png"

        # 表示する画像を更新
        img = load_bytesImg(f"./images/{img_name}", size_ratio=0.3)
        window["image"].update(data=img)

動作例はこんな感じ

右上に表示する画像はimagesディレクトリ内に配置しています
ディレクトリ構成はこんな感じ

├─tabgroup.py
├─images
    ├─func01.png
    ├─func02.png
    ├─func03.png

以下、コードの解説です

レイアウト定義部分

# タブごとのレイアウト
text_width, text_height = 40, 5
tab1_layout = [[sg.Text( 'tab1です', size=(text_width, text_height) )]]
tab2_layout = [[sg.Text( 'tab2です', size=(text_width, text_height) )]]
tab3_layout = [[sg.Text( 'tab3です', size=(text_width, text_height) )]]

# 画像データをbytes形式で用意
img = load_bytesImg("./images/func01.png", size_ratio=0.3)

# 全体のレイアウト
layout = [
    [sg.Image(data=img, pad=((220, 10), (0, 0)), key="image")],
    [sg.TabGroup([[
        sg.Tab('Tab 1', tab1_layout, key="tab1"),
        sg.Tab('Tab 2', tab2_layout, key="tab2"),
        sg.Tab('Tab 3', tab3_layout, key="tab3")
    ]], key="tab_group", enable_events=True)],
    [sg.Output( size=(40, 5) )]
]

PySimpleGUIにて、レイアウトは基本的にlist形式で定義します
今回使用しているものは以下の通り

  • Text
    • テキストを表示する
    • sizeで幅と高さを指定
  • Image
    • 画像を表示する
    • 画像は パス指定 or bytes形式 で渡す
    • paddingで右寄せにしている
  • TabGroup
    • タブ切り替え要素
  • Output
    • 標準出力要素
    • printなどでテキストを表示することができる

要素の役割や引数などはCall referenceで確認できます

注意しなければならないのがTabGroup要素のenable_eventsで、この値をTrueにしておかなければタブの切り替え時にイベントを検知できません(デフォルトではFalse)

また、load_bytesImg()に関しては後述にて解説します

イベントループ部分

window = sg.Window('TabGroup sample', layout, margins=(20,20))

# イベントループ
while True:
    event, values = window.read()

    # ウィンドウを閉じたら終了
    if event == sg.WIN_CLOSED:
        break

    # タブ切り替え
    elif event=="tab_group":
        select_tab = values["tab_group"]
        img_name = ""

        if select_tab=="tab1":
            print(f"select tab1")
            img_name = "func01.png"

        elif select_tab=="tab2":
            print(f"select tab2")
            img_name = "func02.png"

        elif select_tab=="tab3":
            print(f"select tab3")
            img_name = "func03.png"

        # 表示する画像を更新
        img = load_bytesImg(f"./images/{img_name}", size_ratio=0.3)
        window["image"].update(data=img)

windowオブジェクトを定義、ここでタイトルバーのテキストと先程定義したレイアウトを設定します

全体の流れとしては
イベントループで常にwindow.read()を呼び出し、返されたeventでイベントを検知、valueswindow内の要素を参照、更新する、という感じです

要素へのアクセスはvalues["keyで指定した文字列"]という形式で行えます
今回の例では、layout定義にてkey="tab_group"と指定しているのでvalues["tab_group"]で要素へのアクセスができます
この値が何なのかでどのタブを選択しているかを検知することができます

選択したタブに応じた画像ファイル名を指定、要素を更新し表示に反映させます

画像データのbytes変換部分

Image要素での画像指定は、ファイルのパス(文字列)またはbytes形式で行いますが
画像のリサイズ機能は無いみたいです

画像の表示範囲は指定できても、画像自体のリサイズはできないので
元々レイアウトに合わせた画像を用意する必要があり、ちょっとめんどくさいです

サイズが合ってない画像だとこうなります

表示範囲を大きくはみでてますね
別ツールで画像をリサイズして保存して...とやるのは非効率なのでアプリ内処理でリサイズくらいはやりたいです

今回はload_bytesImg()関数がその処理部分に当たります

# 画像をリサイズ、bytes形式で返す
def load_bytesImg(img_path, size_ratio=1):
    img = Image.open(img_path)
    resize_img = img.resize((int(img.width*size_ratio), int(img.height*size_ratio)))

    output = io.BytesIO()
    resize_img.save(output, format="png")
    return output.getvalue()

この関数でやってることは

  1. 画像をPillow.Imageオブジェクトとして読み込み
  2. Image.resizeメソッドでリサイズ
  3. ioモジュールのByteIOメソッドでbytes形式に変換して返す

です
引数size_ratioの値で拡大縮小倍率を指定しています 画像のアスペクト比は固定です

まとめ

PySimpleGUIを用いて複数機能を搭載した簡単なインタフェースを作成できました
TabGroup要素によるタブ切り替え時のイベント検知、画像のリサイズについて言及している記事があまりなかったので自分用としてまとめてみました

リンク

PySimpleGUI:公式リファレンス
Pillow.Image:公式リファレンス

5
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
7