MENU

問い合わせ


    【CAD図面PDFをExcelへ自動転記するツール開発 第0回】要件整理とPython開発環境、tkinterの最初の画面を作る

    建築・不動産・設計事務所の担当者から「CAD図面PDFに書かれた部屋名を、決まったExcelの様式へ一つひとつ手で転記している」という相談を受けることがあります。図面を目で確認しながら、寝室・洋室・トイレ・浴室といった部屋名をExcelの決まったセルへ入力していく作業は、単純なようでいて時間がかかり、見落としや入力ミスも起きやすい定型業務です。

    この連載では、この転記作業を自動化するWindows向けデスクトップツールを、Pythonだけで一から組み上げていきます。図面PDFに埋め込まれた文字を読み取り、読み取れない場合はローカルOCR、精度を上げたい場合は外部の生成AIも使い分けながら、最終的に指定のExcel様式へ書き込むところまでを、1回1機能の小さな単位で実装します。

    第0回の今回は、コードはまだ「部屋名を読み取る」ところまで進みません。要件の整理、Python環境の構築、tkinterによる最初のウィンドウ、そしてPDF・Excelファイルを選ぶダイアログの土台を作ります。地味な回に見えますが、この連載で一番大事な判断がここに詰まっています。

    目次

    この連載で最終的にできること

    先に、連載全体のゴールを共有しておきます。

    • CAD図面PDF(1〜複数ページ)を選択し、画面でプレビューできる
    • 図面に埋め込まれた文字(CADソフト由来のPDFはテキストとして抽出できることが多い)から部屋名候補を検出する
    • 文字が埋め込まれていない(スキャンされた)図面には、ローカルOCR(Tesseract)で対応する
    • 精度を上げたい場合は、外部の生成AI(画像を理解できるマルチモーダルAI)に図面画像を渡し、部屋名だけを抽出できる
    • 検出結果は必ず画面上で人が確認・修正してからExcelへ書き込む
    • 部屋名とExcelのセルの対応は、コードを直さずに設定ファイルで変更できる
    • フォルダ単位で複数のPDF・複数物件をまとめて処理できる
    • Pythonを入れていない担当者にも配布できるよう、単一のWindows実行ファイル(.exe)にまとめる

    毎回「動くコード」と「動作確認方法」を必ず載せ、前回までのコードに機能を積み上げていく方針です。なお開発・検証はLinux環境で行うため、画面の見た目や操作感、exeの起動そのものはWindows実機での確認が必要になります。どこまでをこのリポジトリの環境(Linux)で確認できて、どこからがWindows実機での確認が必要かを、各回の「動作確認の方法」で区別して書きます。

    第0回の仕組み:なぜtkinterで、なぜGUIとコア処理を分けるのか

    GUIライブラリにtkinterを選ぶ理由

    Pythonには他にもGUIライブラリがありますが、今回は追加インストールが不要な標準ライブラリのtkinterを使います。理由は配布のしやすさです。最終的にはPyInstallerで単一の.exeにまとめて配布する予定のため、GUIライブラリ自体が追加のインストール作業を発注者側に求めない、という条件を優先しました。

    GUI(画面)とコア処理を分ける設計

    もう一つの重要な判断が、画面を扱うコード(app/ui/)と、画面に依存しないロジック(app/core/)を分けることです。

    区分役割tkinterへの依存
    app/ui/画面の表示、ボタン・ダイアログの配置あり
    app/core/ファイルパスの検証、後の回で追加するPDF解析・OCR・Excel書き込みなどなし

    app/core/ をtkinterに依存させない理由は単純で、Linux上のこの開発環境ではtkinterの画面を目視確認できないためです。画面に依存しないロジックだけでも自動テスト(pytest)で検証できるようにしておけば、「動くはずのコードが実は壊れていた」という事態を減らせます。この分離は連載を通して守るルールにします。

    第0回で実装する範囲

    今回作るのは、次の3点だけです。

    1. tkinterのウィンドウを開くエントリーポイント
    2. PDF・Excelテンプレートを選ぶファイル選択ダイアログと、選んだパスを保持するメイン画面
    3. 選んだファイルが「PDFらしいか」「Excelらしいか」を検証するコア処理(拡張子・存在確認のみ)

    図面の中身を読む処理(画像化・文字抽出・OCR)は次回以降です。今回のファイル検証は「拡張子が.pdfか」「ファイルが実在するか」程度にとどめ、PDFとして壊れていないかまでは踏み込みません(それは第1回、PyMuPDFで実際に開く処理と合わせて確認します)。

    実装

    プロジェクトの構成(第0回時点)

    code/
    ├ app/
    │  ├ main.py             … エントリーポイント
    │  ├ core/
    │  │  └ file_selection.py … 選択ファイルの検証(tkinter非依存)
    │  └ ui/
    │     └ main_window.py    … メイン画面
    ├ tests/
    │  └ test_file_selection.py
    ├ requirements.txt
    ├ pytest.ini
    └ .gitignore

    コア処理:選択ファイルの検証(app/core/file_selection.py)

    拡張子と存在確認だけを行う、tkinterに依存しない小さなモジュールです。SelectedFiles は「PDFパスとExcelパスの両方が揃ったか」を判定するためだけの入れ物にしています。

    # app/core/file_selection.py
    from __future__ import annotations
    
    from dataclasses import dataclass
    from pathlib import Path
    
    PDF_SUFFIXES = frozenset({".pdf"})
    EXCEL_SUFFIXES = frozenset({".xlsx", ".xlsm"})
    
    
    class FileSelectionError(ValueError):
        """選択されたファイルが要件を満たさない場合に送出する例外。"""
    
    
    @dataclass(frozen=True)
    class SelectedFiles:
        pdf_path: Path | None = None
        excel_path: Path | None = None
    
        def is_ready(self) -> bool:
            return self.pdf_path is not None and self.excel_path is not None
    
        def with_pdf(self, pdf_path: Path) -> SelectedFiles:
            return SelectedFiles(pdf_path=pdf_path, excel_path=self.excel_path)
    
        def with_excel(self, excel_path: Path) -> SelectedFiles:
            return SelectedFiles(pdf_path=self.pdf_path, excel_path=excel_path)
    
    
    def validate_pdf_path(path: str | Path) -> Path:
        resolved = Path(path)
        if not resolved.exists():
            raise FileSelectionError(f"ファイルが見つかりません: {resolved}")
        if not resolved.is_file():
            raise FileSelectionError(f"フォルダではなくファイルを選択してください: {resolved}")
        if resolved.suffix.lower() not in PDF_SUFFIXES:
            raise FileSelectionError(f"PDFファイル(拡張子 .pdf)を選択してください: {resolved.name}")
        return resolved

    validate_excel_path も同様の作りで、.xlsx / .xlsm を受け付けます(全文は app/core/file_selection.py を参照)。

    メイン画面(app/ui/main_window.py)

    「PDFを選ぶ」「Excelを選ぶ」ボタンと、両方選ばれるまで押せない実行ボタンを配置しています。実行ボタンを押したときの処理(文字抽出・OCR・Excel書き込み)は、まだ「次回以降で実装します」というメッセージを出すだけの空の実装です。

    # app/ui/main_window.py(抜粋)
    from __future__ import annotations
    
    import tkinter as tk
    from tkinter import filedialog, messagebox, ttk
    
    from app.core.file_selection import FileSelectionError, SelectedFiles, validate_excel_path, validate_pdf_path
    
    
    class MainWindow(ttk.Frame):
        def __init__(self, master: tk.Tk) -> None:
            super().__init__(master, padding=16)
            self.selected = SelectedFiles()
            # ...ウィジェット配置は省略(全文は app/ui/main_window.py)
    
        def _on_choose_pdf(self) -> None:
            path = filedialog.askopenfilename(title="CAD図面PDFを選択", filetypes=[("PDFファイル", "*.pdf")])
            if not path:
                return
            try:
                validated = validate_pdf_path(path)
            except FileSelectionError as exc:
                messagebox.showerror("ファイル選択エラー", str(exc))
                return
            self.selected = self.selected.with_pdf(validated)
            self._refresh_run_button()
    
        def _refresh_run_button(self) -> None:
            self.run_button.configure(state="normal" if self.selected.is_ready() else "disabled")

    ファイルダイアログ自体がfiletypesで拡張子を絞り込みますが、ドラッグ&ドロップや手入力で別拡張子を渡された場合に備え、app/core/file_selection.py の検証を必ず通してから状態を更新するようにしています。

    エントリーポイント(app/main.py)

    # app/main.py
    from __future__ import annotations
    
    import tkinter as tk
    
    from app.ui.main_window import build_main_window
    
    
    def main() -> None:
        root = tk.Tk()
        build_main_window(root)
        root.mainloop()
    
    
    if __name__ == "__main__":
        main()

    動作確認の方法

    Linux開発環境で確認できたことと、Windows実機での確認が必要なことを分けて書きます。

    Linux開発環境で確認できたこと

    • pytest:app/core/file_selection.py に対するテスト10件がすべて成功(存在しないファイル・拡張子違い・フォルダを指定した場合にエラーになること、PDFとExcelの両方が揃って初めてis_ready()がTrueになることを検証)
    • ruff check .:エラーなし
    • python -c "import app.main":importエラーなし(tkinterが利用できるPython環境で確認)
    • xvfb-run python app/main.py 相当の起動確認:仮想ディスプレイ(Xvfb)上でウィンドウを生成し、タイトルバーの文字列と、実行ボタンが初期状態で無効(disabled)になっていることをコードから確認。例外を出さずに終了できることも確認

    実際に起動した画面は次のとおりです。

    起動直後のメイン画面。CAD図面PDF・Excelテンプレートともに「未選択」と表示され、3. 文字検出ボタンと4. 実行ボタンはグレーアウトして押せない

    ※これは開発環境(Linux/Xvfb上・Ubuntu標準のTkテーマ)で起動して確認した画面です。Windows実機ではウィンドウの装飾や既定フォントなど見た目が異なります。

    Windows実機での確認が必要なこと(このリポジトリでは未確認)

    • 実際の画面レイアウト・フォント・ボタンの見た目
    • ファイル選択ダイアログの操作感(Windowsのエクスプローラーとの見た目の違いなど)
    • 高DPIディスプレイでの表示スケーリング

    Xvfbでの起動確認は「例外を出さずにウィンドウを生成できる」ことの確認であり、「Windows上で見た目どおりに動く」ことの確認ではありません。画面の実物確認は、開発会社にとって当たり前の工程ですが、Linux上で開発を進める場合はこの区別を意識しておく必要があります。

    つまずきやすい点・セキュリティ上の注意

    • 開発環境のPythonバージョン:連載の技術スタックはPython 3.13系を前提にしていますが、このリポジトリの検証環境ではPython 3.11/3.12までしか用意できませんでした。文法・標準ライブラリの使い方に3.13固有の差異は出ていませんが、Windows実機でセットアップする際は3.13系を使ってください。
    • tkinterはOSの表示環境に依存する:Linuxサーバー環境ではデフォルトでtkinterが入っていないことがあります(今回の検証環境もそうでした)。Windows配布用のツールなので通常は問題になりませんが、開発を複数人・複数OSで行う場合はこの点を共有しておくと手戻りが減ります。
    • 次回以降で使うPyMuPDFのライセンス:図面PDFを開く処理(第1回)ではPyMuPDFを使う予定ですが、これはAGPL-3.0ライセンス(商用ライセンスも別途あり)のライブラリです。自社の内製ツールとして社内だけで使う分には問題になりにくいのですが、ツールを他社へ配布・販売する場合はライセンス条件の確認が必要になります。詳しくは第1回で扱います。
    • 図面・Excelファイルの取り扱い:図面やExcelの様式には、顧客の建物情報が含まれることがあります。この連載のサンプルコード・記事では実在の顧客情報を使わず、架空の図面・様式を前提に進めます。実際の開発では、サンプルデータにも社内の取り扱いルールを適用してください。

    発注者向けメモ

    この回で確認しておきたいこと・工数の勘所

    「決まったフォーマットへの転記」は要件が明確に見えやすい作業です。ところが実際に進めてみると、部屋名の表記ゆれ(洋室1/寝室1/ベッドルーム等)や、Excelの様式が取引先ごとに微妙に違うといった「例外」が後から効いてきて、当初の見積もりより工数がかかりがちです。開発を始める前に、対象となる図面のパターン(CADソフト由来で文字が埋め込まれているか、古いスキャン図面が混ざっているか)をできるだけ集めておくと、あとの手戻りを減らせます。

    • ☐ 対象図面のサンプルを、パターン別(CAD由来・スキャンのみ・両方混在)に最低数点ずつ集めたか
    • ☐ 転記先のExcel様式は何パターンあるか(取引先ごとに違うか)を洗い出したか
    • ☐ 部屋名の呼び方のゆれ(現場での言い方の違い)をリストアップしたか
    • ☐ 図面を外部のAIサービスに送信してよいか、社内の情報管理ルール・契約上の秘密保持義務を確認したか
    • ☐ 配布先の担当者のPCにPythonが入っているか、入っていない前提でexe配布が必要かを確認したか

    開発会社への質問例

    • 「御社では、図面PDFが『文字が埋め込まれたPDF』か『画像として取り込まれたスキャンPDF』かを、どうやって判定しますか?」
    • 「ローカルのOCRと外部の生成AI、どちらを基本の方式にする想定ですか。その理由も教えてください」
    • 「Excelの様式が途中で追加・変更になった場合、どこまでが設定ファイルの変更で済み、どこからがコードの改修になりますか?」
    • 「AI・OCRが読み取った部屋名を、人が確認・修正できる画面はどの程度作り込む想定ですか?」

    これらの質問は、見積もりの前提(対象図面のパターン数、AIを使うかどうか、人の確認をどこまで挟むか)をすり合わせるためのものです。前提が揃っていないと、開発途中で「そんな図面もあったのか」という手戻りにつながりやすくなります。

    まとめと次回予告

    第0回では、連載全体のゴールを確認したうえで、Python環境の構築、tkinterによる最初のウィンドウ、PDF・Excelファイルの選択ダイアログの土台を作りました。実際にファイルの中身を読む処理はまだ入っていませんが、画面(GUI)とロジック(コア処理)を分けるという、この連載を通して守る設計方針はここで固めています。

    次回(第1回)は、選んだPDFの図面ページをPyMuPDFで画像化し、tkinterのCanvasに表示してページ送り・拡大縮小ができるようにします。あわせて、PyMuPDFのライセンス(AGPL-3.0)についても具体的に扱います。

    この連載の記事一覧

    この記事は連載「CAD図面PDFをExcelへ自動転記するツール開発」の1回です。連載のほかの回は次のとおりです(連載の一覧ページ)。

    システム制作・運用・保守のお問い合わせはこちら


      よかったらシェアしてね!
      • URLをコピーしました!
      • URLをコピーしました!

      この記事を書いた人

      株式会社THIRD HERO代表取締役 朝野貴朗
      Webシステム開発を中心に、toC向けサービスサイトの運営、ツール開発などを行ってまいりました。

      コメント

      コメント一覧 (1件)

      目次