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

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

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

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

Python

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

Q&A

解決済

3回答

181閲覧

tkinterでエクセルをPDF化するアプリの作成

shield_599

総合スコア1

Tkinter

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

Python

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

0グッド

0クリップ

投稿2025/03/28 04:27

実現したいこと

tkinterでエクセルをPDF化するアプリの作成を考えています。

文字数が多かったので不要なコードは一部削除してます。

python

1 2# pdf_converter_standalone.py 3 4import os 5import win32com.client as win32 6import zipfile 7 8folder = r"ローカルのフォルダーを指定" # ← 必要に応じて変数化 9files = [f for f in os.listdir(folder) if f.endswith(".xlsx") and not f.startswith("~$")] 10 11 12for file in files: 13 try: 14 excel = win32.gencache.EnsureDispatch("Excel.Application") 15 wb = excel.Workbooks.Open(os.path.join(folder, file)) 16 if wb is None: 17 print(f"⚠ 開けない: {file}") 18 excel.Quit() 19 continue 20 21 ws = wb.Sheets(1) 22 ws.PageSetup.PrintArea = "B1:Y63" 23 wb.ExportAsFixedFormat(0, os.path.join(folder, file.replace(".xlsx", ".pdf"))) 24 wb.Close(False) 25 26 with zipfile.ZipFile(os.path.join(folder, file.replace(".xlsx", ".zip")), "w") as zipf: 27 zipf.write(os.path.join(folder, file.replace(".xlsx", ".pdf")), arcname=file.replace(".xlsx", ".pdf")) 28 29 excel.Quit() 30 print(f"✅ 完了: {file}") 31 32 except Exception as e: 33 print(f"❌ {file}: {e}") 34 try: excel.Quit() 35 except: pass 36 37

発生している問題・分からないこと

pyファイルで直接実行するれば問題ないのですが、tkinterでアプリ化して、アプリから実行するとエラーになります。

エラーメッセージ

error

1(-2147417851, 'サーバーによって例外が返されました。', None, None)

該当のソースコード

Python

1import tkinter as tk 2from tkinter import filedialog, messagebox 3import json 4import os 5from excel_splitter_logic import run_excel_processing, convert_to_pdf_and_zip 6 7CONFIG_FILE = "folder_config.json" 8 9class ExcelSplitterApp: 10 def __init__(self, master): 11 self.master = master 12 master.title("Excel 分割アプリ") 13 master.geometry("600x350") 14 15 self.config = self.load_config() 16 17 # Excelファイルパス入力 18 tk.Label(master, text="Excelファイルのパス:").grid(row=0, column=0, sticky=tk.W) 19 self.excel_path = tk.Entry(master, width=50) 20 self.excel_path.grid(row=0, column=1) 21 tk.Button(master, text="参照", command=self.browse_excel).grid(row=0, column=2) 22 23 # ロゴ画像パス入力 24 tk.Label(master, text="ロゴ画像パス:").grid(row=1, column=0, sticky=tk.W) 25 self.image_path = tk.Entry(master, width=50) 26 self.image_path.grid(row=1, column=1) 27 tk.Button(master, text="参照", command=self.browse_image).grid(row=1, column=2) 28 29 # 記号ごとの出力フォルダ入力 30 self.symbol_entries = {} 31 self.symbols = ['#', '$', '%', '&', 'Other'] 32 for i, symbol in enumerate(self.symbols): 33 label = "マーク無し" if symbol == "Other" else symbol 34 tk.Label(master, text=f"{label} 用フォルダ:").grid(row=2+i, column=0, sticky=tk.W) 35 entry = tk.Entry(master, width=50) 36 entry.grid(row=2+i, column=1) 37 tk.Button(master, text="参照", command=lambda s=symbol: self.browse_symbol_folder(s)).grid(row=2+i, column=2) 38 self.symbol_entries[symbol] = entry 39 40 # 設定ファイルに保存されたパスがあれば反映 41 if symbol in self.config: 42 entry.insert(0, self.config[symbol]) 43 44 # 処理ボタン 45 tk.Button(master, text="処理開始", command=self.start_processing).grid(row=7, column=1, pady=10) 46 tk.Button(master, text="PDF & ZIP化実行", command=self.run_pdf_converter).grid(row=8, column=1, pady=5) 47 48 def browse_excel(self): 49 path = filedialog.askopenfilename(filetypes=[("Excel files", "*.xlsx;*.xlsm")]) 50 if path: 51 self.excel_path.delete(0, tk.END) 52 self.excel_path.insert(0, path) 53 54 def browse_image(self): 55 path = filedialog.askopenfilename(filetypes=[("Image files", "*.png;*.jpg;*.jpeg")]) 56 if path: 57 self.image_path.delete(0, tk.END) 58 self.image_path.insert(0, path) 59 60 def browse_symbol_folder(self, symbol): 61 path = filedialog.askdirectory() 62 if path: 63 self.symbol_entries[symbol].delete(0, tk.END) 64 self.symbol_entries[symbol].insert(0, path) 65 self.config[symbol] = path 66 self.save_config() 67 68 def start_processing(self): 69 try: 70 source_file = self.excel_path.get() 71 image_path = self.image_path.get() 72 symbol_paths = {symbol: self.symbol_entries[symbol].get() for symbol in self.symbols} 73 run_excel_processing(source_file, symbol_paths, image_path) 74 messagebox.showinfo("完了", "Excelの分割保存が完了しました。") 75 except Exception as e: 76 messagebox.showerror("エラー", f"処理中にエラーが発生しました:\n{str(e)}") 77 78 def run_pdf_converter(self): 79 try: 80 sharp_folder = self.symbol_entries["#"].get() 81 if not os.path.exists(sharp_folder): 82 messagebox.showerror("エラー", "「#」記号用のフォルダが設定されていません。") 83 return 84 convert_to_pdf_and_zip(sharp_folder) 85 messagebox.showinfo("完了", "PDF変換とZIP化が完了しました。") 86 except Exception as e: 87 messagebox.showerror("エラー", f"PDF/ZIP処理中にエラーが発生しました:\n{str(e)}") 88 89 def load_config(self): 90 if os.path.exists(CONFIG_FILE): 91 with open(CONFIG_FILE, "r", encoding="utf-8") as f: 92 return json.load(f) 93 return {} 94 95 def save_config(self): 96 with open(CONFIG_FILE, "w", encoding="utf-8") as f: 97 json.dump(self.config, f, ensure_ascii=False, indent=2) 98 99# アプリ実行 100if __name__ == '__main__': 101 root = tk.Tk() 102 app = ExcelSplitterApp(root) 103 root.mainloop() 104

Python

1import os 2import re 3import zipfile 4import unicodedata 5from openpyxl import load_workbook, Workbook 6from openpyxl.utils import get_column_letter 7from openpyxl.worksheet.cell_range import CellRange 8from openpyxl.drawing.image import Image as XLImage 9from openpyxl.styles import Font, Border, Side 10from PIL import Image as PILImage 11from copy import copy 12import win32com.client as win32 13 14# ---------------------------- 15# ファイル名を安全に整形(記号除去、全角→半角、スペース除去) 16# ---------------------------- 17def sanitize_filename(name: str) -> str: 18 name = unicodedata.normalize("NFKC", name) # 全角 → 半角 19 name = re.sub(r'[\\/:*?"<>|]', '', name) # 禁止記号除去 20 name = name.replace('\u3000', '') # 全角スペース除去 21 return name.strip() 22 23# ---------------------------- 24# PDF + ZIP変換処理(ファイルごとにExcel起動) 25# ---------------------------- 26def convert_to_pdf_and_zip(folder_path): 27 for file in os.listdir(folder_path): 28 if file.endswith(".xlsx") and not file.startswith("~$"): 29 xlsx_path = os.path.join(folder_path, file) 30 pdf_path = xlsx_path.replace(".xlsx", ".pdf") 31 zip_path = xlsx_path.replace(".xlsx", ".zip") 32 33 try: 34 print(f"[処理中] {file}") 35 excel = win32.gencache.EnsureDispatch("Excel.Application") 36 excel.Visible = False 37 38 wb = excel.Workbooks.Open(xlsx_path) 39 if wb is None: 40 print(f"[スキップ] Excelで開けませんでした: {file}") 41 excel.Quit() 42 continue 43 44 try: 45 ws = wb.Sheets("試験成績表") 46 except: 47 print(f"[スキップ] シートが存在しません: {file}") 48 wb.Close(False) 49 excel.Quit() 50 continue 51 52 ws.PageSetup.Zoom = False 53 ws.PageSetup.FitToPagesWide = 1 54 ws.PageSetup.FitToPagesTall = 1 55 ws.PageSetup.PrintArea = "A1:Y63" 56 ws.PageSetup.CenterHorizontally = True 57 ws.PageSetup.LeftMargin = 0.2 58 ws.PageSetup.RightMargin = 0.2 59 ws.PageSetup.TopMargin = 0.2 60 ws.PageSetup.BottomMargin = 0.2 61 ws.PageSetup.HeaderMargin = 0.1 62 ws.PageSetup.FooterMargin = 0.1 63 64 if os.path.exists(pdf_path): 65 os.remove(pdf_path) 66 67 wb.ExportAsFixedFormat(0, pdf_path) 68 wb.Close(False) 69 excel.Quit() 70 71 with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf: 72 zipf.write(pdf_path, os.path.basename(pdf_path)) 73 74 print(f"[成功] PDF+ZIP 完了: {file}") 75 76 except Exception as e: 77 print(f"[エラー] PDFまたはZIPに失敗: {file}: {e}") 78 79

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

chatgptなどでエラーの改善を聞いてみましたが効果がありませんでした。

補足

特になし

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

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

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

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

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

teamikl

2025/03/28 08:13

> 文字数が多かったので不要なコードは一部削除してます。 省かれたコード内に問題があった場合、問題解決できません。 現状のコードから省くのではなく、 質問に掲載するエラーが再現できる・「実行可能な」最小のコードを 新たに作成して質問文に掲載してください。(問題の再現に必要ならば読み込み対象のファイルも)
guest

回答3

0

現状のコードでは run_excel_processing が未定義です。
run_excel_processing を省いて実行したところ、特にエラーメッセージは確認できませんでした。
run_pdf_converter の方は適当なファイルで試したためシートが存在しませんとでましたが、
エクセルの読み込み自体は出来てます。

エラー自体は、windows のCOMサーバーとの通信エラーで
-2147417851 は符号なし 32ビットで エラーコードは 0x80010105 (RPC_E_SERVERFAULT)
エクセルとのやり取りの際に何かエラーがあったとしかわかりません。


確認ポイント

  • 実行環境

 IDEでの実行時は、実行環境・構成を要確認。一度 cmd.exe からの実行を試してみる。

  • run_excel_processing 内部について

 プロセスという名前から、推測ですが別プロセス・スレッドを使っていないかどうか、
マルチプロセス・スレッドの場合は、COMの初期化などを正しく行えているかどうか、
必ず対象のスレッド側で初期化が出来てることを確認します。

よくあるのが、COMの初期化が正しく 利用するスレッド内で行われてないケースですが、
質問のコードの状況(特に削除された部分)が不明瞭なので推測になります。

run_pdf_converter の方で街頭のエラーが出る場合は、情報不足ですので
エラーを再現できるデータ・手順等を詳細に質問に追記してください。

投稿2025/03/28 09:10

teamikl

総合スコア8817

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

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

shield_599

2025/03/31 00:00

ご回答ありがとうございます。 返信が遅くなって申し訳ありません。 全文のコードを送付します。
guest

0

自己解決

使用しているPCでエクセルのエラーが多発しており、別PCでアプリを動作させたところ問題なく動作しました。
エラーの原因はPC側にあったみたいです。

ご回答いただきありがとうございました。

投稿2025/03/31 00:49

shield_599

総合スコア1

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

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

0

ご回答ありがとうございます。
返信が遅くなって申し訳ありません。
全文のコードを送付します。

python

1import os 2import re 3import zipfile 4import unicodedata 5from openpyxl import load_workbook, Workbook 6from openpyxl.utils import get_column_letter 7from openpyxl.worksheet.cell_range import CellRange 8from openpyxl.drawing.image import Image as XLImage 9from openpyxl.styles import Font, Border, Side 10from PIL import Image as PILImage 11from copy import copy 12import win32com.client as win32 13 14# ---------------------------- 15# ファイル名を安全に整形(記号除去、全角→半角、スペース除去) 16# ---------------------------- 17def sanitize_filename(name: str) -> str: 18 name = unicodedata.normalize("NFKC", name) # 全角 → 半角 19 name = re.sub(r'[\\/:*?"<>|]', '', name) # 禁止記号除去 20 name = name.replace('\u3000', '') # 全角スペース除去 21 return name.strip() 22 23# ---------------------------- 24# cm → pixel に変換(96dpi想定) 25# ---------------------------- 26def cm_to_px(cm): 27 return int(cm * 96 / 2.54) 28 29# ---------------------------- 30# ロゴ画像を下寄せで合成(透明背景) 31# ---------------------------- 32def create_bottom_aligned_image(image_path, total_width_cm=6.0, total_height_cm=1.2, image_height_cm=0.7): 33 width_px = cm_to_px(total_width_cm) 34 total_height_px = cm_to_px(total_height_cm) 35 image_height_px = cm_to_px(image_height_cm) 36 37 original = PILImage.open(image_path).convert("RGBA") 38 resized = original.resize((width_px, image_height_px)) 39 40 canvas = PILImage.new("RGBA", (width_px, total_height_px), (255, 255, 255, 0)) 41 paste_y = total_height_px - image_height_px 42 canvas.paste(resized, (0, paste_y), resized) 43 44 resized_path = os.path.splitext(image_path)[0] + "_resized.png" 45 canvas.save(resized_path) 46 return resized_path 47 48# ---------------------------- 49# 指定セルの右側罫線を消す(14–15行目、V–Y列) 50# ---------------------------- 51def remove_right_border(original_border): 52 return Border( 53 left=original_border.left, 54 right=Side(style=None), 55 top=original_border.top, 56 bottom=original_border.bottom, 57 diagonal=original_border.diagonal, 58 diagonal_direction=original_border.diagonal_direction, 59 outline=original_border.outline, 60 vertical=original_border.vertical, 61 horizontal=original_border.horizontal, 62 ) 63 64# ---------------------------- 65# Excel分割・保存処理 66# ---------------------------- 67def run_excel_processing(source_file, symbol_paths, image_path): 68 resized_img_path = create_bottom_aligned_image(image_path) 69 sheet_name = "試験成績表" 70 rows_per_chunk = 63 71 insert_cell = "B55" 72 73 wb_formula = load_workbook(source_file, data_only=False) 74 wb_values = load_workbook(source_file, data_only=True) 75 ws_formula = wb_formula[sheet_name] 76 ws_values = wb_values[sheet_name] 77 total_rows = ws_formula.max_row 78 total_cols = ws_formula.max_column 79 merged_ranges = list(ws_formula.merged_cells.ranges) 80 81 num_chunks = (total_rows + rows_per_chunk - 1) // rows_per_chunk 82 83 for chunk_idx in range(num_chunks): 84 start_row = chunk_idx * rows_per_chunk + 1 85 end_row = min(start_row + rows_per_chunk - 1, total_rows) 86 87 new_wb = Workbook() 88 new_ws = new_wb.active 89 new_ws.title = sheet_name 90 91 for i, row in enumerate(ws_formula.iter_rows(min_row=start_row, max_row=end_row, max_col=total_cols), start=1): 92 for cell in row: 93 val = ws_values.cell(row=cell.row, column=cell.column).value 94 new_cell = new_ws.cell(row=i, column=cell.column, value=val) 95 96 if cell.has_style: 97 new_cell.font = copy(cell.font) 98 new_cell.fill = copy(cell.fill) 99 new_cell.number_format = copy(cell.number_format) 100 new_cell.protection = copy(cell.protection) 101 new_cell.alignment = copy(cell.alignment) 102 103 if cell.row in [14, 15] and 22 <= cell.column <= 25: 104 new_cell.border = remove_right_border(cell.border) 105 else: 106 new_cell.border = copy(cell.border) 107 108 for merge in merged_ranges: 109 if merge.min_row >= start_row and merge.max_row <= end_row: 110 rel_merge = CellRange( 111 min_row=merge.min_row - start_row + 1, 112 max_row=merge.max_row - start_row + 1, 113 min_col=merge.min_col, 114 max_col=merge.max_col 115 ) 116 new_ws.merge_cells(str(rel_merge)) 117 118 for col in range(1, total_cols + 1): 119 col_letter = get_column_letter(col) 120 new_ws.column_dimensions[col_letter].width = 3.25 121 122 for row_idx in range(start_row, end_row + 1): 123 src_dim = ws_formula.row_dimensions.get(row_idx) 124 if src_dim and src_dim.height: 125 new_ws.row_dimensions[row_idx - start_row + 1].height = src_dim.height 126 127 new_ws.add_image(XLImage(resized_img_path), insert_cell) 128 129 b3_value = new_ws.cell(row=3, column=2).value or "" 130 v3_value = new_ws.cell(row=3, column=22).value or "" 131 b3_clean = str(b3_value).rstrip() 132 filename_base = sanitize_filename(b3_clean + str(v3_value)) 133 filename = filename_base + ".xlsx" 134 135 symbol_cell = new_ws.cell(row=5, column=2) 136 symbol = str(symbol_cell.value).strip() if symbol_cell.value else "" 137 symbol_cell.font = Font(color="FFFFFF", name=symbol_cell.font.name, size=symbol_cell.font.size) 138 139 folder = symbol_paths.get(symbol, symbol_paths["Other"]) 140 os.makedirs(folder, exist_ok=True) 141 output_path = os.path.join(folder, filename) 142 143 new_wb.save(output_path) 144 145 os.remove(resized_img_path) 146 147# ---------------------------- 148# PDF + ZIP変換処理(ファイルごとにExcel起動) 149# ---------------------------- 150def convert_to_pdf_and_zip(folder_path): 151 for file in os.listdir(folder_path): 152 if file.endswith(".xlsx") and not file.startswith("~$"): 153 xlsx_path = os.path.join(folder_path, file) 154 pdf_path = xlsx_path.replace(".xlsx", ".pdf") 155 zip_path = xlsx_path.replace(".xlsx", ".zip") 156 157 try: 158 print(f"[処理中] {file}") 159 160 # Excel起動(1ファイルずつ) 161 excel = win32.gencache.EnsureDispatch("Excel.Application") 162 excel.Visible = False 163 164 wb = excel.Workbooks.Open(xlsx_path) 165 if wb is None: 166 print(f"[スキップ] Excelで開けませんでした: {file}") 167 excel.Quit() 168 continue 169 170 try: 171 ws = wb.Sheets("試験成績表") 172 print("→ シート取得 OK") 173 174 # 一時保存(ファイル構造が壊れてないか確認のため) 175 temp_save = os.path.join(folder_path, "temp_save.xlsx") 176 wb.SaveAs(temp_save) 177 print("→ 一時保存完了") 178 179 # 印刷設定 180 ws.PageSetup.Zoom = False 181 ws.PageSetup.FitToPagesWide = 1 182 ws.PageSetup.FitToPagesTall = 1 183 ws.PageSetup.PrintArea = "A1:Y63" 184 ws.PageSetup.CenterHorizontally = True 185 ws.PageSetup.LeftMargin = 0.2 186 ws.PageSetup.RightMargin = 0.2 187 ws.PageSetup.TopMargin = 0.2 188 ws.PageSetup.BottomMargin = 0.2 189 ws.PageSetup.HeaderMargin = 0.1 190 ws.PageSetup.FooterMargin = 0.1 191 192 # 既存PDF削除 193 if os.path.exists(pdf_path): 194 os.remove(pdf_path) 195 196 wb.ExportAsFixedFormat(0, pdf_path) 197 print(f"[成功] PDF出力完了: {pdf_path}") 198 199 # ZIP圧縮 200 with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf: 201 zipf.write(pdf_path, os.path.basename(pdf_path)) 202 print(f"[成功] ZIP保存完了: {zip_path}") 203 204 except Exception as inner_e: 205 print(f"[エラー] PDF出力処理中: {file}: {inner_e}") 206 finally: 207 wb.Close(False) 208 excel.Quit() 209 210 except Exception as outer_e: 211 print(f"[エラー] PDFまたはZIPに失敗: {file}: {outer_e}") 212

投稿2025/03/30 23:30

shield_599

総合スコア1

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.32%

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

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

質問する

関連した質問