「Pythonで自分用に作ったツールを、誰かに使ってもらうには、どうしたら良いのか?」その一番分かりやすい答えが、GUIの操作画面を用意することだ。今回は、手軽にGUIを作るライブラリPySimpleGUIを利用して、自作ツールに分かりやすい設定画面を追加する方法を紹介する。
誰でも使えるツールにするにはGUIを用意するのが近道
前回、PDFの作成ツールを作った。実はこのツール、友人に頼まれて作ったものだった。友人と話していて「こんなツールがあったら良いのに!」と言っていたので、筆者が本連載のネタにもなると思って「それなら簡単に作れるよ」と安請け合いしたのだった。しかし、実際に使ってもらったところ、「使い方が全く分からない、使いたくない」と言われてしまったのだった。
なお、前回を読んでもらうと分かるのだが、指定フォルダに画像ファイルをコピーして、実行ファイルをダブルクリックするとPDFファイルが作成されるという仕組みだった。Pythonをインストールするのは大変だろうと、PyInstallerを使ってWindowsの実行ファイルに変換したものを渡したのだが、プログラムを実行すると、真っ黒の画面(Pythonの実行画面)が出て「ok」と表示されるだけだったのがいけなかったようだ。
そんな訳で、筆者が友人からの評判を落とした敗因は次の3点だ。
- 実行しても真っ黒の画面しか表示されないため、心理的に不安にさせてしまったこと
- フォルダに画像をコピーしてから実行するという仕様が分かりにくかったこと
- 詳しい使い方を説明しなかったこと
以上の点を反省して、前回作成したツールを「誰でも使える」という点に留意して改良してみよう。
PySimpleGUIで設定画面を作ろう
まずは、実行して黒い画面しか表示されないという点を改良してみよう。この手のツールと言えば、以下のように画像ファイルを選択するリストボックスや保存先ファイルを指定するテキストボックスを配置したウィンドウが表示されるのが一般的だろう。
こうした、PythonでGUI画面を作るには、PySimpleGUIというライブラリを使うと便利だ。PySimpleGUIはtkinterをベースに開発されたGUIライブラリであり、WindowsだけでなくmacOSやLinuxでも同じように動かすことができるのもメリットだ。 本連載の73回目でもPySimpleGUIの簡単な使い方を紹介した。今回は、PySimpleGUIをもう少し深堀りして、画像をPDFに変換するダイアログを作成しよう。
PySimpleGUIをインストールして簡単なプログラムを作ろう
まずは、PythonパッケージのPySimpleGUIをインストールしよう。
# --- Windowsの場合 ---
python -m pip install pysimplegui
# --- macOSの場合 ---
python3 -m pip install pysimplegui
それでは、簡単なウィンドウの利用例を確認してみよう。最初にプログラムの実行画面を確認してからプログラムを見てみよう。
このプログラムは「カレー」か「ラーメン」を尋ねるプログラムだが、二つのボタンを配置してボタンを押した時にメッセージがポップアップするものにした。以下がそのプログラムだ。
import PySimpleGUI as sg
# レイアウトを作成 --- (*1)
layout = [
[sg.Text('今日は何を食べますか?')],
[sg.Button('カレー'), sg.Button('ラーメン')]
]
# ウィンドウを作成 --- (*2)
window = sg.Window('テスト', layout)
# イベントループ --- (*3)
while True:
event, values = window.read() #イベントを取得 --- (*4)
# 「カレー」ボタンが押されたか? --- (*5)
if event == 'カレー':
sg.popup('カレー美味しいよね!');
break;
if event == 'ラーメン':
sg.popup('ラーメン美味しいよね');
break;
# 閉じるボタンが押されたか?
if event == sg.WIN_CLOSED:
break
window.close()
上記のプログラムを「curry_or_ramen.py」という名前でファイルを保存しよう。そして、ターミナル(WindowsならPowerShell、macOSならターミナル.app)を起動して、以下のコマンドを実行しよう。
# --- Windowsの場合 ---
python curry_or_ramen.py
# --- macOSの場合 ---
python3 curry_or_ramen.py
それでは、プログラムを確認してみよう。
プログラムを実行するには、ターミナル(WindowsならPowerShell、macOSならターミナル.app)を起動して、以下のコマンドを実行しよう。
プログラムを確認しよう。プログラムの(*1)では、レイアウトを作成する。PySimpleGUIの良いところだが、レイアウトを二次元のリストで表現できる。以下の図を見れば、非常に直感的にGUIのパーツを定義できることが分かるだろう。
(*2)のsg.Windowメソッドでウィンドウを作成できる。(*1)で指定したレイアウトを第2引数に与える。
(*3)ではイベントループを実行する。ウィンドウは作成するだけでは駄目で、イベントループを実行することで、ユーザーの操作に対していろいろな処理を実行できるようになる。
(*4)では、window.read()でイベントデータを取得する。(*5)以降ではイベントに応じた処理を記述する。ここでは、どのボタンが押されたのかを判定する。eventにはボタンのラベルが入っている。そして、sg.popupでダイアログにメッセージを表示できる。
ファイルダイアログを表示するプログラムを作ろう
以上で基本が分かったことだろう。次にファイルを選んでテキストボックスに表示するプログラムを作ってみよう。次の図のような動作をするプログラムを作ってみよう。
以下のプログラムを実行すると入力ボックスと「参照」と「処理実行」というボタンの配置されたウィンドウが表示される。そこで、「参照」ボタンを押すと、ファイルの選択ダイアログが表示される。ファイルを選び「処理実行」ボタンを押すと、選択したファイルをダイアログに表示するというプログラムだ。
import PySimpleGUI as sg
# レイアウトを作成 --- (*1)
layout = [
[ sg.InputText(key='-FILE-'), sg.Button('参照') ],
[ sg.Button('処理実行') ]
]
window = sg.Window('テスト', layout)
# イベントループを実行
while True:
# イベントを取得 --- (*2)
event, values = window.read()
print(event, values) # デバッグ用
# イベントに応じた処理を実行 --- (*3)
if event == sg.WIN_CLOSED: break
if event == '参照':
# ファイル選択ダイアログを表示 --- (*4)
f = sg.popup_get_file('ファイルを選択してください')
# ファイルを選択したか? --- (*5)
if f is not None:
# ファイルを表示 --- (*6)
window['-FILE-'].update(f)
if event == '処理実行':
# 選択したファイルを取得 --- (*7)
f = values['-FILE-']
sg.popup('次のファイルを選びました:' + f)
break
# 終了処理
window.close()
上記のプログラムを「simple_dialog.py」という名前で保存する。そして、ターミナルで以下のコマンドを実行するとウィンドウが表示される。
# --- Windowsの場合 ---
python simple_dialog.py
# --- macOSの場合 ---
python3 simple_dialog
プログラムを確認しよう。(*1)ではレイアウトを作成する。(*2)ではイベントループの中で、イベント情報を取得する。そして、(*3)以降でイベントに応じた処理を行う。
(*4)では「参照」ボタンを押した時の処理を記述している。sg.popup_get_fileメソッドを実行すると、ファイルの選択ダイアログが表示される。そして、(*5)でファイルを選択したか確認し、(*6)では、window[キー].update(内容)を実行して、keyを指定したパーツの内容を更新する。
そして、(*7)のようにイベント時にInputTextに表示されている内容を取得することができる。
PDF生成ツールのUIを作成しよう
ここまで来れば、冒頭で紹介したように、前回作成したコマンドラインから使うPythonのプログラムにGUIを追加することができるだろう。完成したプログラムはちょっと長くなったので、ポイントだけ抜粋して紹介しよう。プログラム全体は、こちらのGitHubにアップロードしておいた。
それで画面機能のみを抜粋したのが以下のプログラムだ。「mock.py」などの名前で保存して、ターミナル上で「python mock.py」を実行すれば、ウィンドウを動かすことができるようにした。
import PySimpleGUI as sg
# 画面レイアウトを定義 - 最初にパーツを定義 --- (*1)
frame_pdf_path = [
[
sg.InputText('', enable_events=True, key='-OUT-', size=(90,10)),
sg.FileSaveAs('保存先の変更', file_types=(('PDFファイル', '*.pdf'),), target='-OUT-'),
]
]
frame_images = [
[ sg.Listbox([], size=(100,10), enable_events=True, key='-LIST-') ],
[
sg.Button('画像を追加'),
sg.Button('画像をクリア'),
],
]
frame_per_page = [
[ sg.Combo(['2', '4', '6', '8'], default_value='6', key='-PERPAGE-')],
]
# メインのレイアウト --- (*2)
layout = [
[ sg.Frame('画像の一覧', frame_images) ],
[ sg.Frame('1ページに割付ける枚数', frame_per_page) ],
[ sg.Frame('PDFの保存先', frame_pdf_path) ],
[ sg.Button('PDF作成'), sg.Button('終了') ],
]
window = sg.Window('画像一覧からPDF作成ツール', layout)
# イベントループ
image_files = []
while True:
event, values = window.read()
if event == sg.WIN_CLOSED: break
# 「画像を追加」のボタンをクリックしたとき --- (*3)
if event == '画像を追加':
files = sg.popup_get_file(
'画像を選択してください',
no_window=True,
file_types=(('画像ファイル', '*.*'),),
multiple_files=True)
if files != None:
image_files.extend(files)
window['-LIST-'].update(image_files)
if event == '画像をクリア':
window['-LIST-'].update([])
image_files = []
window.close()
プログラムを確認してみよう。(*1)以降の部分では、画面レイアウトを定義している。frame_pdf_pathやframe_iamges、frame_per_pageは、画面の各部を表すフレームを個別に定義している。