32
45

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Pythonで簡単なGUIアプリケーションを作る

Last updated at Posted at 2021-07-15

どんな記事?

  • Pythonのライブラリを使ってGUIアプリを作った
  • Excelファイルの読み込みやDB(MySQL)への接続などを行った

はじめに

社外製の業務システムを運用することになりました
ネットワークを介してデータの通信を行うWebアプリケーションではなく、ローカルPCにDB(MySQL)を用意して接続・利用するスタンドアロン型のシステムです

しかしこのシステム、元々社外での利用を想定していない状況で作られたアプリケーションなので、
開発者でない人が使用することを想定していません
よって、利用するパラメータやしきい値の設定は直接DBをいじって変更するしかないようです
またテーブル構成の仕様上、同じ入力値を何回も入力する必要があったりする部分が多々あります

というか、そもそも部署内の方は非エンジニアの人が多く、SQLクエリを記述してデータ更新~といった操作は難しい...
-> 非エンジニアでもDB内のパラメータを簡単に操作できるツールとか作れる?

要は「入力しやすいインタフェースを作る」って感じです

という経緯で、GUIアプリケーションを作ることになりました
自身の知識の定着も兼ねて、記事として残しておきます

機能概要

運用イメージ

  1. 入力シートとしてExcelファイルを作成、このファイルにパラメータ等を入力する
  2. 1で作成したシートを元にCSVを生成
  3. 2で作成したCSVをDBにインポート

という流れで利用するツールとして作成していこうと思います
別にCSV生成を挟まなくても実現できそうなのですが、部署内から「一度入力した設定が上書きされて復元不可能になるのが怖い」との意見が出たので、バックアップ的な役割としてワンクッション置いておきます

2つの機能

入力シートのフォーマットは別として、今回作成するアプリケーションには以下の機能が最低限必要です

  • (パラメータ等を入力した)Excelファイルを元にしてCSVファイル生成
  • 生成したCSVファイルをDBにインポート

できたもの

動作例はこんな感じ

構成

使用したツール・ライブラリなど

  • 使用言語はPython
  • DBとの接続(インポート機能)にはmysql.connectorライブラリを使用
  • Excelファイルへの値の読み書きはopenpyxlライブラリを使用
  • コマンドプロンプトで操作するのは不便なのでGUI作成にPySimpleGUIライブラリを使用
  • python実行環境がないPCでも使えるようパッケージング(exe化)を行う。pyinstallerライブラリを使用

動作環境

  • Python 3.6.1
    • openpyxl 3.0.5
    • PySimpleGUI 4.34.0
    • pyinstaller 4.0
  • MySQL 5.7.19

ソースコード

以下、ソースコード
import openpyxl as ex
import PySimpleGUI as sg
import os
import csv
import datetime
import mysql.connector as mydb

class Person:
    def __init__(self, name: str, mail_address:str, age: str) -> None:
        self.name = name
        self.mail_address = mail_address
        self.age = age

    def toList(self):
        return [
            self.name,
            self.mail_address,
            self.age
        ]

class Persons:
    # ExcelファイルからデータをLoad
    def __init__(self, inputsheet_name: str) -> None:
        self.inputsheet_name = inputsheet_name # データ元のファイル名
        self.persons = []

        wb = ex.load_workbook(f"./InputSheet/{inputsheet_name}")
        ws = wb["Sheet1"]

        for index in range(3, 9999):
            name = ws.cell(index, 3).value
            mail_address = ws.cell(index, 5).value
            age = ws.cell(index, 7).value

            if name is None:
                break

            self.persons.append(Person(name, mail_address, age))

    # Personの属性名をリスト形式で出力
    @staticmethod
    def attributeList():
        return [
            "name",
            "mail_address",
            "age"
        ]

    # 格納されているデータをすべてリスト表示
    def print_all(self):
        print(self.attributeList())
        for p in self.persons:
            print(p.toList())

        return 0

    # csvファイルに出力
    def output_csv(self):
        # List形式でpersonsの情報を格納
        out = []
        out.append(self.attributeList())
        for record in self.persons:
            out.append(record.toList())

        # csv書き出し
        filename = f"persons_{datetime.datetime.now():%Y%m%d_%H%M%S}"
        csvFilePath = f"./csv/{filename}.csv"
        with open(csvFilePath, "w", encoding="utf-8") as f:
            writer = csv.writer(
                f,
                lineterminator="\n",
                escapechar=" "
            )
            writer.writerows(out)

        print(f"Created file {csvFilePath}")
        return 0

# csvデータからDBにインポート
def importCSVTable(csvfile_name):
    # *一部本来の記述から変更しています
    conn = mydb.connect(
        host="localhost",
        port=3306,
        user="sampleuser",
        password="*****",
        database="sample_db",
        allow_local_infile=True
    )

    # DB操作用カーソル
    cur = conn.cursor()

    # 既存レコード全削除
    table_name = "persons"
    cur.execute(f"TRUNCATE {table_name} ;")

    # csvファイルからレコード読み込み
    csvfile_path = f"./csv/{csvfile_name}"
    enclosed = "\""     # 囲み文字
    terminated = ","    # 区切り文字
    lines = "\\r\\n"    # 改行コード
    sql = f"""
        LOAD DATA LOCAL INFILE
            "{csvfile_path}"
        INTO TABLE {table_name}
            FIELDS TERMINATED BY '{terminated}'
            OPTIONALLY ENCLOSED BY '{enclosed}'
            LINES TERMINATED BY '{lines}'
            IGNORE 1 LINES ;
    """
    cur.execute(sql)

    # DBに反映
    conn.commit()
    conn.close()

    return 0

def Layout():
    textbox_width = 50
    textbox_height = 8

    inputsheet_list = os.listdir("./InputSheet")

    tab1_layout = [
        [sg.Text("InputSheet list")],
        [sg.Listbox(
            inputsheet_list,
            size=(textbox_width, textbox_height),
            key="inputsheet"
        )],
        [sg.Button("CREATE csv file", key="create_csv")]
    ]

    csv_list = os.listdir("./csv")
    tab2_layout = [
        [sg.Text("csv list")],
        [sg.Listbox(
            csv_list,
            size=(textbox_width, textbox_height),
            key="select_csv",
            enable_events=True
        )],
        [sg.Button("IMPORT to database", key="import_csv")]
    ]

    layout = [
        [sg.TabGroup(
            [[
                sg.Tab("csv生成", tab1_layout),
                sg.Tab("DBへインポート", tab2_layout)
            ]],
            enable_events=True
        )],
        # Log出力
        [sg.Text("Log")],
        [sg.Output( size=(textbox_width, textbox_height*0.5) )]
    ]

    return layout

def run():
    window = sg.Window(
        title="SampleApp",
        layout=Layout(),
    )

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

        if event is sg.WIN_CLOSED:
            break

        elif event=="create_csv" and len(values['inputsheet'])!=0:
            persons = Persons(inputsheet_name=values['inputsheet'][0]) # Personsインスタンス生成
            persons.output_csv()    # CSV形式で書き出し
            sg.popup("csvファイルへの書き出しが完了しました", title="")

        elif event=="import_csv" and len(values['select_csv'])!=0:
            csvfile_name = values['select_csv'][0]
            print(csvfile_name)
            importCSVTable(csvfile_name)
            sg.popup("DBへのインポートが完了しました", title="")

if __name__ == "__main__":
    run()

まとめ

「直接ではなくExcelファイルなどの入力でDBの中身を変更したい」という要望に対して
pythonを用いて簡単なGUIアプリケーションを作成しました

  • PySimpleGUI
    pythonではGUI作成ライブラリとしてtkinterが標準インストールされているのですが、今回使用したPySimpleGUIはそれと比べ直感的にコーディングでき、公式リファレンスが非常にわかりやすいのでとても使いやすかったです

  • pyinstaller
    コマンドのみで簡単にexeファイルを生成でき、python実行環境が無いPCでもアプリケーションを動作させられるのも良かったです
    パッケージングライブラリはpyinstallerの他にもいくつかあるようですが、pyinstallerはその中でも非常に簡単に利用できるので採用しました
    ただ、生成したexeのファイルサイズが大きめだったり、起動時間が長くなったりするなどの欠点はあります

  • openpyxl
    こちらに関しては別記事でも解説を行っています

32
45
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
32
45

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?