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()

コメント