Help us understand the problem. What is going on with this article?

Pythonでも簡単にGUIは作れる

PythonだってGUIを作りたい

Pythonで書いたプログラムを実行して使う場合、『GUIで入出力できたら便利なのに…』と思うときはありませんか?

誰かにプログラムを配布する場合でも、CUI(コマンドラインから入出力)はあまり親切とは言えません。
特にITスキルの高くない人にとっては、CUIは拒絶反応を起こすこともあります。

Pythonでも簡単にGUIを作れたら…

そんな場合、PySimpleGuiを使ってみてはいかがでしょうか?
PySimpleGuiは誰でも簡単にGUIを実装できるのが特徴で、PySimpleGui公式ドキュメントによると、PySimpleGuiはすぐに習得でき、コード量も他のGUIライブラリ(Tkinter、Qt、WxPythonなど)の1/2~1/10程度で済むとのこと。

百聞は一見にしかずなので、まずは下記のコードと実行結果をご覧ください。

import PySimpleGUI as sg

sg.theme('DarkAmber')   # デザインテーマの設定

# ウィンドウに配置するコンポーネント
layout = [  [sg.Text('ここは1行目')],
            [sg.Text('ここは2行目:適当に文字を入力してください'), sg.InputText()],
            [sg.Button('OK'), sg.Button('キャンセル')] ]

# ウィンドウの生成
window = sg.Window('サンプルプログラム', layout)

# イベントループ
while True:
    event, values = window.read()
    if event == sg.WIN_CLOSED or event == 'キャンセル':
        break
    elif event == 'OK':
        print('あなたが入力した値: ', values[0])

window.close()

実行結果

pysimplegui_sample.png

ね、簡単でしょう?

コードは大きく5つのステップで構成されています。
各ステップの詳細は後述しますので、まずは全体の流れをざっと見てみましょう。

準備(インストール)

PySimpleGuiは標準ライブラリではないので、まずインストールする必要があります。
下記のpipコマンドでさくっとインストールしましょう。

pip install pysimplegui

コード

ステップ1. インポート

PySimpleGUIをインポートします。このときsgという別名をつけるのが標準的なので、本記事でもその慣例に従います。

import PySimpleGUI as sg

ステップ2. デザインテーマの設定

sg.theme()でGUIのデザインを設定します。
ここでは'DarkAmber'というテーマを設定しましたが、他にも140種類ものデザインテーマが用意されています。

sg.theme('DarkAmber')

ステップ3. ウィンドウの部品とレイアウト

ここがGUIの肝になりますが、ウィンドウに配置する部品(コンポーネント)とレイアウトを設定していきます。

layout = [  [sg.Text('ここは1行目')],
            [sg.Text('ここは2行目:適当に文字を入力してください'), sg.InputText()],
            [sg.Button('OK'), sg.Button('キャンセル')] ]

コンポーネントの配置の仕方は、

layout = [  [1行目のコンポーネント],
            [2行目のコンポーネント],
            [3行目のコンポーネント] ]

というように各行に配置したい部品をカンマで区切って設定します。
直感的でわかりやすいですね。

行の中に複数の部品を配置するときは、次のようにカンマで区切って書きます。

layout = [  [1行目のコンポーネント1, 1行目のコンポーネント2],
            [2行目のコンポーネント1],
            [3行目のコンポーネント1, 3行目のコンポーネント2, 3行目のコンポーネント3,] ]

ステップ4. ウィンドウの生成

sg.Window()にステップ3で作ったlayoutを指定して、ウィンドウを生成します。
このとき、第1引数としてウィンドウタイトルを指定できます。

window = sg.Window('サンプルプログラム', layout)

ステップ5. イベントループ

イベントループの中でイベントの発生を待ちます。
window.read()でイベントを受信します。イベントを受信したら、イベントによって行うべき処理を選択して実行します。

サンプルコードでは、

  • ウィンドウの×ボタンが押された(sg.WIN_CLOSED)→ループを抜けてウィンドウを閉じる(window.close())
  • 'キャンセル'ボタンを押された→ループを抜けてウィンドウを閉じる(window.close())
  • 'OK'ボタンが押された→テキストボックスに入力した値をprint文で表示

というイベント処理を実行しています。

while True:
    event, values = window.read()
    if event == sg.WIN_CLOSED or event == 'キャンセル':
        break
    elif event == 'OK':
        print('あなたが入力した値: ', values[0])

window.close()

GUIを作ってみる

では実際にモノを作りながら説明していきます。

【自動化】PDF内の表をPythonで抜き出す』の記事で紹介したコードを少し改変し、「PDFファイルの指定したページ内の表を抜き出してcsv化するプログラム」をGUI付きで作ってみます。

PDFファイルの指定したページ内の表をDataFrameで抜き出す部分は関数化しておきます。
今回はここは重要ではないので、説明は割愛させていただきます。
詳しくは『【自動化】PDF内の表をPythonで抜き出す』をご覧ください。

import pandas as pd
import tabula

def readTableFromPDF(filePath, isLattice, targetPages):
    '''
    指定されたfilePathのPDFファイルから表を読み込みDataFramleに変換する
    '''
    dfs = tabula.read_pdf(filePath, lattice=isLattice, pages=targetPages)

    # 変換したDataFrameを表示
    for index, df in enumerate(dfs):
        print("----------表"+str(index+1)+"----------")
        print(df.head())

    return dfs

準備(GUIのデザイン)

まずどんなGUIにしたいのかデザインを決めましょう。
紙でも電子でもいいので、デザインを書いてみます。

私はこんなGUIを考えました。

gui_design.png

ステップ1. インポート

PySimpleGUIをsgという名前でインポートします。

import PySimpleGUI as sg

ステップ2. デザインテーマの設定

sg.theme()でGUIのデザインテーマを設定します。

様々なテーマが用意されていますが、sg.theme_previewer()という関数をコールするとテーマ一覧を見ることができます。

sg.theme_previewer() # デザインテーマの一覧を表示

公式サイトも参考に選んでみてください。Look and Feel Theme Explosion

ここではDarkTeal7というテーマで作ることにします。

sg.theme('DarkTeal7')

ステップ3. ウィンドウの部品とレイアウト

GUIのデザイン案を行ごとに考えてみます。
gui_design1.png

全部で6行あるので、下記のようなコードになるわけです。

layout = [  [1行目のコンポーネント],
            [2行目のコンポーネント],
            [3行目のコンポーネント],
            [4行目のコンポーネント],
            [5行目のコンポーネント],
            [6行目のコンポーネント]  ]

では、1行ずつ順番に見ていきましょう。

1行目

gui_design_row1.png

1行目はテキストを表示しているだけですね。
テキストはsg.Text("表示したいテキスト")で配置します。

    [sg.Text('読み取り対象のファイルとページを指定してください')]

2行目

gui_design_row2.png

2行目は3つのコンポーネントを配置します。

■ 1つめのコンポーネント(テキスト)
テキストは1行目と同じでsg.Text()で配置します。
レイアウトによってはコンポーネントのサイズを指定したいこともあると思います。
その場合は、sg.Text('表示したいテキスト', size=("横幅","縦幅"))という形で指定します。

■ 2つめと3つめのコンポーネント(ファイルブラウザボタン)
ファイルをブラウザで選択するためのボタンです。sg.FileBrowse('ボタンに表示したいテキスト')で配置します。
選択したファイルのパスはsg.Input()で配置したボックスに表示します。

各コンポーネントには、そのコンポーネントを識別するための名前をkey='名前'という形で与えることができます。
「ボタンが押された」などのイベントが発生した場合、どのボタンが押されたのかを識別する必要がありますよね。
そのときに必要になるので、イベントハンドリングしたいコンポーネントには名前をつけるようにしましょう。

後ほど、ファイルのパスを取得する必要があるので、sg.FileBrowse()には名前をつけておきます。

    [sg.Text('ファイル', size=(15, 1)), sg.Input(), sg.FileBrowse('ファイルを選択', key='inputFilePath')]

3行目

gui_design_row3.png

3行目も3つのコンポーネントを配置します。

■ 1つめのコンポーネント(テキスト)
sg.Text()で配置します。

■ 2つめのコンポーネント(テキストボックス)
テキストを入力するボックスはsg.InputText()です。
このコンポーネントにも名前をつけておきます。

■ 3つめのコンポーネント(テキスト)
sg.Text()で配置します。
文字数が少し多いので、2行のテキストで表示するためsize=("横幅","縦幅")の"縦幅"に2を指定します。

sg.Text('複数ページのときは\n3-10 のように指定してください', size=(30, 2))

    [sg.Text('ページ', size=(15, 1)), sg.InputText('', size=(10, 1), key='pages'), sg.Text('複数ページのときは\n3-10 のように指定してください', size=(30, 2))]

4行目

gui_design_row4.png

4行目は2つのコンポーネントを配置します。

■ 1つめのコンポーネント(テキスト)
sg.Text()で配置します。

■ 2つめのコンポーネント(プルダウンメニュー)
プルダウンメニューはsg.Combo(("値1","値2",...), default_value="値n")です。
このコンポーネントにも名前をつけておきます。

    [sg.Text('表の罫線', size=(15, 1)),sg.Combo(('あり', 'なし'), default_value="あり",size=(10, 1), key='lattice')]

5行目

gui_design_row5.png

5行目は2つのコンポーネントを配置します。

■ 1つめと2つめのコンポーネント(ボタン)
ボタンはsg.Button('ボタンに表示したいテキスト')で配置します。
ボタンを押されたときのイベントをハンドリングしたいので、これらにもkey='名前'で名前をつけておきます。

    [sg.Button('読み取り', key='read'), sg.Button('csvに保存', key='save')]

6行目

gui_design_row6.png

この行のコンポーネントは1つだけです。
PDFファイルから読み取った表を確認のためにここに出力します。

テキストの出力にはsg.Output(size=("横幅","縦幅"))を使います。

    [sg.Output(size=(80,20))]

コンポーネントまとめ

1~6行目までのコンポーネントをまとめて書きます。

layout = [
    [sg.Text('読み取り対象のファイルとページを指定してください')],
    [sg.Text('ファイル', size=(10, 1)), sg.Input(), sg.FileBrowse('ファイルを選択', key='inputFilePath')],
    [sg.Text('ページ', size=(10, 1)), sg.InputText('', size=(10, 1), key='pages'), sg.Text('複数ページのときは\n3-10 のように指定してください', size=(30, 2))],
    [sg.Text('表の罫線', size=(10, 1)),sg.Combo(('あり', 'なし'), default_value="あり",size=(10, 1), key='lattice')],
    [sg.Button('読み取り', key='read'), sg.Button('csvに保存', key='save')],
    [sg.Output(size=(80,20))]
]

ステップ4. ウィンドウの生成

ステップ3で作成したlayoutでウィンドウを生成します。
ウィンドウのタイトルは「PDFの表を抜き出すツール」とでもしておきましょう。

window = sg.Window('PDFの表を抜き出すツール', layout)

ステップ5. イベントループ

最後のステップです。ここもとても大事なステップですね。

イベントループの中でイベントの発生を待ちます。
window.read()でイベントを受信します。イベントを受信したら、イベントによって行うべき処理を選択して実行します。

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

    if event == sg.WIN_CLOSED: #ウィンドウのXボタンを押したときの処理
        break

    if event == 'イベント名1':
        # イベント名1が発生したときの処理

    if event == 'イベント名2':
        # イベント名2が発生したときの処理

window.close()

window.read()の戻り値であるeventには、各コンポーネントにkey='名前'で与えた名前が入ります。
例えば、「読み取り」ボタンには'read'という名前をつけているので、このボタンが押された場合、eventには'read'という名前が入っています。

valuesには各コンポーネントが持っている値が入っていて、これも各コンポーネントにkey='名前'で与えた名前を指定して取り出すことができます。
例えば、4行目のプルダウンメニュー('あり' or 'なし')の値はvalues['lattice']で取得できます。

今回のイベントハンドリングは下記の2つになります。

イベント イベント名 行う処理
「読み取り」ボタンが押された 'read' readTableFromPDF()を実行する
「csvに保存」ボタンが押された 'save' readTableFromPDF()を実行して、戻り値で得られたDataFrameをcsvとして保存する
while True:
    event, values = window.read()

    if event == sg.WIN_CLOSED: #ウィンドウのXボタンを押したときの処理
        break

    if event == 'read': #「読み取り」ボタンが押されたときの処理
        if values['lattice'] == 'あり':
            isLattice = True
        else:
            isLattice = False

        readTableFromPDF(values['inputFilePath'], isLattice, values['pages'])

    if event == 'save': #「csvに保存」ボタンが押されたときの処理
        if values['lattice'] == 'あり':
            isLattice = True
        else:
            isLattice = False

        dfs = readTableFromPDF(values['inputFilePath'], isLattice, values['pages'])

        for index, df in enumerate(dfs):
            basename_without_ext = os.path.splitext(os.path.basename(values['inputFilePath']))[0] # PDFファイル名を抜き出す
            filename = basename_without_ext + "_"  + str(index+1) +".csv"
            df.to_csv(filename, index=None)
            print("csvファイルに保存しました:", filename)

        print("すべてのcsvファイル保存が完了しました")

window.close()

なお、ここでのprint文の結果は6行目のコンポーネントであるsg.Output()のボックス内に表示されます。

ここまでのGUIコードのまとめ

ステップ1~5までの全コードをまとめておきます。

# ステップ1. インポート
import PySimpleGUI as sg
import os

# ステップ2. デザインテーマの設定
sg.theme('DarkTeal7')

# ステップ3. ウィンドウの部品とレイアウト
layout = [
    [sg.Text('読み取り対象のファイルとページを指定してください')],
    [sg.Text('ファイル', size=(10, 1)), sg.Input(), sg.FileBrowse('ファイルを選択', key='inputFilePath')],
    [sg.Text('ページ', size=(10, 1)), sg.InputText('', size=(10, 1), key='pages'), sg.Text('複数ページのときは\n3-10 のように指定してください', size=(30, 2))],
    [sg.Text('表の罫線', size=(10, 1)),sg.Combo(('あり', 'なし'), default_value="あり",size=(10, 1), key='lattice')],
    [sg.Button('読み取り', key='read'), sg.Button('csvに保存', key='save')],
    [sg.Output(size=(80,20))]
]

# ステップ4. ウィンドウの生成
window = sg.Window('PDFの表を抜き出すツール', layout)

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

    if event == sg.WIN_CLOSED: #ウィンドウのXボタンを押したときの処理
        break

    if event == 'read': #「読み取り」ボタンが押されたときの処理
        if values['lattice'] == 'あり':
            isLattice = True
        else:
            isLattice = False

        readTableFromPDF(values['inputFilePath'], isLattice, values['pages'])

    if event == 'save': #「csvに保存」ボタンが押されたときの処理
        if values['lattice'] == 'あり':
            isLattice = True
        else:
            isLattice = False

        dfs = readTableFromPDF(values['inputFilePath'], isLattice, values['pages'])

        for index, df in enumerate(dfs):
            basename_without_ext = os.path.splitext(os.path.basename(values['inputFilePath']))[0] # PDFファイル名を抜き出す
            filename = basename_without_ext + "_"  + str(index+1) +".csv"
            df.to_csv(filename, index=None)
            print("csvファイルに保存しました:", filename)

        print("すべてのcsvファイル保存が完了しました")

window.close()

完成したGUI

以上のコードを実行すると、下記のようなGUIが表示されます。ほぼデザイン案の通りにできていますね。

gui_pdfreader1.png

PDFファイルとページを指定して、「読み取り」をしてみます。

gui_pdfreader2.png

ちゃんとPDFファイル内のページを読み取れていますね!

やっぱりGUIは楽しくなくちゃ

PySimpleGuiはその名の通りシンプルにGUIを書くことができるので、楽しみながらGUIを制作することができました。
ここで紹介したコンポーネントはごく基本的なものですが、他にも様々なコンポーネントを簡単に作ることができます。
PySimpleGuiの日本語ドキュメントは残念ながら少ないですが、下記のサイトを参照しながら楽しくGUIを作っていただければと思います。

PySimpleGui公式
PySimpleGuiの公式サイト(英語)です。

PySimpleGUIの基本的な使用方法
記事内にリンクされているテキストは現在数少ないPySimpleGuiの日本語テキストだと思います。
私も大いに参考にさせていただきました。

konitech913
自称【自動化番長】。「これ、人間がやる作業じゃなくない…?」という単調&イライラする作業から人類を解放すべくあらゆる自動化を研究中です。
http://ai-kotohajime.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした