【Tkinter入門 最終回】実践!Pythonでデスクトップアプリ開発 - 材料重量計算ツールをGUI化しよう!

「CUI(黒い画面)のスクリプトって、自分は良くても他の人には使ってもらいにくい…」

「Tkinterで学んだウィジェットやレイアウト、どうやって実践的なアプリに組み込むの?」
「自分の作ったPythonプログラムを、ウィンドウやボタンのある『アプリ』にしてみたい!」

こんにちは! Tkinter探検隊、隊長のPythonistaです! 第1回ではウィンドウと基本的なウィジェットの作成、第2回ではユーザーからの入力を受け付ける方法を学びました。GUIアプリの基本的な部品と、それをコードと連携させる仕組みは、もうあなたの手の中にあります。

シリーズ最終回となる今回は、まさにその集大成!これまでの全ての知識を総動員して、以前別のプロジェクトで作成したコマンドライン版「材料重量計算スクリプト」に、Tkinterで見栄えの良いグラフィカルなインターフェース(GUI)を付け、誰でも直感的に使えるデスクトップアプリケーションへと進化させます。

このプロジェクトを通して、既存のプログラムロジックとGUIをどうやって組み合わせるのか、その実践的なプロセスを体験できます。さあ、あなたのコードに命を吹き込み、本格的なアプリケーション開発の第一歩を踏み出しましょう!


1. 設計図を描こう:どんなアプリにする?

まずは、どんなアプリケーションにしたいか、その設計図を描きます。いきなりコードを書き始めるのではなく、完成形をイメージするのが成功の秘訣です。

1.1. 機能とレイアウト

元のCUIスクリプトの機能を元に、以下のようなレイアウトを考えます。

  • 材料選択: アルミ、鉄、ステンレスをリストから選べるようにしたい。→ **プルダウンメニュー (Combobox)** が良さそう。
  • 体積入力: ユーザーが体積の数値と単位を入力できるようにしたい。→ **テキスト入力ボックス (Entry)** が必要。
  • 計算実行: 計算を開始するきっかけ。→ **ボタン (Button)** が必要。
  • 結果表示: 計算結果を分かりやすく表示したい。→ **ラベル (Label)** を使おう。
  • その他、各入力欄が何を表すかを示すための案内用ラベルも必要ですね。

【画像推奨箇所: アプリの完成形の手書きスケッチや、簡単な図。「材料」の横にプルダウン、「体積」の横に入力欄、下に「計算」ボタン、さらに下に結果表示エリア、といったレイアウト図】

今回は、これらの部品を上下に並べるシンプルなレイアウトを目指します。

1.2. プログラムの「頭脳」を再利用

今回のプロジェクトの素晴らしい点は、計算ロジック自体は既に完成していることです。以前作成したスクリプトの、計算部分の関数をそのまま「頭脳」として再利用します。GUIは、その「頭脳」に対する「顔」や「手足」を作る作業と考えることができます。

(今回は1つのファイルにまとめますが、本来は計算ロジック部分を別ファイル(例: `calculator_engine.py`)に、GUI部分を別ファイルに分けると、より綺麗な設計になります。)


2. ステップ1:計算ロジックとGUIの骨格を準備

まずは、必要なモジュールをインポートし、以前作成した計算ロジックの関数群と、Tkinterウィンドウの基本的な骨格を準備します。

import tkinter as tk
from tkinter import ttk
import re

# --- ▼▼▼ 以前作成した計算ロジック部分 (頭脳) ▼▼▼ ---
DENSITIES = {'アルミ': 2.70, '鉄': 7.87, 'ステンレス': 7.93}
UNIT_TO_CM3_FACTORS = {
    'mm^3': 0.001, 'mm3': 0.001, '㎣': 0.001, '立方ミリメートル': 0.001,
    'cm^3': 1, 'cm3': 1, '㎤': 1, '立方センチメートル': 1,
    'm^3': 1_000_000, 'm3': 1_000_000, '㎥': 1_000_000, '立方メートル': 1_000_000,
}
def parse_volume_input(input_str):
    clean_str = input_str.strip().lower().replace(',', '')
    pattern = f"([0-9\.]+)\\s*({'|'.join(UNIT_TO_CM3_FACTORS.keys())})"
    match = re.search(pattern, clean_str, re.IGNORECASE)
    if match:
        value_str, unit_str = match.groups()
        value = float(value_str)
        return value * UNIT_TO_CM3_FACTORS[unit_str.lower()]
    else:
        # 数値のみの場合は、cm^3とみなす (GUIではシンプルにする)
        return float(clean_str)
def format_weight(weight_g):
    try:
        weight_g = float(weight_g)
        if weight_g >= 1_000_000: return f"{weight_g / 1_000_000:.3f} t"
        elif weight_g >= 1000: return f"{weight_g / 1000:.3f} kg"
        else: return f"{weight_g:.3f} g"
    except (ValueError, TypeError): return "計算エラー"
# --- ▲▲▲ 計算ロジック部分はここまで ▲▲▲ ---


# --- ▼▼▼ ここからGUIの骨格を作成 ▼▼▼ ---
class WeightCalculatorApp(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("材料重量計算アプリ")
        self.geometry("400x250")
        
        # ここにウィジェットを作成し、配置していく
        pass # 後で実装

# アプリケーションの実行
if __name__ == "__main__":
    app = WeightCalculatorApp()
    app.mainloop()

今回は、アプリケーション全体をクラスとしてまとめる、よりオブジェクト指向的な書き方に挑戦してみます。WeightCalculatorAppクラスが私たちのアプリの本体です。


3. ステップ2:ウィジェットの作成とレイアウト

次に、設計図に従って必要なウィジェットを作成し、.grid()を使ってウィンドウ上に綺麗に配置していきます。これらのコードは、WeightCalculatorAppクラスの__init__メソッドの中に書いていきます。

class WeightCalculatorApp(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("材料重量計算アプリ")
        self.geometry("400x250")
        self.create_widgets()

    def create_widgets(self):
        # スタイル設定
        style = ttk.Style(self)
        style.configure('TLabel', font=('Helvetica', 12))
        style.configure('TButton', font=('Helvetica', 12))
        style.configure('TCombobox', font=('Helvetica', 12))

        # --- ウィジェットの作成 ---
        # 材料選択
        self.material_label = ttk.Label(self, text="1. 材料を選択:")
        self.material_var = tk.StringVar()
        self.material_combo = ttk.Combobox(self, textvariable=self.material_var, values=list(DENSITIES.keys()), state='readonly')
        self.material_combo.current(0) # 初期値を設定

        # 体積入力
        self.volume_label = ttk.Label(self, text="2. 体積を入力 (例: 1500mm3):")
        self.volume_var = tk.StringVar()
        self.volume_entry = ttk.Entry(self, textvariable=self.volume_var, width=30)

        # 計算ボタン
        self.calc_button = ttk.Button(self, text="重量を計算する", command=self.handle_calculation) # commandは後で定義

        # 結果表示ラベル
        self.result_var = tk.StringVar()
        self.result_var.set("ここに計算結果が表示されます")
        self.result_label = ttk.Label(self, textvariable=self.result_var, font=('Helvetica', 14, 'bold'), foreground='#00008B')

        # --- .grid() でのレイアウト ---
        self.material_label.grid(row=0, column=0, padx=10, pady=10, sticky='W')
        self.material_combo.grid(row=0, column=1, padx=10, pady=10, sticky='EW')
        
        self.volume_label.grid(row=1, column=0, padx=10, pady=10, sticky='W')
        self.volume_entry.grid(row=1, column=1, padx=10, pady=10, sticky='EW')

        self.calc_button.grid(row=2, column=0, columnspan=2, padx=10, pady=15)
        
        self.result_label.grid(row=3, column=0, columnspan=2, padx=10, pady=10)

    def handle_calculation(self):
        # この関数の中身を次に実装する
        self.result_var.set("ボタンがクリックされました!")

# (アプリケーション実行部分は同じ)

この時点では、まだボタンを押しても計算は実行されません。しかし、アプリの「見た目」はほぼ完成しました!ttkウィジェットを使うことで、OS標準のモダンな見た目になっています。


4. ステップ3:GUIと計算ロジックを連携させる

最後に、ボタンがクリックされたときに実行されるhandle_calculationメソッドの中身を実装し、GUIの入力と計算ロジックを繋ぎこみます。

# WeightCalculatorApp クラスの中に追加

    def handle_calculation(self):
        """計算ボタンが押されたときの処理"""
        # 1. 制御変数からユーザーの入力値を取得
        material = self.material_var.get()
        volume_input_str = self.volume_var.get()

        # 入力が空かチェック
        if not volume_input_str:
            self.result_var.set("エラー: 体積を入力してください。")
            return

        try:
            # 2. 以前作成した関数を使って、入力文字列を解析し、体積(cm^3)を計算
            volume_cm3 = parse_volume_input(volume_input_str)
            
            # 3. 密度を取得して重量を計算
            density = DENSITIES[material]
            weight_g = volume_cm3 * density
            
            # 4. 表示用に重量をフォーマット
            formatted_weight = format_weight(weight_g)
            
            # 5. 結果表示用の制御変数の値を更新 -> ラベルの表示が自動で変わる!
            self.result_var.set(f"計算結果: {formatted_weight}")

        except Exception as e:
            # 入力解析エラーや計算エラーをキャッチ
            self.result_var.set(f"エラー: 有効な入力ではありません。")
            print(f"詳細エラー: {e}") # デバッグ用にコンソールにも出力

このhandle_calculationメソッドをWeightCalculatorAppクラスの中に追加すれば、アプリケーションの完成です!


・コード全文はこちらをご確認ください!

まとめ:コマンドラインツールから、誰でも使えるアプリへ!

お疲れ様でした!今回の実践プロジェクトを通して、私たちは以下の重要なステップを経験しました。

  • アプリケーションの**設計**:必要な機能を考え、どのようなウィジェットで実現するかを計画しました。
  • **ウィジェットの作成と配置**: Label, Entry, Combobox, Buttonといったウィジェットを、gridマネージャを使って整然と配置しました。
  • **制御変数による連携**: StringVarなどの制御変数を使い、GUI上の入力とPythonのコードをスムーズに連携させました。
  • ロジックの再利用と統合**: 以前作成した計算ロジック(頭脳)をそのまま再利用し、ボタンのcommand(手足)と結びつけることで、GUIアプリケーションを完成させました。

コマンドラインで動いていたスクリプトに、グラフィカルな「顔」を与えることで、プログラミングを知らない人でも直感的に使える、より価値の高いツールへと昇華させることができました。これは、ソフトウェア開発における非常に重要なプロセスです。

これにて、Tkinter入門シリーズは完結です!皆さんの手元には、デスクトップアプリを自由に作成するための基本的なスキルセットが揃いました。ぜひ、これまでのシリーズで作った他のツール(ToDoリストなど)のGUI化にも挑戦してみてください。あなたのアイデアを「アプリ」という形にする楽しさを、これからも探求していってくださいね!

Happy GUI Hacking! 👨‍💻👩‍💻

その他の投稿はこちら

コメント

このブログの人気の投稿

タイトルまとめ

これで迷わない!WindowsでPython環境構築する一番やさしい方法 #0

【Python標準ライブラリ完結!】11の冒険をありがとう!君のPython力が飛躍する「次の一歩」とは? 🚀