ページを読んでできるもの
- Pythonで以下の入力内容のGUIが簡単に作れます
- 基本的な入力(テキストボックス、チェックボックス)
- テキストボックスにはデフォルトで入力が、チェックボックスにはチェックが入っています
- 入力された内容をポップアップで表示する
公式の日本語対応について
公式サイトが日本からのユーザが増えたとのことでreadmeの日本語翻訳に対応しました!!
https://github.com/PySimpleGUI/PySimpleGUI/blob/master/readme.ja.md
Tkinterについて
Tkinterは標準のPythonのGUIライブラリです。個人的に考えるメリット、デメリットは以下の通りです。
-
メリット
- 標準だからインストール不要
- サンプルはそこそこ多い
-
デメリット
- ウィジェット(UIパーツ)を配置する際、種類、オプション、レイアウトと順を追って設定しないといけない
- ウィジェットの配置が増えると、レイアウトがコードを見てもよくわからない
Tkinterの例
実際にTkinterについて実際にコードを載せます
PythonでGUIアプリを作る方法【Tkinter】
の記事の最後のコードと実行結果をこちらに転載します
コードは以下になります
import tkinter
from tkinter import messagebox
#ボタンがクリックされたら実行
def button_click():
input_value = input_box.get()
messagebox.showinfo("クリックイベント",input_value + "が入力されました。")
#ウインドウの作成
root = tkinter.Tk()
root.title("Python GUI")
root.geometry("360x240")
#入力欄の作成
input_box = tkinter.Entry(width=40)
input_box.place(x=10, y=100)
#ラベルの作成
input_label = tkinter.Label(text="ラベル")
input_label.place(x=10, y=70)
#ボタンの作成
button = tkinter.Button(text="実行ボタン",command=button_click)
button.place(x=10, y=130)
#ウインドウの描画
root.mainloop()
ウィジェット(UIのパーツ)が縦一列に並んだ場合は比較的簡単です。
問題は横一列に「ラベル」+「テキストボックス」というようなよくあるレイアウトで複数のウィジェットを配置した場合です
コードはいかになります。
import tkinter
from tkinter import messagebox
#ボタンがクリックされたら実行
def button_click():
input_name_value = input_name.get()
input_address_value = input_address.get()
input_phone_value = input_phone.get()
show_message = "名前:" + input_name_value + 'が入力されました。\n'
show_message += "住所:" + input_address_value + 'が入力されました。\n'
show_message += "電話番号:" + input_phone_value + "が入力されました。"
print(show_message)
# 入力内容をポップアップ画面で表示
messagebox.showinfo("入力内容" ,show_message)
#ウインドウの作成
root = tkinter.Tk()
root.title("Python GUI")
root.geometry("360x120")
# 名前
input_name_label = tkinter.Label(text="名前")
input_name_label.grid(row=1, column=1, padx=10,)
# 入力欄の作成
input_name = tkinter.Entry(width=40)
input_name.grid(row=1, column=2)
# 住所
input_address_label = tkinter.Label(text="住所")
input_address_label.grid(row=2, column=1, padx=10,)
# 住所入力欄の作成
input_address = tkinter.Entry(width=40)
input_address.grid(row=2, column=2)
# 電話番号
input_phone_label = tkinter.Label(text="名前")
input_phone_label.grid(row=3, column=1, padx=10,)
# 電話番号入力欄の作成
input_phone = tkinter.Entry(width=40)
input_phone.grid(row=3, column=2)
#ボタンの作成
button = tkinter.Button(text="実行ボタン",command=button_click)
button.place(x=10, y=80)
#ウインドウの描画
root.mainloop()
問題なのは以下のところです。
# 電話番号
input_phone_label = tkinter.Label(text="名前")
input_phone_label.grid(row=3, column=1, padx=10,)
Tkinterはウィジェットを配置する際はまず自分が何(ラベル、ボタン、テキストボックスetc)を設定します。
そしてそのあとに配置する場所を指定します。
Tkinterでグリッドレイアウトを用いる際は、grid関数にrow,columnを使ってレイアウトを調整します。今回は縦×横が3×2のレイアウトなのでまだいいですが、例えば5×10などウィジェットが増えていくと、コードをぱっと見ただけではレイアウトがよくわからなくなります。
PySimpleGUI
PySimpleGUIは2018年から開発が始まったライブラリーです。
- github
- 公式ドキュメント
tkinter、Qt、WxPython、Remiののラッパーで公式ではもとのライブラリーで書く場合を比べて、コード量が2分の1から10分の1程度でかけると書いてあります。
私自身も実際に書いてみてTkinterで書くよりもはるかに短いコード量でかけると思いました。
最大の特徴は、リストを使用して、UIのレイアウトを配置することです。
実際に使用して見ましょう。
インストール方法はpipで簡単にインストールできます
pip install pysimplegui
or
pip3 install pysimplegui
簡単な使い方は以下のとおりです。公式の以下のサンプルをもとに作成しています。
- 1 Shot - Simple Data Entry - Return Values - Auto Numbered
import PySimpleGUI as sg
# セクション1 - オプションの設定と標準レイアウト
sg.theme('Dark Blue 3')
layout = [
[sg.Text('Python GUI')],
[sg.Text('名前', size=(15, 1)), sg.InputText('○○〇×××')],
[sg.Text('住所', size=(15, 1)), sg.InputText('△△△△村')],
[sg.Text('電話番号', size=(15, 1)), sg.InputText('xxx-xxx-xxx')],
[sg.Submit(button_text='実行ボタン')]
]
# セクション 2 - ウィンドウの生成
window = sg.Window('住所を入力', layout)
# セクション 3 - イベントループ
while True:
event, values = window.read()
if event is None:
print('exit')
break
if event == '実行ボタン':
show_message = "名前:" + values[0] + 'が入力されました。\n'
show_message += "住所:" + values[1] + 'が入力されました。\n'
show_message += "電話番号:" + values[2] + "が入力されました。"
print(show_message)
# ポップアップ
sg.popup(show_message)
# セクション 4 - ウィンドウの破棄と終了
window.close()
実行結果はいかになります。
いかがでしょうか?
レイアウトは以下で定義しています。2次元のリストでレイアウトを定義しています。
layout = [
[sg.Text('Python GUI')],
[sg.Text('名前', size=(15, 1)), sg.InputText('○○〇×××')],
[sg.Text('住所', size=(15, 1)), sg.InputText('△△△△村')],
[sg.Text('電話番号', size=(15, 1)), sg.InputText('xxx-xxx-xxx')],
[sg.Submit(button_text='実行ボタン')]
そして取得した値は以下で取得できます
if event == '実行ボタン':
show_message = "名前:" + values[0] + 'が入力されました。\n'
show_message += "住所:" + values[1] + 'が入力されました。\n'
show_message += "電話番号:" + values[2] + "が入力されました。"
詳しくは公式ドキュメントを読むことをおすすめしますが、
Tkinterでは54行でかけます。しかしPySimpleGUIですと28行でかけます。
なにより、Tkinterで書いたものよりもレイアウトや値の取得方法が格段にわかりやすいです。
checkboxを使ってみる
先程のコードにデフォルトでチェックが入っているチェックボックスを追加してみます。
- 公式のcheckboxのリファレンス
コードはいかになります
import PySimpleGUI as sg
# セクション1 - オプションの設定と標準レイアウト
sg.theme('Dark Blue 3')
layout = [
[sg.Text('Python GUI')],
[sg.Text('名前', size=(15, 1)), sg.InputText('○○〇×××')],
[sg.Text('住所', size=(15, 1)), sg.InputText('△△△△村')],
[sg.Text('電話番号', size=(15, 1)), sg.InputText('xxx-xxx-xxx')],
[sg.Checkbox('同意する', default=True)],
[sg.Submit(button_text='実行ボタン')]
]
# セクション 2 - ウィンドウの生成
window = sg.Window('住所を入力', layout)
# セクション 3 - イベントループ
while True:
event, values = window.read()
if event is None:
print('exit')
break
if event == '実行ボタン':
show_message = "名前:" + values[0] + 'が入力されました。\n'
show_message += "住所:" + values[1] + 'が入力されました。\n'
show_message += "電話番号:" + values[2] + "が入力されました。"
show_message += "同意のチェックは:" + str(values[3]) + "です"
print(show_message)
# ポップアップ
sg.popup(show_message)
# セクション 4 - ウィンドウの破棄と終了
window.close()
実行結果はいかになります。
チェックボックスの値はBool方でture,falseで取得できます。
Tkinterでチェックボックスでデフォルトの値を設定しようとおもうとちょっとめんどうだったものが簡単に設定できます。
Tkinterのチェックボックスの設定方法については以下の記事が参考になります。
- PythonのTkinterを使ってみる(最初からChecked,Uncheckedの値をもたせる)
PySimpleGUIは他にもTkinterを生で使うよりも、簡単にウィジェットが使用できます。また公式のサンプルが豊富です。matplotlibやopenCvを使用した例などが豊富にあります。
表示内容の更新方法について
Tkinterの内部の構造については以下の記事が参考になります
- VBAer向け、Python入門用 GUIサンプル
こちらの記事のサンプルではシンプルな例として、tkinterを使用してボタンをクリックするとラベルの内容が更新されるプログラムを紹介しています。
紹介しているプログラムは以下になります。
#1 tkinterモジュールをtkという名前でインポート
import tkinter as tk
#2 ウインドウの生成と変数への格納
root_window = tk.Tk()
root_window.geometry("200x100")
#3 可変文字列の生成と変数への格納
label_text = tk.StringVar()
#4 可変文字列の初期化
label_text.set("Please click the button.")
#5 ボタンが押された時用の関数を予め定義しておく。
def button_clicked():
label_text.set("Button clicked.")
#6 GUIパーツの生成と変数への格納
lbl = tk.Label(
master=root_window,
textvariable=label_text)
btn = tk.Button(
master=root_window,
text="Button",
command=button_clicked
)
#7 GUIパーツの配置
lbl.place(x=5,y=5)
btn.place(x=5,y=30)
#8 ウインドウ表示
root_window.mainloop()
個人的にはちっともシンプルに思えないです・・・
理由としては、tkinterは元々Tcl/Tk の Tk 部分を Python で利用できるようしているので使いかたがPython的ではないです。
例えば、tkintrではウィンドウサイズをroot_window.geometry("横x縦")
という風に縦と横の数値を小文字のエックス(x)で記述した文字列でないといけないです。Pythonで記載するのであれば(縦,横)
のタプルで記載したいです。
またラベルの表示などの文字列をこうする場合にはウィジェットを設定する際にあらかじめTk.StringVar()
を使わないといけないです。
覚えないといけないことが多くて大変です。
もっと簡単にわかりやすく書きたいです。
PySimpleGUIで書くと以下のようになります
import PySimpleGUI as sg
sg.theme('Dark Green 6')
layout = [[sg.Text('Please click the button', key='-TEXT-')],
[sg.Submit(button_text='実行ボタン')]]
# ウィンサイズはsizeに(縦の大きさ,横の大きさ)で記載
window = sg.Window(title='show input text', size=(200, 100)).Layout(layout)
# イベントループ
while True:
event, values = window.read()
if event is None:
break
if event == '実行ボタン':
update_text = "Button clicked."
# 表示内容を更新する際はウィジェットに指定されたkeyの値に.update("文字列")を入れることで可能
window['-TEXT-'].update(update_text)
window.close()
実行結果は以下になります
わかりやすいですね。
exe化について
exe化はPyInstllerを使用して簡単にできます
以下は公式の以下の説明からの引用です
- Creating a Windows .EXE File
単一ファイルの作り方は以下になります
pip install PyInstaller
pyinstaller -wF my_program.py
-wF
をつけることで単一ファイルができます
また以下のエラーが出た場合は、--hidden-import tkinter
をオプションに加えます。
ValueError: script '.......\src\tkinter' not found
Macだと以下でできるそうです(公式では確かめていないそうです)
pyinstaller --onefile --add-binary = '/ System / Library / Frameworks / Tk.framework / Tk': 'tk' --add-binary = '/ System / Library / Frameworks / Tcl.framework / Tcl': ' tcl 'your_program.py
この情報はRedditにあり、ソースはhttps://github.com/pyinstaller/pyinstaller/issues/1350 から引用しています。
複雑なレイアウトについて
PySimpleGUIでは入れ子の構造のような複雑なレイアウトも簡単に書くことができます。
例えば、住所を1つのテキストボックスに記載していますが、「郵便番号」、「都道府県」、「住所」という風に細かく分けてみます。
このような感じになるかと思います。
実行時
これは以下の内容で実現できます
import PySimpleGUI as sg
# セクション1 - オプションの設定と標準レイアウト
sg.theme('Dark Blue 3')
frame1 = sg.Frame('住所', [[sg.Text('郵便番号', size=(15, 1))],
[sg.InputText(key='-POST-NUM-')],
[sg.Text('都道府県', size=(15, 1), )],
[sg.InputText(key='-PREFECTURES-')],
[sg.Text('住所', size=(15, 1))],
[sg.InputText(key='-ADDRESS-')],
],
relief=sg.RELIEF_SUNKEN, tooltip='住所をいれてね!')
layout = [
[sg.Text('Python GUI')],
[sg.Text('名前', size=(15, 1)), sg.InputText(
default_text='○○〇×××', key='-USER-NAME-')],
[sg.Text('住所を入れてね', size=(15, 1)), frame1], # frame1のレイアウトを入れ子にして入れている
[sg.Text('電話番号', size=(15, 1)), sg.InputText(
default_text='xxx-xxx-xxx', key='-PHONE-NUM-')],
[sg.Submit('実行ボタン')]
]
# セクション 2 - ウィンドウの生成
window = sg.Window('住所を入力', layout)
# セクション 3 - イベントループ
while True:
event, values = window.read()
if event is None:
print('exit')
break
if event == '実行ボタン':
print(values)
show_message = "名前:" + values['-USER-NAME-'] + 'が入力されました。\n'
show_message += "郵便番号:" + values['-POST-NUM-'] + 'が入力されました。\n'
show_message += "都道府県:" + values['-PREFECTURES-'] + 'が入力されました。\n'
show_message += "住所:" + values['-ADDRESS-'] + 'が入力されました。\n'
show_message += "電話番号:" + values['-PHONE-NUM-'] + "が入力されました。"
# ポップアップ
sg.popup(show_message)
# セクション 4 - ウィンドウの破棄と終了
window.close()
住所の部分は変数frame1
リストとして別に定義して、layout
リストに入れています。
こうすることで入れ子のような複雑なレイアウトも簡単にできます。
frame1 = sg.Frame(layout=[[sg.Text('郵便番号', size=(15, 1))],
[sg.InputText('', key='post_num')],
[sg.Text('都道府県', size=(15, 1), )],
[sg.InputText('', key='prefectures')],
[sg.Text('住所', size=(15, 1))],
[sg.InputText('', key='address')],
],
title='住所', relief=sg.RELIEF_SUNKEN, tooltip='住所をいれてね!')
またウィジェット(pySimpleGUIではElementと呼びます)の値ですがkeyを指定することで該当の値をそのkey名で取得できます。
print(values)
{0: 'テスト太郎', 'post_num': '123-456', 'prefectures': 'VVV県', 'address': 'XXXX市○○○○', 1: 'xxx-xxx-xxx'}
見た目について
公式のMore Ways to "Dress Up Your Windows"で以下の3つの要素の説明があります。
- ウィンドウの透明度の設定
- タイトルバーの非表示の設定
- ボタンをbase64エンコードで画像に差し替える方法
そのうち「ウィンドウの透明度の設定」、「タイトルバーの非表示」について説明します。
「ボタンをbase64エンコードで画像に差し替える方法」については画像ファイルを直接指定して適用する方法もありますので、それとまとめて後日、個別に説明します。
### ウィンドウの透明度の設定
実行結果は以下になります。透明度を50%に設定して実施
window
エレメントにalpha_channel
の要素を1~0.01で指定することで透明度が設定できます。今回ですと以下のように設定しています。
window = sg.Window('住所を入力', layout, alpha_channel=.5)
小数点表記の場合は頭の0はつけなくても動作します。もちろんalpha_channel=0.5
と設定しても動作します。
タイトルバーの非表示の設定
実行結果は以下になります
window
エレメントにno_titlebar
をTrue
で指定することでタイトルバーを非表示に設定できます。ただしドラッグしてアプリの移動ができなくなってしまいます。ドラッグして移動を行うためにはgrab_anywhere
をTrue
を設定します
今回ですと以下のように設定しています。
window = sg.Window('住所を入力', layout, no_titlebar=True, grab_anywhere=True)
ライセンスについて
PySimpleGUIはライセンスはLGPL3.0になります。
MITライセンスではないですが、再配布しない限り社内や個人で利用するにあたっての使用は自由なはずですので通常使用する分には問題がないはずです。
日本語のPySimpleGUIの解説
数が少ないですがPySimpleGUIを日本語での紹介をしている記事があるので紹介します
-
PySimpleGUIでグラフを描く
- PySimpleGUIのみでグラフを書く方法と公式サンプルのmatplotlibとの連携を紹介しています
-
PySimpleGUIでVBAの代わりになるUIをつくってみる(ファイルダイアログ、リスト、ログの出力)
- 公式のサンプルファイルを拡張して作っています
-
PySimpleGUIで画像処理ビューアーを作る
- pillowを使用して処理前、処理後の画像を表示しています。
-
(外部サイト)PySimpleGUIでGUIアプリを作ってみた ~その1~
- ポップアップのカスタマイズ
-
(外部サイト)PySimpleGUIでGUIアプリを作ってみた ~その2~ View実装
- オブジェクト指向でコードを書いてみた話
-
(外部サイト)PySimpleGUIでGUIアプリを作ってみた ~その3~ イベント処理
- eventをif文で判定せずにハンドラーで行っています。個人的にお勧め
-
(外部サイト)HPLCのデータ整理を自動化してみた (Python, GUI)
- 化学系、HPLC(高速液体クロマトグラフィー)のデータ整理ようのアプリを作られたとのこと
-
(外部サイト)PySimpleGUI ポップアップ一覧
- オブジェクト指向(MVP)でコードを書かれています
-
(外部サイト)マイブームPySimpleGUIを紹介
- 短いサンプルをいくつ書かれています
-
(外部サイト)PySimpleGUIでマインスイーパ
- マインスイーパーを作成されています
-
(外部サイト)PySimpleGUIが今キてるかもしれない
- 公式のストップウォッチの改良
今後の予定
時間があれば公式のサンプルプログラムからいくつかの紹介とTkinterで作られたGUIをPySimpleGUIで書き直した場合のサンプルを書こうかと考えています。
参考 Macでのtkinterのインストールについて
Macでpyenvを使用する場合は、tkinterを使用するとエラーになることがあります。
その際は以下の方法でインストールするとよいと思いますのでリンクを貼っておきます
Macでの使用について
Macでの使用ですが、tkeinterをインストールするのは手間ががかるのと日本語を入力する場合IMEが開かないという問題があります。
Macの場合はPySimpleGUIWx(wxPythonのラッパー)を使用した方がよいかもしれません。
wxPythonの場合は日本語の入力でも問題はありませんでした。
基本的な使い方としてはwxPythonとPySimpleGUIWxをインストール後に
import PySimpleGUI as sg
となっているところを、
import PySimpleGUIWx as sg
とすれば同じコードで実行が可能になります。※PySimpleGUIWxをインストール前にwxPythonをインストールする必要があります
以下が実行結果になります。左がPySimpleGUIWxで実行した画面、右がPySimpleGUIでの実行画面になります。