質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

2708閲覧

【Python】xlwingsとTkinterを使用してVBAを操作したい

tr0903

総合スコア1

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/08/02 04:47

編集2021/08/02 05:55

前提・実現したいこと

Pythonを使用してVBAを操作したいです。
VBAにて.xmlファイルを編集し、.xlsxファイル形式で別のフォルダに保存するというマクロを作成しました。
これをPythonから実行したいので
まず、Tkinterで作成したGUIで親フォルダをユーザーに指定させ、
それを元に.xmlファイルのフルパス、新しく保存する.xlsxファイルのフルパスを作成し、
VBAに引数として渡して実行という流れを考えました。
マクロが入ったExcelファイルはカレントディレクトリに保存しています。

発生している問題・エラーメッセージ

親フォルダを文字列で指定した場合は問題なく動くのですが、
Tkinterで親フォルダのパスを取得するとVBA側でエラーが出てしまいます。
どうにか解決できないでしょうか。

実行時エラー'1004': ファイル'C://Users/username/Desktop/After/(文字列)にアクセスできません。 次のいずれかの理由が考えられます。 ・ファイル名またはパスが存在しません ・ファイルがほかのプログラムによって使用されています ・保存しようとしているブックと同じ名前のブックが現在開かれています

エラーメッセージではファイル名が英字+数字の文字列になっていますが、
ローカルの値は正しいファイル名になっています。

該当のソースコード

Python

1import os 2from tkinter import * 3from tkinter import ttk 4from tkinter import filedialog 5import xlwings as xw 6 7 8def dirdialog_clicked(): #フォルダ取得用 9 iDir = os.path.abspath(os.path.dirname(__file__)) 10 iDirPath = filedialog.askdirectory(initialdir = iDir) 11 entry.set(iDirPath) 12 13def conductMain(): 14 folder_list = ["Book1","Book2","Book3"] 15 wb = xw.Book(os.getcwd() + os.sep + "Excel_edit.xlsm") 16 macro = wb.macro("edit") 17 for file in folder_list: 18 oldpath = entry.get() + os.sep + "Before" + os.sep + file + ".xml" 19 newpath = entry.get() + os.sep + "After" + os.sep + file + ".xlsx" 20 if os.path.exists(newpath) or not os.path.exists(oldpath): 21 continue 22 macro(oldpath, newpath) 23 wb.app.kill() 24 25 26if __name__ == "__main__": 27 28 # rootの作成 29 root = Tk() 30 root.title("CFU assay copy&rename") 31 32 # Frame1の作成 33 frame1 = ttk.Frame(root, padding=10) 34 frame1.grid(row=0, column=1, sticky=E) 35 36 # 「フォルダ参照」ラベルの作成 37 IDirLabel = ttk.Label(frame1, text="画像フォルダ", padding=(5, 2)) 38 IDirLabel.pack(side=LEFT) 39 40 # 「フォルダ参照」エントリーの作成 41 entry = StringVar() 42 IDirEntry = ttk.Entry(frame1, textvariable=entry, width=30) 43 IDirEntry.pack(side=LEFT) 44 45 # 「フォルダ参照」ボタンの作成 46 IDirButton = ttk.Button(frame1, text="参照", command=dirdialog_clicked) 47 IDirButton.pack(side=LEFT) 48 49 # Frame2の作成 50 frame2 = ttk.Frame(root, padding=10) 51 frame2.grid(row=8,column=1,sticky=E ) 52 53 # 実行ボタンの設置 54 button1 = ttk.Button(frame2, text="実行", command=conductMain) 55 button1.pack(fill = "x", padx=30, side = "left") 56 57 # キャンセルボタンの設置 58 button2 = ttk.Button(frame2, text=("閉じる"), command= root.destroy) 59 button2.pack(fill = "x", padx=30, side = "left") 60 61 root.mainloop()

VBA

1Sub edit(oldpath As String, newpath As String) 2 3 Workbooks.Open oldpath 4 5 '本来はここでセルの値を編集しています 6 7 ActiveWorkbook.SaveAs Filename:=newpath, FileFormat:=xlWorkbookDefault 8 ActiveWorkbook.Close 9 10End Sub

試したこと

Tkinterを使用せずに、指定したいフォルダのフルパスを直接記述した場合は問題なく動きました。

Python

1import os 2import xlwings as xw 3 4mainfolder = r"C:\Users\usename\Desktop\mainfolder" 5folder_list = ["Book1","Book2","Book3"] 6wb = xw.Book(os.getcwd() + os.sep + "Excel_edit.xlsm") 7macro = wb.macro("edit") 8for file in folder_list: 9 oldpath = mainfolder + os.sep + os.sep + "Before" + os.sep + file + ".xml" 10 newpath = mainfolder + os.sep + os.sep + "After" + os.sep + file + ".xlsx" 11 if os.path.exists(newpath) or not os.path.exists(oldpath): 12 continue 13 macro(oldpath, newpath) 14wb.app.kill()

該当ソースコード内のconductMain()をこのように書き換えた場合はエラーが発生しませんでしたが、

Python

1def conductMain(): 2 mainfolder = r"C:\Users\usename\Desktop\mainfolder" 3 folder_list = ["Book1","Book2","Book3"] 4 wb = xw.Book(os.getcwd() + os.sep + "Excel_edit.xlsm") 5 macro = wb.macro("edit") 6 for file in folder_list: 7 oldpath = entry.get() + os.sep + "Before" + os.sep + file + ".xml" #書き換え個所 8 newpath = mainfolder + os.sep + "After" + os.sep + file + ".xlsx" #書き換え個所 9 if os.path.exists(newpath) or not os.path.exists(oldpath): 10 continue 11 macro(oldpath, newpath) 12 wb.app.kill()

このように書き換えるとエラーが発生しましたので、
Excelファイルの保存先にTkinterを用いて取得したパスが含まれているのが原因の可能性が高いと思われます。

Python

1def conductMain(): 2 mainfolder = r"C:\Users\usename\Desktop\mainfolder" 3 folder_list = ["Book1","Book2","Book3"] 4 wb = xw.Book(os.getcwd() + os.sep + "Excel_edit.xlsm") 5 macro = wb.macro("edit") 6 for file in folder_list: 7 oldpath = mainfolder + os.sep + "Before" + os.sep + file + ".xml" #書き換え個所 8 newpath = entry.get() + os.sep + "After" + os.sep + file + ".xlsx" #書き換え個所 9 if os.path.exists(newpath) or not os.path.exists(oldpath): 10 continue 11 macro(oldpath, newpath) 12 wb.app.kill()

補足情報(FW/ツールのバージョンなど)

Windows10(64bit)
python 3.9.5
xlwings 0.24.5
エディタ:Visual Code Studio

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

'C://Users/username/Desktop/After/(文字列)にアクセスできません。

問題点:PATH の区切り文字 \ backward-slash (日本語だと 円マーク) と / forward-slashの違い

Python 内ではどちでらも区切り文字として解釈されますが、
外部のプログラムに渡す場合は、注意が必要です。

Python 内の os.path.exists での存在チェックは通るが、
外部プログラム/ライブラリ側で、正しいファイルのパスを得られていない可能性があります。
print等で変数を表示して、期待する文字列が得られているかどうかを確かめましょう。

解決策: pathlib.Path を使い、プラットフォーム対応のパス区切りを使う。

from pathlib import Path # ... folder = Path(mainfoler) oldpath = folder / "Before" / f"{file}.xml" newpath = folder / "After" / f"{file}.xlsx" # DEBUG: 期待通りの値が得られているか表示して確認。 print(str(oldpath), str(newpath)) if not newpath.exists() and oldpath.exists(): macro(str(oldpath), str(newpath))

windows の場合、場合によっては、区切り文字 \ のエスケープが必要になるかもしれません。

投稿2021/08/02 08:25

編集2021/08/03 08:12
teamikl

総合スコア8760

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tr0903

2021/08/03 07:55

pathlib.Pathを使用することで無事に解決できました! おっしゃる通り、正しいパスが得られてなかったようです。 区切り文字\のエスケープは特に必要ありませんでした。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問