20
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TkinterとCustomTkinterで電卓つくって比較してみた

Posted at

はじめに

image.png

PythonでGUIアプリを作りたいとき、多くの開発者が最初に出会うのがTkinterです。しかし、デフォルトのTkinterは見た目が古く感じることも。そこで注目されているのがCustomTkinterです。

この記事では、標準のTkinterとモダンなCustomTkinterを比較しながら、実際にGUIアプリを作成する方法を解説します。

Tkinterとは

TkinterはPythonの標準ライブラリに含まれるGUIツールキットです。

メリット:

  • 標準ライブラリなので追加インストール不要
  • 軽量で高速
  • 豊富な情報とサンプルコード

デメリット:

  • デザインが古く見える
  • モダンなUIを作るには工夫が必要

CustomTkinterとは

CustomTkinterは、TkinterをベースにしたモダンなデザインのGUIライブラリです。

メリット:

  • モダンで美しいデザイン
  • ダークモード対応
  • Tkinterの知識がそのまま活用可能

デメリット:

  • 追加インストールが必要
  • 比較的新しいライブラリ

インストール

CustomTkinterを使用する場合は、pipでインストールします:

pip install customtkinter

電卓アプリを作ってみよう

同じ機能の電卓アプリを、TkinterとCustomTkinterの両方で作成して比較してみます。

標準Tkinter版

import tkinter as tk
from tkinter import ttk

class Calculator:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("電卓 - Tkinter")
        self.root.geometry("300x400")
        
        # 計算式を保存する変数
        self.equation = ""
        
        # 結果表示用のEntry
        self.result_var = tk.StringVar()
        self.result_var.set("0")
        
        self.create_widgets()
    
    def create_widgets(self):
        # 結果表示エリア
        result_frame = tk.Frame(self.root)
        result_frame.pack(pady=10, padx=10, fill='x')
        
        result_entry = tk.Entry(
            result_frame, 
            textvariable=self.result_var,
            font=('Arial', 16),
            justify='right',
            state='readonly'
        )
        result_entry.pack(fill='x')
        
        # ボタンエリア
        button_frame = tk.Frame(self.root)
        button_frame.pack(pady=10, padx=10, fill='both', expand=True)
        
        # ボタンの配置
        buttons = [
            ['C', '±', '%', '÷'],
            ['7', '8', '9', '×'],
            ['4', '5', '6', '-'],
            ['1', '2', '3', '+'],
            ['0', '.', '', '=']
        ]
        
        for i, row in enumerate(buttons):
            for j, text in enumerate(row):
                if text == '=':
                    btn = tk.Button(
                        button_frame, text=text,
                        font=('Arial', 14),
                        bg='orange',
                        command=self.calculate
                    )
                    btn.grid(row=i, column=3, sticky='nsew', padx=2, pady=2)
                elif text == '':
                    # 空の要素はスキップ
                    continue
                else:
                    btn = tk.Button(
                        button_frame, text=text,
                        font=('Arial', 14),
                        command=lambda t=text: self.button_click(t)
                    )
                    btn.grid(row=i, column=j, sticky='nsew', padx=2, pady=2)
        
        # グリッドの重みを設定
        for i in range(5):
            button_frame.rowconfigure(i, weight=1)
        for j in range(4):
            button_frame.columnconfigure(j, weight=1)
    
    def button_click(self, char):
        if char == 'C':
            self.equation = ""
            self.result_var.set("0")
        elif char == '±':
            if self.equation and self.equation[-1].isdigit():
                self.equation = str(-float(self.equation))
                self.result_var.set(self.equation)
        else:
            if char in '×÷':
                char = '*' if char == '×' else '/'
            self.equation += str(char)
            self.result_var.set(self.equation)
    
    def calculate(self):
        try:
            result = eval(self.equation)
            self.result_var.set(str(result))
            self.equation = str(result)
        except:
            self.result_var.set("Error")
            self.equation = ""
    
    def run(self):
        self.root.mainloop()

if __name__ == "__main__":
    calc = Calculator()
    calc.run()

image.png

CustomTkinter版

import customtkinter as ctk

class ModernCalculator:
    def __init__(self):
        # テーマ設定
        ctk.set_appearance_mode("dark")
        ctk.set_default_color_theme("blue")
        
        self.root = ctk.CTk()
        self.root.title("電卓 - CustomTkinter")
        self.root.geometry("300x400")
        
        self.equation = ""
        
        self.create_widgets()
    
    def create_widgets(self):
        # 結果表示エリア
        self.result_var = ctk.StringVar(value="0")
        result_entry = ctk.CTkEntry(
            self.root,
            textvariable=self.result_var,
            font=ctk.CTkFont(size=20, weight="bold"),
            justify='right',
            state='readonly',
            height=50
        )
        result_entry.pack(pady=20, padx=20, fill='x')
        
        # ボタンエリア
        button_frame = ctk.CTkFrame(self.root)
        button_frame.pack(pady=10, padx=20, fill='both', expand=True)
        
        # ボタンの配置
        buttons = [
            ['C', '±', '%', '÷'],
            ['7', '8', '9', '×'],
            ['4', '5', '6', '-'],
            ['1', '2', '3', '+'],
            ['0', '.', '', '=']
        ]
        
        for i, row in enumerate(buttons):
            for j, text in enumerate(row):
                if text == '=':
                    btn = ctk.CTkButton(
                        button_frame, text=text,
                        font=ctk.CTkFont(size=16, weight="bold"),
                        command=self.calculate,
                        fg_color="orange",
                        hover_color="darkorange",
                        width=50, height=50
                    )
                    btn.grid(row=i, column=3, sticky='ew', padx=5, pady=5)
                elif text == '':
                    # 空の要素はスキップ
                    continue
                elif text in ['÷', '×', '-', '+']:
                    btn = ctk.CTkButton(
                        button_frame, text=text,
                        font=ctk.CTkFont(size=16, weight="bold"),
                        command=lambda t=text: self.button_click(t),
                        fg_color="gray30",
                        hover_color="gray40",
                        width=50, height=50
                    )
                    btn.grid(row=i, column=j, sticky='ew', padx=5, pady=5)
                else:
                    btn = ctk.CTkButton(
                        button_frame, text=text,
                        font=ctk.CTkFont(size=16, weight="bold"),
                        command=lambda t=text: self.button_click(t),
                        width=50, height=50
                    )
                    btn.grid(row=i, column=j, sticky='ew', padx=5, pady=5)
        
        # グリッドの重みを設定
        for i in range(5):
            button_frame.rowconfigure(i, weight=1)
        for j in range(4):
            button_frame.columnconfigure(j, weight=1)
    
    def button_click(self, char):
        if char == 'C':
            self.equation = ""
            self.result_var.set("0")
        elif char == '±':
            if self.equation and self.equation[-1].isdigit():
                self.equation = str(-float(self.equation))
                self.result_var.set(self.equation)
        else:
            if char in '×÷':
                char = '*' if char == '×' else '/'
            self.equation += str(char)
            self.result_var.set(self.equation)
    
    def calculate(self):
        try:
            result = eval(self.equation)
            self.result_var.set(str(result))
            self.equation = str(result)
        except:
            self.result_var.set("Error")
            self.equation = ""
    
    def run(self):
        self.root.mainloop()

if __name__ == "__main__":
    calc = ModernCalculator()
    calc.run()

image.png

どちらを選ぶべきか

Tkinterは軽量で追加ライブラリ不要なため、シンプルで軽いアプリに向きます。
CustomTkinterはモダンなデザインや標準のダークモード対応が魅力で、ユーザー体験を重視するアプリに適しています。

サンプルコードの注意点

今回のコードはデモの実装として紹介していますが、以下の課題があります:

コードの±機能: 記事中の±ボタンの実装は不完全
eval()の使用: セキュリティ上の問題

ちゃんとした実装にするには、適切な数式パーサーの使用やより堅牢なエラーハンドリングが必要です。

まとめ

TkinterとCustomTkinterは、それぞれ異なる特徴を持つGUIライブラリです。プロジェクトの要件に応じて適切に選択することで、効率的にGUIアプリケーションを開発できます。

モダンなデザインを求める場合はCustomTkinter、軽量性を重視する場合はTkinterを選択するのが良いでしょう。どちらもPythonでGUIアプリを作成する優秀な選択肢です。

参考資料


この記事がPython GUIアプリ開発の参考になれば幸いです。ぜひ実際にコードを試してみてください!

20
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?