[Python] GUIの翼を手に入れる

Pocket

今日のPython勉強日記のテーマは「GUI」です。

前回、PDFをテキスト化するプログラムを作成しましたが、ファイル名を毎回手入力する必要があり、使い勝手がイマイチでした。普段使いできるように、以下のようなユーザーインターフェースを作りたいと思います。

PythonにはさまざまなGUI用ライブラリがありますが、今回は一番シンプルなPySimpleGUIを使います。手間暇かけることは避け、楽な方へ流されていく感じで進めていきたいと考えております。

まず、前回のPDFテキスト化を関数にまとめましょう。


import os
import fitz

def pdf_to_text(pdf_file):
    message = "The file has been successfully converted."
    if not os.path.exists(pdf_file):
        message = "file not found."
        return message
    # 入力ファイルの拡張子をtxtに変更して出力ファイル名とする(1)
    txt_file = os.path.splitext(pdf_file)[0] + ".txt"
    # テキスト抽出
    doc = fitz.open(pdf_file)
    with open(txt_file, "w", encoding="utf-8") as f:
        for page in doc:
            text = page.get_text("text")
            f.write(text)
    return message

関数「pdf_to_text」にはファイル名を渡します。(1)のところで渡されたファイル名の拡張子を「txt」に変更し、出力先のテキストファイル名としています。つまり、「sample.pdf」というPDFファイルが渡されると、「sample.txt」というテキストファイルに処理結果を保存します。

次にこのツールの画面を作っていきます。ファイル名の入力欄とファイル選択ボタン、変換実行ボタンを用意するだけですのでコードも短いです。


import PySimpleGUI as sg

def main():
    title = 'PDFファイルからテキスト抽出'
    input_file = ''
    # PySimpleGUIのテーマ設定
    sg.theme('Default1')
    # ボタン中央寄せの準備(0)
    column_button = [
        [sg.Button('変換', size=(14, 1), bind_return_key=True)]
    ]
    # レイアウト(1)
    layout = [
        [sg.Text('PDFファイル', size=(14, 1)), 
        sg.Input(input_file, key='input_file'), 
        sg.FilesBrowse('...', file_types=(('PDFファイル', '*.pdf'),))],
        [sg.Column(column_button, justification='c')]
    ]

    # GUI表示(2)
    window = sg.Window(title, layout)
    while True:
        event, values = window.read()
        if event is None:
            break
        if event == '変換':#(3)
            execute(window, values)

    window.close()

if __name__ == '__main__':
    main()

(1)のレイアウトのところでボタンや入力欄などのパーツを並べています。「sg.FilesBrowse」がファイル選択ボタンで、このボタンを押すとファイル選択ダイアログが表示されます。

PDFファイルだけを選択するため、file_typesを設定しています。

ファイルを選択すると、input_fileというテキスト入力欄にファイルのパスが自動的に入力されます。これ、PySimpleGUIが勝手にやってくれます。至れり尽くせりですね。

少し手を加えているところがあるとすれば、「変換」ボタンの配置でしょうか。(0)のところで「column_button」を作成し、layoutの最終行で「sg.Column」内にその「column_button」を置き、「justification=’c’」を設定しています。これは「変換」ボタンを中央寄せで表示するために行っています。この設定なしにボタンを配置すると、左寄せになり、ちょっと見た目が窮屈だったので変更しました。

準備が整ったので、実際にウインドウを表示します。それが(2)のところです。ウインドウに表示するタイトルと先ほど設定したレイアウトを渡せば、画面にウインドウが表示され、「while True:」でイベントのループが始まります。(3)のところで「変換」ボタンが押されたことを捕捉し、関数「execute」を呼び出しています。「execute」を見てみましょう。


# 変換ボタンクリック時の処理
def execute(window, values):
    input_file = values['input_file'] #(1)
    message = pdf_to_text(input_file) #(2)
    sg.popup(message)

この関数にはWindowオブジェクトと入力欄の辞書が渡されます。1このwindowにはinput_fileしか入力欄はありません。(1)のところでinput_fileの値(ファイルのパス)を取得します。(2)でpdf_to_textを呼び出し、input_fileの値を渡して処理を実行します。

最後に返ってきたメッセージを表示します。処理が成功していれば「The file has been successfully converted.」というメッセージが表示されます。指定されたファイルが見つからなかった場合、「file not found.」と表示されます。

以上をまとめると次のようになります。


import os
import PySimpleGUI as sg
import fitz

def pdf_to_text(pdf_file):
    message = "The file has been successfully converted."
    if not os.path.exists(pdf_file):
        message = "file not found."
        return message
    # 入力ファイルの拡張子をtxtに変更して出力ファイル名とする
    txt_file = os.path.splitext(pdf_file)[0] + ".txt"
    # テキスト抽出
    doc = fitz.open(pdf_file)
    with open(txt_file, "w", encoding="utf-8") as f:
        for page in doc:
            text = page.get_text("text")
            f.write(text)
    return message

# ボタンクリック時の処理
def execute(window, values):
    input_file = values['input_file']
    message = pdf_to_text(input_file)
    sg.popup(message)

def main():
    title = 'PDFファイルからテキスト抽出'
    input_file = ''
    # PySimpleGUIのテーマ設定
    sg.theme('Default1')
    # ボタン中央寄せの準備(0)
    column_button = [
        [sg.Button('変換', size=(14, 1), bind_return_key=True)]
    ]
    # レイアウト(1)
    layout = [
        [sg.Text('PDFファイル', size=(14, 1)), 
        sg.Input(input_file, key='input_file'), 
        sg.FilesBrowse('...', file_types=(('PDFファイル', '*.pdf'),))],
        [sg.Column(column_button, justification='c')]
    ]

    # GUI表示(2)
    window = sg.Window(title, layout)
    while True:
        event, values = window.read()
        if event is None:
            break
        if event == '変換':#(3)
            execute(window, values)

    window.close()

if __name__ == '__main__':
    main()

わずか50行ほどのコードで必要な部品を備えたウインドウを表示できるようになりました。

正直、見た目はショボイですが、ここまで作っておけば、これを土台に色んなツールを開発できます。

今回のように何かのファイルを選択し、そのファイルの内容に何らかの処理を加え、自分が求める状態へと加工する、というシチュエーションはさまざまな場面で出現します。状況に応じて「何らかの処理」部分を書き換えるだけで、いくらでもツールを作ることが可能になるのです。今回のコードでいえば、PDFテキスト化の部分(pdf_to_text)を別の処理に置き換えるだけです。

先日、XLIFFファイルから原文、訳文のテキストを抽出という処理を取り上げましたが、こちらのコードに置き換えれば、あっというまにXLIFF抽出ツールができあがります。ファイルのフォーマット変更、テキスト処理、変換、置換、検索など、さまざまな機能を組み込めますので、日常のちょっとした不便を解決する、業務を効率化するツールを「自分の手」で作れるようになります。

「これができたということは、あれもできるしこれもできるな」という風に視界が開ける感じ、プログラミングの楽しみのひとつだと思います。

  1. 今のところ引数のwindowは使っていませんが、将来的に使いそうですので用意しています。 []

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください