ChatGPTがなあ!
やりたかったこと
Excelをこねこねする処理があり、シート名部分一致で処理を実行するため、処理させたいシートの順番を前に持っていきたかった。
……部分一致にさせるな案件と言えばそうなのだけども。
処理終わってからいちいち開いて直すより、GUI上で調整してから処理を開始すればよくね? になり、初のGUI画面作成へ。
ツール選定の経緯
初心者なので、pythonでGUI作るにはどんなフレームワークがいいのか分かりません。
天下のChatGPTに尋ねたところ、「Tkinter」や「PySimpleGUI」が軽くて良いよと教えてもらいました。
そしてPySimpleGUIを調べてみると、なるほど見やすい分かりやすいこれはいい!
https://qiita.com/dario_okazaki/items/656de21cab5c81cabe59
是非とも使いたかったのですが……なんと商用利用は有料(無償でもアカウント登録必須)になってしまったようで、類似のTkEasyGUIを使うことにしました。
https://daisuke20240310.hatenablog.com/entry/tkeasygui
2024年4月うまれのピチピチちゃん。
成果物
GUIさぱらんなのでサンプルコピペしながら動作を把握した後にChatGPTに任せました。
8割くらいChatGPT製。
エラーが出たとこだけ修正しました。
PySimpleGUIで作ってくださいと言って出してくれたコードをTkEasyGUIで使うようにしました。
とはいえ完全な互換性はないようなので、複雑な処理を書きたいときは要注意。
import TkEasyGUI as sg
# import PySimpleGUI as sg
import xlwings as xw
import pandas as pd
# Excelファイルを開く関数
def open_excel_file(file_path):
try:
read_book = pd.ExcelFile(file_path)
sh_name = read_book.sheet_names # sheet_names メソッドで Excel ブック内の各シートの名前をリストで取得
app = xw.App(visible=False, add_book=False)
wb = app.books.open(file_path)
return wb, sh_name
except Exception as e:
sg.popup_error(f"Excelファイルを開く際にエラーが発生しました:{str(e)}")
return None, []
# シートを並べ替える関数
def reorder_sheets(wb, sheet_names, from_index, to_index):
try:
# シートを移動する
sheet_to_move = wb.sheets[from_index]
new_name = wb.sheets[from_index].name
# sheet_to_move.api.Copy(Before=sheet_to_move.api)
if from_index > to_index:
# 上に移動
sheet_to_move.api.Copy(Before=wb.sheets[to_index].api)
# sg.popup("moto"+str(wb.sheets[from_index+1].name))
wb.sheets[from_index+1].delete()
else:
# 下に移動
sheet_to_move.api.Copy(After=wb.sheets[to_index].api)
# sg.popup("moto"+str(wb.sheets[from_index].name))
wb.sheets[from_index].delete()
# sg.popup("moto"+str(wb.sheets[from_index].name)+" ato"+str(wb.sheets[to_index].name))
wb.sheets[to_index].name = new_name
# ブックを保存して閉じる
wb.save()
wb.close()
sg.popup("シートの並べ替えが完了しました。")
except Exception as e:
sg.popup_error(f"シートの並べ替え中にエラーが発生しました:{str(e)}")
# レイアウト
layout = [
[sg.Text("Excelファイルを選択してください:")],
[sg.Input(key="-FILE-", enable_events=True), sg.FileBrowse()],
[sg.Text("シートの並べ替え:")],
[sg.Listbox(values=[], size=(30, 6), key="-SHEETS-", enable_events=True)],
[sg.Button("上に移動", key="-MOVE_UP-", disabled=True), sg.Button("下に移動", key="-MOVE_DOWN-", disabled=True)],
[sg.Button("終了")]
]
# ウィンドウを作成
window = sg.Window("Excelシート並べ替え", layout)
# イベントループ
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED or event == "終了":
break
elif event == "-FILE-":
file_path = values["-FILE-"]
workbook, sheet_names = open_excel_file(file_path)
window["-SHEETS-"].update(values=sheet_names)
window["-MOVE_UP-"].update(disabled=True)
window["-MOVE_DOWN-"].update(disabled=True)
elif event == "-SHEETS-":
selected_index = sheet_names.index(values["-SHEETS-"][0])
if selected_index > 0:
window["-MOVE_UP-"].update(disabled=False)
else:
window["-MOVE_UP-"].update(disabled=True)
if selected_index < len(sheet_names) - 1:
window["-MOVE_DOWN-"].update(disabled=False)
else:
window["-MOVE_DOWN-"].update(disabled=True)
elif event == "-MOVE_UP-":
selected_index = sheet_names.index(values["-SHEETS-"][0])
reorder_sheets(workbook, sheet_names, selected_index, selected_index - 1)
workbook, sheet_names = open_excel_file(file_path)
window["-SHEETS-"].update(values=sheet_names)
elif event == "-MOVE_DOWN-":
selected_index = sheet_names.index(values["-SHEETS-"][0])
reorder_sheets(workbook, sheet_names, selected_index, selected_index + 1)
workbook, sheet_names = open_excel_file(file_path)
window["-SHEETS-"].update(values=sheet_names)
window.close()
修正したのは大きく二か所。
-SHEETS-のイベント処理とシートを並べ替える関数(def reorder_sheets)です。
一つ目のところは、文字列を渡さなきゃいけない引数に永遠に数値を渡そうとするGPTがいたので手で修正。
こういうエラーが出るから直してクレメンス、と伝えたつもりでも期待通りの回答(エラーが出ないコード)が返ってこなかったのでぜんぜん使いこなせてない。
二つ目のところは、大本の処理でExcelを操作する際、xlwingsを使っていたのでなんとかしてxlwingsを使う処理にさせた次第です。
xlwingsではシート移動できないようなので、コピーを挿入してリネームするという力業で実装しました。
ひな形は作ってもらいましたが、やはりというかエラー吐くコードが出力されてしまったので最終的に自力で修正しました。
xlwingsに拘らない人はopenpyxlを使えば下のように短くかけるのでオススメ。
from openpyxl import load_workbook
# Excelファイルを開く関数
def open_excel_file(file_path):
try:
workbook = load_workbook(file_path)
sheet_names = workbook.sheetnames
return workbook, sheet_names
except Exception as e:
sg.popup_error(f"Excelファイルを開く際にエラーが発生しました:\n{str(e)}")
return None, []
# シートを並べ替える関数
def reorder_sheets(workbook, sheet_names, from_index, to_index):
try:
sheet_name = sheet_names[from_index]
workbook.move_sheet(sheet_name, offset=to_index - from_index)
workbook.save(file_path)
sg.popup("シートの並べ替えが完了しました。")
except Exception as e:
sg.popup_error(f"シートの並べ替え中にエラーが発生しました:\n{str(e)}")
おしまい
PySimpleGUIではOutputのところにprintの結果を出力できるらしいですが、今のところTkEasyGUIではできなさそうです。
そこだけが地味にぐぬぬとなりましたが、あとは大した不満はありません。
強いて言うならテーマでダークにできると嬉しいかなくらい。
ほぼほぼ同一のコード使えて便利でした。
6/13追記
なんか勘違いしていたようなのですが、printでログ出力できますね!
というメモ。
MultilineじゃなくてOutputでもできる。
import TkEasyGUI as eg
from datetime import datetime
logg =eg.Multiline(size=(70, 20), font=('Courier', 10))
layout = [
[eg.Text("printテスト")],
[logg],
[eg.Submit(button_text='ログ入力',key="-input-")]
]
print = logg.print
# Create the window
window = eg.Window("Demo", layout, finalize=True)
while True:
event, values = window.read()
# とじる
if event in [eg.WINDOW_CLOSED, "-close-"]:
# wb.close()
break
if event == '-input-':
now = datetime.now()
ymd = now.strftime("%Y/%m/%d %H:%M:%S")
print('現在の時刻は {0} です'.format(ymd))
ただ、下記サイトの例のように問答無用でprintがoutputに出力することは出来ない模様。
https://shinshin86.hateblo.jp/entry/2021/09/25/092419
問答無用でprintが出力される仕様はクセつよそうだけども。
GUI上とパーツ分けしている部品単体で動かしたときのどちらもいいかんじにprintだせたらなあと思って悩んどります。
のでもうすこし悩みます。