PythonとSQLiteで作る!子供のお手伝いポイントカードシステムの作成ガイド

Python

子供にお手伝いをしてもらう動機付けに、ポイントカードシステムを導入してみませんか?この記事では、PythonのSQLite3tkinterを使って、子供がお手伝いをしてポイントを貯め、それを商品と交換できるシステムの作成方法を紹介します。

このコードは、Python(バージョン3.x)と標準ライブラリだけで動作します。WindowsやMacにPythonがインストールされていれば、すぐに試せます。おすすめのエディタは「VS Code」です。

完成イメージ

子供が使う画面では、自分のポイントを確認したり、貯まったポイントで商品と交換します。

親御さんが使う画面では、子供をシステムに登録したり、ポイントを付与したり、交換可能な商品を登録します。

    システム概要

    このシステムは3つの主要なテーブル(ユーザーポイント商品)を持つデータベースを作成します。それぞれのテーブルで、子供の情報、ポイントの履歴、そして商品情報を管理します。

    • users:子供の名前と生年月日で識別し、ユーザー情報を管理。
    • points:どのお手伝いでポイントが付与されたか、その理由とともに記録。
    • products:商品名と交換に必要なポイントを管理。

    1. 必要なライブラリ

    import sqlite3
    import tkinter as tk
    from tkinter import messagebox
    
    • sqlite3: データベースを作成・管理するためのライブラリです。
    • tkinter: GUI(グラフィカル・ユーザー・インターフェース)を作成するためのライブラリです。
    • messagebox: tkinterの中のツールで、メッセージを表示するために使います。

    2. データベースの初期設定

    まず、ユーザー、ポイント、商品を管理するデータベースを設定します。このコードは、必要なテーブルを作成する初期設定です。

    def setup_database():
        conn = sqlite3.connect('points.db')
        c = conn.cursor()
        
        # ユーザーテーブルの作成
        c.execute('''
            CREATE TABLE IF NOT EXISTS users (
                id INTEGER PRIMARY KEY,
                name TEXT,
                birthdate TEXT
            )
        ''')
    
        # ポイントテーブルの作成
        c.execute('''
            CREATE TABLE IF NOT EXISTS points (
                user_id INTEGER,
                points INTEGER,
                reason TEXT,
                FOREIGN KEY(user_id) REFERENCES users(id)
            )
        ''')
    
        # 商品テーブルの作成
        c.execute('''
            CREATE TABLE IF NOT EXISTS products (
                id INTEGER PRIMARY KEY,
                name TEXT,
                required_points INTEGER
            )
        ''')
    
        conn.commit()
        conn.close()
    
    • usersテーブル: 子供の名前と生年月日を記録。
    • pointsテーブル: ポイントの履歴と理由を記録。
    • productsテーブル: 商品名と交換に必要なポイント数を記録。

    3. ユーザー登録と削除

    次に、子供(ユーザー)をシステムに登録したり、削除する機能を実装します。

    ユーザー登録

    pythonコードをコピーするdef register_user(name, birthdate):
        conn = sqlite3.connect('points.db')
        c = conn.cursor()
        try:
            c.execute('INSERT INTO users (name, birthdate) VALUES (?, ?)', (name, birthdate))
            conn.commit()
            messagebox.showinfo("成功", "ユーザー登録完了")
        except sqlite3.IntegrityError:
            messagebox.showerror("エラー", "このユーザーは既に登録されています")
        conn.close()
    

    ユーザー削除

    def delete_user(name, birthdate):
        conn = sqlite3.connect('points.db')
        c = conn.cursor()
        c.execute('SELECT id FROM users WHERE name = ? AND birthdate = ?', (name, birthdate))
        user = c.fetchone()
        if user:
            c.execute('DELETE FROM points WHERE user_id = ?', (user[0],))
            c.execute('DELETE FROM users WHERE id = ?', (user[0],))
            conn.commit()
            messagebox.showinfo("成功", "ユーザー削除完了")
        else:
            messagebox.showerror("エラー", "ユーザーが見つかりません")
        conn.close()
    
    • register_user(): 子供を名前と生年月日で登録します。
    • delete_user(): 子供を削除し、関連するポイントデータも削除します。

    4. ポイントの付与と確認

    ポイントを付与する機能

    pythonコードをコピーするdef add_points(name, birthdate, points, reason):
        conn = sqlite3.connect('points.db')
        c = conn.cursor()
        c.execute('SELECT id FROM users WHERE name = ? AND birthdate = ?', (name, birthdate))
        user = c.fetchone()
        if user:
            c.execute('INSERT INTO points (user_id, points, reason) VALUES (?, ?, ?)', (user[0], points, reason))
            conn.commit()
            messagebox.showinfo("成功", f"{points}ポイント追加しました(理由: {reason})")
        else:
            messagebox.showerror("エラー", "ユーザーが見つかりません")
        conn.close()
    

    ポイント確認機能

    pythonコードをコピーするdef check_points(name, birthdate):
        conn = sqlite3.connect('points.db')
        c = conn.cursor()
        c.execute('SELECT id FROM users WHERE name = ? AND birthdate = ?', (name, birthdate))
        user = c.fetchone()
        if user:
            c.execute('SELECT SUM(points) FROM points WHERE user_id = ?', (user[0],))
            total_points = c.fetchone()[0]
            if total_points is None:
                total_points = 0
    
            c.execute('SELECT points, reason FROM points WHERE user_id = ?', (user[0],))
            history = c.fetchall()
    
            result = f"現在のポイント: {total_points}\n\n【ポイント履歴】\n"
            for entry in history:
                result += f"ポイント: {entry[0]}, 理由: {entry[1]}\n"
            
            messagebox.showinfo("ポイント確認", result)
        else:
            messagebox.showerror("エラー", "ユーザーが見つかりません")
        conn.close()
    
    • add_points(): 子供にポイントを付与し、その理由を記録します。
    • check_points(): 子供の現在のポイントと過去のポイント履歴を確認できます。

    5. 商品登録と商品交換

    商品を登録する機能

    def register_product(name, required_points):
        conn = sqlite3.connect('points.db')
        c = conn.cursor()
        c.execute('INSERT INTO products (name, required_points) VALUES (?, ?)', (name, required_points))
        conn.commit()
        messagebox.showinfo("成功", f"商品 {name} が登録されました(必要ポイント: {required_points})")
        conn.close()
    

    商品とポイントを交換する機能

    def use_points_for_product(name, birthdate, product_id):
        conn = sqlite3.connect('points.db')
        c = conn.cursor()
    
        c.execute('SELECT id FROM users WHERE name = ? AND birthdate = ?', (name, birthdate))
        user = c.fetchone()
        if not user:
            messagebox.showerror("エラー", "ユーザーが見つかりません")
            conn.close()
            return
    
        c.execute('SELECT required_points FROM products WHERE id = ?', (product_id,))
        product = c.fetchone()
        if not product:
            messagebox.showerror("エラー", "商品が見つかりません")
            conn.close()
            return
    
        c.execute('SELECT SUM(points) FROM points WHERE user_id = ?', (user[0],))
        total_points = c.fetchone()[0]
        if total_points is None:
            total_points = 0
    
        if total_points >= product[0]:
            c.execute('INSERT INTO points (user_id, points, reason) VALUES (?, ?, ?)', (user[0], -product[0], '商品交換'))
            conn.commit()
            messagebox.showinfo("成功", "商品と交換しました!")
        else:
            messagebox.showerror("エラー", "ポイントが不足しています")
    
        conn.close()
    
    • register_product(): 商品を登録し、交換に必要なポイントを指定します。
    • use_points_for_product(): 子供が貯めたポイントを使って商品と交換します。

    6. tkinterを使ったGUI

    ユーザー画面

    子供がポイントを確認し、商品と交換するための画面です。

    pythonコードをコピーするdef create_user_gui():
        user_window = tk.Toplevel()
        user_window.title("ユーザー画面")
    
        tk.Label(user_window, text="名前").grid(row=0, column=0)
        name_entry = tk.Entry(user_window)
        name_entry.grid(row=0, column=1)
    
        tk.Label(user_window, text="生年月日 (YYYY-MM-DD)").grid(row=1, column=0)
        birthdate_entry = tk.Entry(user_window)
        birthdate_entry.grid(row=1, column=1)
    
        tk.Button(user_window, text="ポイント確認", command=lambda: check_points(name_entry.get(), birthdate_entry.get())).grid(row=2, column=0, columnspan=2)
    
        tk.Label(user_window, text="商品ID").grid(row=3, column=0)
        product_entry = tk.Entry(user_window)
        product_entry.grid(row=3, column=1)
        tk.Button(user_window, text="商品と交換", command=lambda: use_points_for_product(name_entry.get(), birthdate_entry.get(), int(product_entry.get()))).grid(row=4, column=0, columnspan=2)
    

    お店画面

    親御さんが子供を登録したり、ポイントを付与したり、商品を登録するための画面です。

    def create_shop_gui():
        shop_window = tk.Toplevel()
        shop_window.title("お店画面")
    
        tk.Label(shop_window, text="名前").grid(row=0, column=0)
        name_entry = tk.Entry(shop_window)
        name_entry.grid(row=0, column=1)
    
        tk.Label(shop_window, text="生年月日 (YYYY-MM-DD)").grid(row=1, column=0)
        birthdate_entry = tk.Entry(shop_window)
        birthdate_entry.grid(row=1, column=1)
    
        tk.Label(shop_window, text="ポイント数").grid(row=2, column=0)
        points_entry = tk.Entry(shop_window)
        points_entry.grid(row=2, column=1)
    
        tk.Label(shop_window, text="理由").grid(row=3, column=0)
        reason_entry = tk.Entry(shop_window)
        reason_entry.grid(row=3, column=1)
    
        tk.Button(shop_window, text="ユーザー登録", command=lambda: register_user(name_entry.get(), birthdate_entry.get())).grid(row=4, column=0, columnspan=2)
        tk.Button(shop_window, text="ユーザー削除", command=lambda: delete_user(name_entry.get(), birthdate_entry.get())).grid(row=5, column=0, columnspan=2)
        tk.Button(shop_window, text="ポイント付与", command=lambda: add_points(name_entry.get(), birthdate_entry.get(), int(points_entry.get()), reason_entry.get())).grid(row=6, column=0, columnspan=2)
    
        tk.Label(shop_window, text="商品名").grid(row=7, column=0)
        product_name_entry = tk.Entry(shop_window)
        product_name_entry.grid(row=7, column=1)
    
        tk.Label(shop_window, text="必要ポイント").grid(row=8, column=0)
        product_points_entry = tk.Entry(shop_window)
        product_points_entry.grid(row=8, column=1)
    
        tk.Button(shop_window, text="商品登録", command=lambda: register_product(product_name_entry.get(), int(product_points_entry.get()))).grid(row=9, column=0, columnspan=2)
    

    7. メイン画面と起動

    最後に、メイン画面から「ユーザー画面」か「お店画面」を選択できるようにします。

    def create_main_gui():
        root = tk.Tk()
        root.title("お手伝いポイントカードシステム")
    
        tk.Button(root, text="ユーザー画面", command=create_user_gui).pack(pady=10)
        tk.Button(root, text="お店画面", command=create_shop_gui).pack(pady=10)
    
        root.mainloop()
    
    # データベースの初期設定を行い、システムを起動します
    setup_database()
    create_main_gui()
    

    すべてのコード

    以下に、すべてのコードを一つにまとめたものを示します。これをコピー&ペーストして使ってください。

    import sqlite3
    import tkinter as tk
    from tkinter import messagebox, ttk
    
    # データベース初期設定
    def setup_database():
        conn = sqlite3.connect('points.db')
        c = conn.cursor()
        
        # ユーザーテーブル
        c.execute('''
            CREATE TABLE IF NOT EXISTS users (
                id INTEGER PRIMARY KEY,
                name TEXT,
                birthdate TEXT
            )
        ''')
    
        # ポイントテーブル
        c.execute('''
            CREATE TABLE IF NOT EXISTS points (
                user_id INTEGER,
                points INTEGER,
                reason TEXT,
                date TEXT,
                FOREIGN KEY(user_id) REFERENCES users(id)
            )
        ''')
    
        # 商品テーブル
        c.execute('''
            CREATE TABLE IF NOT EXISTS products (
                id INTEGER PRIMARY KEY,
                name TEXT,
                required_points INTEGER
            )
        ''')
    
        conn.commit()
        conn.close()
    
    # ユーザー登録
    def register_user(name, birthdate):
        conn = sqlite3.connect('points.db')
        c = conn.cursor()
        try:
            c.execute('INSERT INTO users (name, birthdate) VALUES (?, ?)', (name, birthdate))
            conn.commit()
            messagebox.showinfo("成功", "ユーザー登録完了")
        except sqlite3.IntegrityError:
            messagebox.showerror("エラー", "このユーザーは既に登録されています")
        conn.close()
    
    # ユーザー削除
    def delete_user(name, birthdate):
        conn = sqlite3.connect('points.db')
        c = conn.cursor()
        c.execute('SELECT id FROM users WHERE name = ? AND birthdate = ?', (name, birthdate))
        user = c.fetchone()
        if user:
            c.execute('DELETE FROM points WHERE user_id = ?', (user[0],))
            c.execute('DELETE FROM users WHERE id = ?', (user[0],))
            conn.commit()
            messagebox.showinfo("成功", "ユーザー削除完了")
        else:
            messagebox.showerror("エラー", "ユーザーが見つかりません")
        conn.close()
    
    # 商品登録
    def register_product(name, required_points):
        conn = sqlite3.connect('points.db')
        c = conn.cursor()
        c.execute('INSERT INTO products (name, required_points) VALUES (?, ?)', (name, required_points))
        conn.commit()
        messagebox.showinfo("成功", f"商品 {name} が登録されました(必要ポイント: {required_points})")
        conn.close()
    
    # ポイント確認(ユーザー画面)
    def check_points(name, birthdate):
        conn = sqlite3.connect('points.db')
        c = conn.cursor()
        c.execute('SELECT id FROM users WHERE name = ? AND birthdate = ?', (name, birthdate))
        user = c.fetchone()
        if user:
            # 合計ポイント
            c.execute('SELECT SUM(points) FROM points WHERE user_id = ?', (user[0],))
            total_points = c.fetchone()[0]
            if total_points is None:
                total_points = 0
            
            # ポイント履歴
            c.execute('SELECT date, reason, points FROM points WHERE user_id = ?', (user[0],))
            history = c.fetchall()
            
            return total_points, history
        else:
            messagebox.showerror("エラー", "ユーザーが見つかりません")
        conn.close()
        return 0, []
    
    # 商品と交換
    def exchange_product(name, birthdate, product_id):
        conn = sqlite3.connect('points.db')
        c = conn.cursor()
        
        # ユーザーID取得
        c.execute('SELECT id FROM users WHERE name = ? AND birthdate = ?', (name, birthdate))
        user = c.fetchone()
        if not user:
            messagebox.showerror("エラー", "ユーザーが見つかりません")
            conn.close()
            return
        
        # 商品情報取得
        c.execute('SELECT required_points FROM products WHERE id = ?', (product_id,))
        product = c.fetchone()
        if not product:
            messagebox.showerror("エラー", "商品が見つかりません")
            conn.close()
            return
        
        # 合計ポイント確認
        c.execute('SELECT SUM(points) FROM points WHERE user_id = ?', (user[0],))
        total_points = c.fetchone()[0]
        if total_points is None:
            total_points = 0
    
        if total_points >= product[0]:
            # ポイント減算
            c.execute('INSERT INTO points (user_id, points, reason, date) VALUES (?, ?, ?, date("now"))', (user[0], -product[0], '商品交換'))
            conn.commit()
            messagebox.showinfo("成功", "商品と交換しました!")
        else:
            messagebox.showerror("エラー", "ポイントが不足しています")
        
        conn.close()
    
    # 商品リスト取得
    def get_products():
        conn = sqlite3.connect('points.db')
        c = conn.cursor()
        c.execute('SELECT id, name, required_points FROM products')
        products = c.fetchall()
        conn.close()
        return products
    
    # ユーザーリスト取得
    def get_users():
        conn = sqlite3.connect('points.db')
        c = conn.cursor()
        c.execute('SELECT id, name, birthdate FROM users')
        users = c.fetchall()
        conn.close()
        return users
    
    # ユーザー画面
    def create_user_gui():
        user_window = tk.Toplevel()
        user_window.title("ユーザー画面")
        user_window.geometry("600x400")
    
        tk.Label(user_window, text="名前").grid(row=0, column=0)
        name_entry = tk.Entry(user_window)
        name_entry.grid(row=0, column=1)
    
        tk.Label(user_window, text="生年月日 (YYYY-MM-DD)").grid(row=1, column=0)
        birthdate_entry = tk.Entry(user_window)
        birthdate_entry.grid(row=1, column=1)
    
        # 現在の合計ポイントと履歴を表示
        def show_points():
            total_points, history = check_points(name_entry.get(), birthdate_entry.get())
            total_points_label.config(text=f"合計ポイント: {total_points}")
            for row in history_tree.get_children():
                history_tree.delete(row)
            for h in history:
                history_tree.insert("", "end", values=h)
    
        tk.Button(user_window, text="ポイント確認", command=show_points).grid(row=2, column=0, columnspan=2)
    
        total_points_label = tk.Label(user_window, text="合計ポイント: 0")
        total_points_label.grid(row=3, column=0, columnspan=2)
    
        # ポイント履歴テーブル
        history_tree = ttk.Treeview(user_window, columns=("date", "reason", "points"), show="headings")
        history_tree.heading("date", text="日付")
        history_tree.heading("reason", text="商品/理由")
        history_tree.heading("points", text="ポイント")
        history_tree.grid(row=4, column=0, columnspan=2)
    
        # 商品交換ボタン
        def show_product_list():
            product_window = tk.Toplevel()
            product_window.title("商品一覧")
            product_window.geometry("400x300")
    
            product_tree = ttk.Treeview(product_window, columns=("name", "required_points"), show="headings")
            product_tree.heading("name", text="商品名")
            product_tree.heading("required_points", text="必要ポイント")
            product_tree.pack(fill=tk.BOTH, expand=True)
    
            for p in get_products():
                product_tree.insert("", "end", values=(p[1], p[2]))
    
            def exchange():
                selected_item = product_tree.selection()
                if selected_item:
                    product_name = product_tree.item(selected_item)["values"][0]
                    product_id = None
                    for p in get_products():
                        if p[1] == product_name:
                            product_id = p[0]
                            break
                    if product_id:
                        exchange_product(name_entry.get(), birthdate_entry.get(), product_id)
    
            tk.Button(product_window, text="商品と交換", command=exchange).pack(pady=10)
    
        tk.Button(user_window, text="商品と交換", command=show_product_list).grid(row=5, column=0, columnspan=2)
    
    # お店画面
    def create_shop_gui():
        shop_window = tk.Toplevel()
        shop_window.title("お店画面")
        shop_window.geometry("600x400")
    
        def user_registration():
            registration_window = tk.Toplevel()
            registration_window.title("ユーザー登録")
            tk.Label(registration_window, text="名前").grid(row=0, column=0)
            name_entry = tk.Entry(registration_window)
            name_entry.grid(row=0, column=1)
    
            tk.Label(registration_window, text="生年月日 (YYYY-MM-DD)").grid(row=1, column=0)
            birthdate_entry = tk.Entry(registration_window)
            birthdate_entry.grid(row=1, column=1)
    
            tk.Button(registration_window, text="登録", command=lambda: register_user(name_entry.get(), birthdate_entry.get())).grid(row=2, column=0, columnspan=2)
    
        def user_deletion():
            deletion_window = tk.Toplevel()
            deletion_window.title("ユーザー削除")
            tk.Label(deletion_window, text="名前").grid(row=0, column=0)
            name_entry = tk.Entry(deletion_window)
            name_entry.grid(row=0, column=1)
    
            tk.Label(deletion_window, text="生年月日 (YYYY-MM-DD)").grid(row=1, column=0)
            birthdate_entry = tk.Entry(deletion_window)
            birthdate_entry.grid(row=1, column=1)
    
            tk.Button(deletion_window, text="削除", command=lambda: delete_user(name_entry.get(), birthdate_entry.get())).grid(row=2, column=0, columnspan=2)
    
        def product_registration():
            product_window = tk.Toplevel()
            product_window.title("商品登録")
            tk.Label(product_window, text="商品名").grid(row=0, column=0)
            product_name_entry = tk.Entry(product_window)
            product_name_entry.grid(row=0, column=1)
    
            tk.Label(product_window, text="必要ポイント").grid(row=1, column=0)
            product_points_entry = tk.Entry(product_window)
            product_points_entry.grid(row=1, column=1)
    
            tk.Button(product_window, text="登録", command=lambda: register_product(product_name_entry.get(), int(product_points_entry.get()))).grid(row=2, column=0, columnspan=2)
    
        def show_overview():
            overview_window = tk.Toplevel()
            overview_window.title("ユーザー・商品確認")
            overview_window.geometry("800x400")
    
            # ユーザーリスト
            user_tree = ttk.Treeview(overview_window, columns=("name", "birthdate"), show="headings")
            user_tree.heading("name", text="ユーザー名")
            user_tree.heading("birthdate", text="生年月日")
            user_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
    
            for u in get_users():
                user_tree.insert("", "end", values=(u[1], u[2]))
    
            # 商品リスト
            product_tree = ttk.Treeview(overview_window, columns=("name", "required_points"), show="headings")
            product_tree.heading("name", text="商品名")
            product_tree.heading("required_points", text="必要ポイント")
            product_tree.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
    
            for p in get_products():
                product_tree.insert("", "end", values=(p[1], p[2]))
    
        tk.Button(shop_window, text="ユーザー登録", command=user_registration).grid(row=0, column=0, columnspan=2, pady=10)
        tk.Button(shop_window, text="ユーザー削除", command=user_deletion).grid(row=1, column=0, columnspan=2, pady=10)
        tk.Button(shop_window, text="商品登録", command=product_registration).grid(row=2, column=0, columnspan=2, pady=10)
        tk.Button(shop_window, text="確認", command=show_overview).grid(row=3, column=0, columnspan=2, pady=10)
    
    # メイン画面
    def create_main_gui():
        root = tk.Tk()
        root.title("お手伝いポイントカードシステム")
        root.geometry("400x200")
    
        tk.Button(root, text="ユーザー画面", command=create_user_gui, height=3, width=20).pack(pady=10)
        tk.Button(root, text="お店画面", command=create_shop_gui, height=3, width=20).pack(pady=10)
    
        root.mainloop()
    
    # データベース初期設定とメインGUI起動
    setup_database()
    create_main_gui()
    
    楽天ブックス
    ¥2,860 (2025/04/03 11:35時点 | 楽天市場調べ)

    コメント

    タイトルとURLをコピーしました