「一對多關聯」圖片(老師 $\rightarrow$ 課程),將 teacher2 (老師資料表) 和 course2 (課程資料表) 的管理功能整合到一個單一的 Tkinter 應用程式中。
這個程式將使用 Notebook (分頁) 來分別管理老師和課程資料,並確保課程資料的「老師編號」有外來鍵約束。
操作步驟:
1. 建立資料庫: 點擊頂部的「1. 建立資料庫」按鈕,會創建
MySchoolDB.db檔案,以及teacher2和course2兩個資料表。切換分頁: 使用上方的「老師資料 (teacher2)」和「課程資料 (course2)」分頁進行不同資料表的管理。
老師資料 (Teacher2) 操作: 在第一個分頁中,可以新增、更正、刪除老師資料。
課程資料 (Course2) 操作: 在第二個分頁中,可以新增、更正、刪除課程資料。在新增或更正時,
老師編號必須填入一個在 老師資料 分頁中存在的編號,否則會新增失敗。6. 刪除資料庫: 點擊頂部的「6. 刪除資料庫」會刪除整個
.db檔案。
import tkinter as tk
from tkinter import messagebox, ttk
import sqlite3
import os
DB_NAME = "MySchoolDB.db"
# --- 資料庫操作類別 ---
class DatabaseManager:
"""管理 MySchoolDB.db 中的 teacher2 和 course2 資料表的 CRUD 操作。"""
def __init__(self, db_name=DB_NAME):
self.db_name = db_name
def connect(self):
"""建立資料庫連線並啟用外來鍵約束"""
try:
conn = sqlite3.connect(self.db_name)
# 必須啟用外來鍵約束
conn.execute("PRAGMA foreign_keys = ON;")
return conn
except sqlite3.Error as e:
messagebox.showerror("資料庫錯誤", f"無法連線到資料庫: {e}")
return None
# --- 1. 建立資料庫 ---
def create_tables(self):
"""建立 teacher2 和 course2 兩個資料表"""
conn = self.connect()
if not conn: return False
try:
cursor = conn.cursor()
# 1. 建立 teacher2 (老師資料表 - 主表)
# 欄位: *老師編號 (主鍵), 老師姓名, 研究領域
cursor.execute('''
CREATE TABLE IF NOT EXISTS teacher2 (
老師編號 TEXT PRIMARY KEY,
老師姓名 TEXT NOT NULL,
研究領域 TEXT
)
''')
# 2. 建立 course2 (課程資料表 - 從表, 包含外來鍵)
# 欄位: *課程代號 (主鍵), 課程名稱, 學分數, #老師編號 (外來鍵)
cursor.execute('''
CREATE TABLE IF NOT EXISTS course2 (
課程代號 TEXT PRIMARY KEY,
課程名稱 TEXT NOT NULL,
學分數 INTEGER,
老師編號 TEXT,
FOREIGN KEY (老師編號) REFERENCES teacher2(老師編號) ON DELETE SET NULL ON UPDATE CASCADE
)
''')
conn.commit()
return True
except sqlite3.Error as e:
messagebox.showerror("資料庫錯誤", f"建立資料表失敗: {e}")
return False
finally:
if conn: conn.close()
# --- 2/3/4/5. 老師資料 (teacher2) CRUD 操作 ---
def execute_teacher_crud(self, query, params=None, action="select"):
"""執行老師資料表的 CRUD 操作"""
conn = self.connect()
if not conn: return False if action != "select" else []
try:
cursor = conn.cursor()
if params:
cursor.execute(query, params)
else:
cursor.execute(query)
if action == "select":
return cursor.fetchall()
conn.commit()
return cursor.rowcount
except sqlite3.IntegrityError as e:
if "UNIQUE constraint failed" in str(e):
messagebox.showwarning("操作失敗", "該老師編號已存在!")
else:
messagebox.showerror("資料庫錯誤", f"{action} 操作失敗: {e}")
conn.rollback()
return False
except sqlite3.Error as e:
messagebox.showerror("資料庫錯誤", f"{action} 操作失敗: {e}")
conn.rollback()
return False
finally:
if conn: conn.close()
# --- 2/3/4/5. 課程資料 (course2) CRUD 操作 ---
def execute_course_crud(self, query, params=None, action="select"):
"""執行課程資料表的 CRUD 操作"""
conn = self.connect()
if not conn: return False if action != "select" else []
try:
cursor = conn.cursor()
if params:
cursor.execute(query, params)
else:
cursor.execute(query)
if action == "select":
# 為了顯示完整的資訊,使用 LEFT JOIN 連接 teacher2 表
return cursor.fetchall()
conn.commit()
return cursor.rowcount
except sqlite3.IntegrityError as e:
if "UNIQUE constraint failed" in str(e):
messagebox.showwarning("操作失敗", "該課程代號已存在!")
elif "foreign key constraint failed" in str(e):
messagebox.showwarning("操作失敗", "老師編號不存在,請先新增老師資料!")
else:
messagebox.showerror("資料庫錯誤", f"{action} 操作失敗: {e}")
conn.rollback()
return False
except sqlite3.Error as e:
messagebox.showerror("資料庫錯誤", f"{action} 操作失敗: {e}")
conn.rollback()
return False
finally:
if conn: conn.close()
# --- 6. 刪除資料庫 ---
def delete_database_file(self):
"""刪除資料庫檔案"""
if os.path.exists(self.db_name):
try:
# 確保檔案沒有被鎖定
os.remove(self.db_name)
return True
except OSError as e:
messagebox.showerror("刪除失敗", f"無法刪除資料庫檔案 {self.db_name}: {e}\n請確認是否有其他程式正在使用此檔案。")
return False
else:
messagebox.showwarning("刪除失敗", f"資料庫檔案 {self.db_name} 不存在。")
return False
# --- Tkinter GUI 應用程式類別 ---
class IntegratedManager(tk.Tk):
def __init__(self):
super().__init__()
self.title("學校資料管理系統 (整合版)")
self.db = DatabaseManager()
self.geometry("850x550")
self.create_main_widgets()
self.load_teacher_data() # 啟動時載入老師資料
self.load_course_data() # 啟動時載入課程資料
def create_main_widgets(self):
# 頂部功能按鈕
button_frame = tk.Frame(self, padx=10, pady=5)
button_frame.pack(fill='x')
buttons = [
("1. 建立資料庫", self.create_db_command),
("6. 刪除資料庫", self.delete_db_command),
]
for i, (text, command) in enumerate(buttons):
btn = tk.Button(button_frame, text=text, command=command, width=15)
btn.grid(row=0, column=i, padx=5, pady=5)
# Notebook 分頁
self.notebook = ttk.Notebook(self)
self.notebook.pack(pady=10, padx=10, expand=True, fill="both")
# 老師資料分頁
self.teacher_frame = ttk.Frame(self.notebook)
self.notebook.add(self.teacher_frame, text=' 老師資料 (teacher2) ')
self.create_teacher_widgets(self.teacher_frame)
# 課程資料分頁
self.course_frame = ttk.Frame(self.notebook)
self.notebook.add(self.course_frame, text=' 課程資料 (course2) ')
self.create_course_widgets(self.course_frame)
# ==================================
# === 老師資料 (Teacher2) 介面與功能 ===
# ==================================
def create_teacher_widgets(self, parent):
# 輸入區
input_frame = tk.LabelFrame(parent, text="輸入/查詢區", padx=10, pady=10)
input_frame.pack(fill='x', padx=10, pady=5)
tk.Label(input_frame, text="老師編號:").grid(row=0, column=0, padx=5, pady=2, sticky='w')
self.t_entry_id = tk.Entry(input_frame)
self.t_entry_id.grid(row=0, column=1, padx=5, pady=2, sticky='w')
tk.Label(input_frame, text="老師姓名:").grid(row=0, column=2, padx=5, pady=2, sticky='w')
self.t_entry_name = tk.Entry(input_frame)
self.t_entry_name.grid(row=0, column=3, padx=5, pady=2, sticky='w')
tk.Label(input_frame, text="研究領域:").grid(row=1, column=0, padx=5, pady=2, sticky='w')
self.t_entry_field = tk.Entry(input_frame)
self.t_entry_field.grid(row=1, column=1, padx=5, pady=2, sticky='w')
# 按鈕區塊
t_btn_frame = tk.Frame(parent, padx=10, pady=5)
t_btn_frame.pack(fill='x')
tk.Button(t_btn_frame, text="2. 新增資料", command=self.add_teacher_data).grid(row=0, column=0, padx=5)
tk.Button(t_btn_frame, text="3. 更正資料", command=self.update_teacher_data).grid(row=0, column=1, padx=5)
tk.Button(t_btn_frame, text="4. 刪除資料", command=self.delete_teacher_data).grid(row=0, column=2, padx=5)
tk.Button(t_btn_frame, text="5. 顯示所有資料", command=self.load_teacher_data).grid(row=0, column=3, padx=5)
# Treeview 顯示區
self.teacher_tree = ttk.Treeview(parent, columns=('老師編號', '老師姓名', '研究領域'), show='headings')
self.teacher_tree.heading('老師編號', text='老師編號')
self.teacher_tree.column('老師編號', width=120, anchor='center')
self.teacher_tree.heading('老師姓名', text='老師姓名')
self.teacher_tree.column('老師姓名', width=150, anchor='w')
self.teacher_tree.heading('研究領域', text='研究領域')
self.teacher_tree.column('研究領域', width=200, anchor='w')
self.teacher_tree.pack(fill='both', expand=True, padx=10, pady=10)
self.teacher_tree.bind('<<TreeviewSelect>>', self.on_teacher_select)
def on_teacher_select(self, event):
selected_item = self.teacher_tree.focus()
if selected_item:
values = self.teacher_tree.item(selected_item, 'values')
self.clear_teacher_entries()
self.t_entry_id.insert(0, values[0])
self.t_entry_name.insert(0, values[1])
self.t_entry_field.insert(0, values[2])
def clear_teacher_entries(self):
self.t_entry_id.delete(0, tk.END)
self.t_entry_name.delete(0, tk.END)
self.t_entry_field.delete(0, tk.END)
# 5. 顯示所有資料 (老師)
def load_teacher_data(self):
for item in self.teacher_tree.get_children():
self.teacher_tree.delete(item)
query = "SELECT 老師編號, 老師姓名, 研究領域 FROM teacher2 ORDER BY 老師編號"
data = self.db.execute_teacher_crud(query, action="select")
for row in data:
self.teacher_tree.insert('', tk.END, values=row)
# 2. 新增資料 (老師)
def add_teacher_data(self):
teacher_id = self.t_entry_id.get().strip()
name = self.t_entry_name.get().strip()
field = self.t_entry_field.get().strip()
if not teacher_id or not name:
messagebox.showwarning("警告", "老師編號和姓名為必填!")
return
query = "INSERT INTO teacher2 (老師編號, 老師姓名, 研究領域) VALUES (?, ?, ?)"
if self.db.execute_teacher_crud(query, (teacher_id, name, field), action="insert"):
messagebox.showinfo("成功", "老師資料新增成功!")
self.clear_teacher_entries()
self.load_teacher_data()
# 3. 更正資料 (老師)
def update_teacher_data(self):
teacher_id = self.t_entry_id.get().strip()
name = self.t_entry_name.get().strip()
field = self.t_entry_field.get().strip()
if not teacher_id or not name:
messagebox.showwarning("警告", "請輸入要更正的老師編號和新的姓名!")
return
query = "UPDATE teacher2 SET 老師姓名=?, 研究領域=? WHERE 老師編號=?"
if self.db.execute_teacher_crud(query, (name, field, teacher_id), action="update"):
messagebox.showinfo("成功", f"老師編號 {teacher_id} 資料更正成功!")
self.clear_teacher_entries()
self.load_teacher_data()
self.load_course_data() # 老師資料變動可能影響課程顯示
# 4. 刪除資料 (老師)
def delete_teacher_data(self):
teacher_id = self.t_entry_id.get().strip()
if not teacher_id:
messagebox.showwarning("警告", "請輸入要刪除的老師編號!")
return
# 由於 teacher2 是 course2 的主鍵,刪除會影響 course2 中的外來鍵 (ON DELETE SET NULL)
if messagebox.askyesno("確認刪除", f"**警告:** 確定要刪除老師編號 {teacher_id} 的資料嗎?\n相關課程的老師編號將被設為 NULL。"):
query = "DELETE FROM teacher2 WHERE 老師編號=?"
if self.db.execute_teacher_crud(query, (teacher_id,), action="delete"):
messagebox.showinfo("成功", "老師資料刪除成功!")
self.clear_teacher_entries()
self.load_teacher_data()
self.load_course_data() # 更新課程顯示
# ==================================
# === 課程資料 (Course2) 介面與功能 ===
# ==================================
def create_course_widgets(self, parent):
# 輸入區
input_frame = tk.LabelFrame(parent, text="輸入/查詢區", padx=10, pady=10)
input_frame.pack(fill='x', padx=10, pady=5)
tk.Label(input_frame, text="課程代號:").grid(row=0, column=0, padx=5, pady=2, sticky='w')
self.c_entry_id = tk.Entry(input_frame)
self.c_entry_id.grid(row=0, column=1, padx=5, pady=2, sticky='w')
tk.Label(input_frame, text="課程名稱:").grid(row=0, column=2, padx=5, pady=2, sticky='w')
self.c_entry_name = tk.Entry(input_frame)
self.c_entry_name.grid(row=0, column=3, padx=5, pady=2, sticky='w')
tk.Label(input_frame, text="學分數:").grid(row=1, column=0, padx=5, pady=2, sticky='w')
self.c_entry_credits = tk.Entry(input_frame)
self.c_entry_credits.grid(row=1, column=1, padx=5, pady=2, sticky='w')
tk.Label(input_frame, text="老師編號 (#外來鍵):").grid(row=1, column=2, padx=5, pady=2, sticky='w')
self.c_entry_teacher_id = tk.Entry(input_frame)
self.c_entry_teacher_id.grid(row=1, column=3, padx=5, pady=2, sticky='w')
# 按鈕區塊
c_btn_frame = tk.Frame(parent, padx=10, pady=5)
c_btn_frame.pack(fill='x')
tk.Button(c_btn_frame, text="2. 新增資料", command=self.add_course_data).grid(row=0, column=0, padx=5)
tk.Button(c_btn_frame, text="3. 更正資料", command=self.update_course_data).grid(row=0, column=1, padx=5)
tk.Button(c_btn_frame, text="4. 刪除資料", command=self.delete_course_data).grid(row=0, column=2, padx=5)
tk.Button(c_btn_frame, text="5. 顯示所有資料", command=self.load_course_data).grid(row=0, column=3, padx=5)
# Treeview 顯示區
self.course_tree = ttk.Treeview(parent, columns=('代號', '名稱', '學分', '老師編號', '老師姓名'), show='headings')
self.course_tree.heading('代號', text='課程代號')
self.course_tree.column('代號', width=100, anchor='center')
self.course_tree.heading('名稱', text='課程名稱')
self.course_tree.column('名稱', width=180, anchor='w')
self.course_tree.heading('學分', text='學分數')
self.course_tree.column('學分', width=80, anchor='center')
self.course_tree.heading('老師編號', text='老師編號')
self.course_tree.column('老師編號', width=120, anchor='center')
self.course_tree.heading('老師姓名', text='授課老師')
self.course_tree.column('老師姓名', width=150, anchor='w')
self.course_tree.pack(fill='both', expand=True, padx=10, pady=10)
self.course_tree.bind('<<TreeviewSelect>>', self.on_course_select)
def on_course_select(self, event):
selected_item = self.course_tree.focus()
if selected_item:
values = self.course_tree.item(selected_item, 'values')
self.clear_course_entries()
self.c_entry_id.insert(0, values[0]) # 課程代號
self.c_entry_name.insert(0, values[1]) # 課程名稱
self.c_entry_credits.insert(0, values[2]) # 學分數
self.c_entry_teacher_id.insert(0, values[3]) # 老師編號
def clear_course_entries(self):
self.c_entry_id.delete(0, tk.END)
self.c_entry_name.delete(0, tk.END)
self.c_entry_credits.delete(0, tk.END)
self.c_entry_teacher_id.delete(0, tk.END)
# 5. 顯示所有資料 (課程)
def load_course_data(self):
for item in self.course_tree.get_children():
self.course_tree.delete(item)
# 查詢課程資料並連老師姓名
query = '''
SELECT
c.課程代號, c.課程名稱, c.學分數, c.老師編號, t.老師姓名
FROM
course2 c
LEFT JOIN
teacher2 t ON c.老師編號 = t.老師編號
ORDER BY c.課程代號
'''
data = self.db.execute_course_crud(query, action="select")
for row in data:
# 將 None 替換為空字串以便 Treeview 顯示
processed_row = tuple('' if x is None else x for x in row)
self.course_tree.insert('', tk.END, values=processed_row)
# 2. 新增資料 (課程)
def add_course_data(self):
course_id = self.c_entry_id.get().strip()
name = self.c_entry_name.get().strip()
credits_str = self.c_entry_credits.get().strip()
teacher_id = self.c_entry_teacher_id.get().strip()
if not course_id or not name or not credits_str:
messagebox.showwarning("警告", "課程代號、名稱和學分數為必填!")
return
try:
credits = int(credits_str)
except ValueError:
messagebox.showwarning("警告", "學分數必須是數字!")
return
query = "INSERT INTO course2 (課程代號, 課程名稱, 學分數, 老師編號) VALUES (?, ?, ?, ?)"
if self.db.execute_course_crud(query, (course_id, name, credits, teacher_id), action="insert"):
messagebox.showinfo("成功", "課程資料新增成功!")
self.clear_course_entries()
self.load_course_data()
# 3. 更正資料 (課程)
def update_course_data(self):
course_id = self.c_entry_id.get().strip()
name = self.c_entry_name.get().strip()
credits_str = self.c_entry_credits.get().strip()
teacher_id = self.c_entry_teacher_id.get().strip()
if not course_id or not name or not credits_str:
messagebox.showwarning("警告", "請輸入完整的更正資訊!")
return
try:
credits = int(credits_str)
except ValueError:
messagebox.showwarning("警告", "學分數必須是數字!")
return
query = "UPDATE course2 SET 課程名稱=?, 學分數=?, 老師編號=? WHERE 課程代號=?"
if self.db.execute_course_crud(query, (name, credits, teacher_id, course_id), action="update"):
messagebox.showinfo("成功", f"課程代號 {course_id} 資料更正成功!")
self.clear_course_entries()
self.load_course_data()
# 4. 刪除資料 (課程)
def delete_course_data(self):
course_id = self.c_entry_id.get().strip()
if not course_id:
messagebox.showwarning("警告", "請輸入要刪除的課程代號!")
return
if messagebox.askyesno("確認刪除", f"確定要刪除課程代號 {course_id} 的資料嗎?"):
query = "DELETE FROM course2 WHERE 課程代號=?"
if self.db.execute_course_crud(query, (course_id,), action="delete"):
messagebox.showinfo("成功", "課程資料刪除成功!")
self.clear_course_entries()
self.load_course_data()
# ==================================
# === 共同功能 ===
# ==================================
# 1. 建立資料庫
def create_db_command(self):
if self.db.create_tables():
messagebox.showinfo("成功", f"資料庫 {DB_NAME} 及其所有資料表已成功建立或已存在。")
self.load_teacher_data()
self.load_course_data()
# 6. 刪除資料庫
def delete_db_command(self):
if messagebox.askyesno("確認刪除資料庫", f"**警告:** 確定要刪除整個資料庫檔案 {DB_NAME} 嗎?所有資料將會遺失!"):
if self.db.delete_database_file():
messagebox.showinfo("成功", "資料庫檔案已刪除。")
self.load_teacher_data()
self.load_course_data()
if __name__ == '__main__':
app = IntegratedManager()
app.mainloop()
兩個獨立的 Python 程式:
teacher_manager.py:管理MySchoolDB.db中的teacher2(老師資料表)。course_manager.py:管理MySchoolDB.db中的course2(課程資料表),並包含外來鍵 (#老師編號) 的邏輯。
好的,根據您提供的「一對多關聯」圖片(老師資料表和課程資料表)和您的要求,我將把程式碼修改並分為兩個獨立的 Python 程式:
teacher_manager.py:管理MySchoolDB.db中的teacher2(老師資料表)。course_manager.py:管理MySchoolDB.db中的course2(課程資料表),並包含外來鍵 (#老師編號) 的邏輯。
這兩個程式都將包含您要求的所有 6 個功能。
程式一:teacher_manager.py (管理 老師資料表)
這個程式負責管理 teacher2 資料表,該表的欄位為:老師編號 (主鍵)、老師姓名、研究領域。
# teacher_manager.py
import tkinter as tk
from tkinter import messagebox, simpledialog, ttk
import sqlite3
import os
DB_NAME = "MySchoolDB.db"
TABLE_NAME = "teacher2"
# --- 資料庫操作類別 ---
class DatabaseManager:
def __init__(self, db_name=DB_NAME, table_name=TABLE_NAME):
self.db_name = db_name
self.table_name = table_name
def connect(self):
"""建立資料庫連線"""
try:
conn = sqlite3.connect(self.db_name)
return conn
except sqlite3.Error as e:
messagebox.showerror("資料庫錯誤", f"無法連線到資料庫: {e}")
return None
def create_table(self):
"""1. 建立資料表 (MySchoolDB.db 及 teacher2 表)"""
conn = self.connect()
if not conn:
return False
try:
cursor = conn.cursor()
# 建立 teacher2 資料表
cursor.execute(f'''
CREATE TABLE IF NOT EXISTS {self.table_name} (
老師編號 TEXT PRIMARY KEY,
老師姓名 TEXT NOT NULL,
研究領域 TEXT
)
''')
conn.commit()
return True
except sqlite3.Error as e:
messagebox.showerror("資料庫錯誤", f"建立資料表失敗: {e}")
return False
finally:
if conn: conn.close()
def insert_data(self, teacher_id, name, field):
"""2. 新增資料"""
conn = self.connect()
if not conn: return False
try:
cursor = conn.cursor()
cursor.execute(f"INSERT INTO {self.table_name} (老師編號, 老師姓名, 研究領域) VALUES (?, ?, ?)",
(teacher_id, name, field))
conn.commit()
return True
except sqlite3.IntegrityError:
messagebox.showwarning("新增失敗", f"老師編號 {teacher_id} 已存在!")
return False
except sqlite3.Error as e:
messagebox.showerror("資料庫錯誤", f"新增資料失敗: {e}")
conn.rollback()
return False
finally:
if conn: conn.close()
def update_data(self, teacher_id, name, field):
"""3. 更正資料"""
conn = self.connect()
if not conn: return False
try:
cursor = conn.cursor()
cursor.execute(f"UPDATE {self.table_name} SET 老師姓名=?, 研究領域=? WHERE 老師編號=?",
(name, field, teacher_id))
conn.commit()
if cursor.rowcount == 0:
messagebox.showwarning("更正失敗", f"老師編號 {teacher_id} 不存在!")
return False
return True
except sqlite3.Error as e:
messagebox.showerror("資料庫錯誤", f"更正資料失敗: {e}")
conn.rollback()
return False
finally:
if conn: conn.close()
def delete_data(self, teacher_id):
"""4. 刪除資料"""
conn = self.connect()
if not conn: return False
try:
cursor = conn.cursor()
# 刪除老師資料
cursor.execute(f"DELETE FROM {self.table_name} WHERE 老師編號=?", (teacher_id,))
conn.commit()
if cursor.rowcount == 0:
messagebox.showwarning("刪除失敗", f"老師編號 {teacher_id} 不存在!")
return False
return True
except sqlite3.Error as e:
messagebox.showerror("資料庫錯誤", f"刪除資料失敗: {e}")
conn.rollback()
return False
finally:
if conn: conn.close()
def get_all_data(self):
"""5. 顯示所有資料"""
conn = self.connect()
if not conn: return []
try:
cursor = conn.cursor()
cursor.execute(f"SELECT * FROM {self.table_name} ORDER BY 老師編號")
rows = cursor.fetchall()
return rows
except sqlite3.Error as e:
messagebox.showerror("資料庫錯誤", f"查詢資料失敗: {e}")
return []
finally:
if conn: conn.close()
def delete_database_file(self):
"""6. 刪除資料庫檔案"""
if os.path.exists(self.db_name):
try:
os.remove(self.db_name)
return True
except OSError as e:
messagebox.showerror("刪除失敗", f"無法刪除資料庫檔案 {self.db_name}: {e}\n請確認是否有其他程式正在使用此檔案。")
return False
else:
messagebox.showwarning("刪除失敗", f"資料庫檔案 {self.db_name} 不存在。")
return False
# --- Tkinter GUI 應用程式類別 ---
class Application(tk.Tk):
def __init__(self):
super().__init__()
self.title("老師資料管理系統 (teacher2)")
self.db = DatabaseManager()
self.geometry("600x450")
self.create_widgets()
self.load_data_to_treeview()
def create_widgets(self):
# 輸入區
input_frame = tk.LabelFrame(self, text="輸入/查詢區", padx=10, pady=10)
input_frame.pack(fill='x', padx=10, pady=5)
tk.Label(input_frame, text="老師編號:").grid(row=0, column=0, padx=5, pady=2, sticky='w')
self.entry_id = tk.Entry(input_frame)
self.entry_id.grid(row=0, column=1, padx=5, pady=2, sticky='w')
tk.Label(input_frame, text="老師姓名:").grid(row=0, column=2, padx=5, pady=2, sticky='w')
self.entry_name = tk.Entry(input_frame)
self.entry_name.grid(row=0, column=3, padx=5, pady=2, sticky='w')
tk.Label(input_frame, text="研究領域:").grid(row=1, column=0, padx=5, pady=2, sticky='w')
self.entry_field = tk.Entry(input_frame)
self.entry_field.grid(row=1, column=1, padx=5, pady=2, sticky='w')
# 按鈕區塊
button_frame = tk.Frame(self, padx=10, pady=5)
button_frame.pack(fill='x')
buttons = [
("1. 建立資料庫", self.create_db_command),
("2. 新增資料", self.add_data_command),
("3. 更正資料", self.update_data_command),
("4. 刪除資料", self.delete_data_command),
("5. 顯示所有資料", self.load_data_to_treeview),
("6. 刪除資料庫", self.delete_db_command)
]
for i, (text, command) in enumerate(buttons):
btn = tk.Button(button_frame, text=text, command=command, width=12)
btn.grid(row=0, column=i, padx=5, pady=5)
# Treeview 顯示區
self.tree = ttk.Treeview(self, columns=('老師編號', '老師姓名', '研究領域'), show='headings')
self.tree.heading('老師編號', text='老師編號')
self.tree.column('老師編號', width=120, anchor='center')
self.tree.heading('老師姓名', text='老師姓名')
self.tree.column('老師姓名', width=150, anchor='w')
self.tree.heading('研究領域', text='研究領域')
self.tree.column('研究領域', width=200, anchor='w')
self.tree.pack(fill='both', expand=True, padx=10, pady=10)
self.tree.bind('<<TreeviewSelect>>', self.on_select)
def on_select(self, event):
"""點擊 Treeview 項目時,將資料填入輸入框"""
selected_item = self.tree.focus()
if selected_item:
values = self.tree.item(selected_item, 'values')
self.clear_entries()
self.entry_id.insert(0, values[0])
self.entry_name.insert(0, values[1])
self.entry_field.insert(0, values[2])
def clear_entries(self):
"""清空所有輸入框"""
self.entry_id.delete(0, tk.END)
self.entry_name.delete(0, tk.END)
self.entry_field.delete(0, tk.END)
def load_data_to_treeview(self):
"""5. 顯示所有資料"""
for item in self.tree.get_children():
self.tree.delete(item)
data = self.db.get_all_data()
for row in data:
self.tree.insert('', tk.END, values=row)
def create_db_command(self):
"""1. 建立資料庫"""
if self.db.create_table():
messagebox.showinfo("成功", f"資料庫 {DB_NAME} 及其資料表 {TABLE_NAME} 已成功建立或已存在。")
self.load_data_to_treeview()
def add_data_command(self):
"""2. 新增資料"""
teacher_id = self.entry_id.get().strip()
name = self.entry_name.get().strip()
field = self.entry_field.get().strip()
if not teacher_id or not name:
messagebox.showwarning("警告", "老師編號和姓名為必填!")
return
if self.db.insert_data(teacher_id, name, field):
messagebox.showinfo("成功", "資料新增成功!")
self.clear_entries()
self.load_data_to_treeview()
def update_data_command(self):
"""3. 更正資料"""
teacher_id = self.entry_id.get().strip()
name = self.entry_name.get().strip()
field = self.entry_field.get().strip()
if not teacher_id or not name:
messagebox.showwarning("警告", "請輸入要更正的老師編號和新的姓名!")
return
if self.db.update_data(teacher_id, name, field):
messagebox.showinfo("成功", f"老師編號 {teacher_id} 資料更正成功!")
self.clear_entries()
self.load_data_to_treeview()
def delete_data_command(self):
"""4. 刪除資料"""
teacher_id = self.entry_id.get().strip()
if not teacher_id:
messagebox.showwarning("警告", "請輸入要刪除的老師編號!")
return
if messagebox.askyesno("確認刪除", f"確定要刪除老師編號 {teacher_id} 的資料嗎?"):
if self.db.delete_data(teacher_id):
messagebox.showinfo("成功", "資料刪除成功!")
self.clear_entries()
self.load_data_to_treeview()
def delete_db_command(self):
"""6. 刪除資料庫檔案"""
if messagebox.askyesno("確認刪除資料庫", f"**警告:** 確定要刪除整個資料庫檔案 {DB_NAME} 嗎?所有資料將會遺失!"):
if self.db.delete_database_file():
messagebox.showinfo("成功", "資料庫檔案已刪除。")
self.load_data_to_treeview() # 清空顯示
if __name__ == '__main__':
app = Application()
app.mainloop()
程式二:course_manager.py (管理 課程資料表)
這個程式負責管理 course2 資料表,該表的欄位為:課程代號 (主鍵)、課程名稱、學分數、老師編號 (外來鍵)。
注意: 為了實現外來鍵的約束,我們必須在建立連線後啟用它 (PRAGMA foreign_keys = ON;)。
# course_manager.py
import tkinter as tk
from tkinter import messagebox, simpledialog, ttk
import sqlite3
import os
DB_NAME = "MySchoolDB.db"
TABLE_NAME = "course2"
# --- 資料庫操作類別 ---
class DatabaseManager:
def __init__(self, db_name=DB_NAME, table_name=TABLE_NAME):
self.db_name = db_name
self.table_name = table_name
def connect(self):
"""建立資料庫連線並啟用外來鍵約束"""
try:
conn = sqlite3.connect(self.db_name)
# 啟用外來鍵約束
conn.execute("PRAGMA foreign_keys = ON;")
return conn
except sqlite3.Error as e:
messagebox.showerror("資料庫錯誤", f"無法連線到資料庫: {e}")
return None
def create_table(self):
"""1. 建立資料表 (MySchoolDB.db 及 course2 表)"""
conn = self.connect()
if not conn:
return False
try:
cursor = conn.cursor()
# 建立 course2 資料表,包含外來鍵約束
cursor.execute(f'''
CREATE TABLE IF NOT EXISTS {self.table_name} (
課程代號 TEXT PRIMARY KEY,
課程名稱 TEXT NOT NULL,
學分數 INTEGER,
老師編號 TEXT,
FOREIGN KEY (老師編號) REFERENCES teacher2(老師編號) ON DELETE SET NULL ON UPDATE CASCADE
)
''')
conn.commit()
return True
except sqlite3.Error as e:
messagebox.showerror("資料庫錯誤", f"建立資料表失敗: {e}")
return False
finally:
if conn: conn.close()
def insert_data(self, course_id, name, credits, teacher_id):
"""2. 新增資料"""
conn = self.connect()
if not conn: return False
try:
cursor = conn.cursor()
cursor.execute(f"INSERT INTO {self.table_name} (課程代號, 課程名稱, 學分數, 老師編號) VALUES (?, ?, ?, ?)",
(course_id, name, credits, teacher_id))
conn.commit()
return True
except sqlite3.IntegrityError as e:
if "UNIQUE constraint failed" in str(e):
messagebox.showwarning("新增失敗", f"課程代號 {course_id} 已存在!")
elif "foreign key constraint failed" in str(e):
messagebox.showwarning("新增失敗", f"老師編號 {teacher_id} 在 teacher2 表中不存在!")
else:
messagebox.showerror("資料庫錯誤", f"新增資料失敗: {e}")
conn.rollback()
return False
except sqlite3.Error as e:
messagebox.showerror("資料庫錯誤", f"新增資料失敗: {e}")
conn.rollback()
return False
finally:
if conn: conn.close()
def update_data(self, course_id, name, credits, teacher_id):
"""3. 更正資料"""
conn = self.connect()
if not conn: return False
try:
cursor = conn.cursor()
cursor.execute(f"UPDATE {self.table_name} SET 課程名稱=?, 學分數=?, 老師編號=? WHERE 課程代號=?",
(name, credits, teacher_id, course_id))
conn.commit()
if cursor.rowcount == 0:
messagebox.showwarning("更正失敗", f"課程代號 {course_id} 不存在!")
return False
return True
except sqlite3.IntegrityError as e:
if "foreign key constraint failed" in str(e):
messagebox.showwarning("更正失敗", f"老師編號 {teacher_id} 在 teacher2 表中不存在!")
else:
messagebox.showerror("資料庫錯誤", f"更正資料失敗: {e}")
conn.rollback()
return False
except sqlite3.Error as e:
messagebox.showerror("資料庫錯誤", f"更正資料失敗: {e}")
conn.rollback()
return False
finally:
if conn: conn.close()
def delete_data(self, course_id):
"""4. 刪除資料"""
conn = self.connect()
if not conn: return False
try:
cursor = conn.cursor()
cursor.execute(f"DELETE FROM {self.table_name} WHERE 課程代號=?", (course_id,))
conn.commit()
if cursor.rowcount == 0:
messagebox.showwarning("刪除失敗", f"課程代號 {course_id} 不存在!")
return False
return True
except sqlite3.Error as e:
messagebox.showerror("資料庫錯誤", f"刪除資料失敗: {e}")
conn.rollback()
return False
finally:
if conn: conn.close()
def get_all_data(self):
"""5. 顯示所有資料"""
conn = self.connect()
if not conn: return []
try:
cursor = conn.cursor()
# 為了顯示完整的資訊,我們使用 LEFT JOIN 連接 teacher2 表
cursor.execute(f'''
SELECT
c.課程代號, c.課程名稱, c.學分數, c.老師編號, t.老師姓名
FROM
{self.table_name} c
LEFT JOIN
teacher2 t ON c.老師編號 = t.老師編號
ORDER BY c.課程代號
''')
rows = cursor.fetchall()
return rows
except sqlite3.Error as e:
messagebox.showerror("資料庫錯誤", f"查詢資料失敗: {e}")
return []
finally:
if conn: conn.close()
def delete_database_file(self):
"""6. 刪除資料庫檔案"""
if os.path.exists(self.db_name):
try:
os.remove(self.db_name)
return True
except OSError as e:
messagebox.showerror("刪除失敗", f"無法刪除資料庫檔案 {self.db_name}: {e}\n請確認是否有其他程式正在使用此檔案。")
return False
else:
messagebox.showwarning("刪除失敗", f"資料庫檔案 {self.db_name} 不存在。")
return False
# --- Tkinter GUI 應用程式類別 ---
class Application(tk.Tk):
def __init__(self):
super().__init__()
self.title("課程資料管理系統 (course2)")
self.db = DatabaseManager()
self.geometry("800x450")
self.create_widgets()
self.load_data_to_treeview()
def create_widgets(self):
# 輸入區
input_frame = tk.LabelFrame(self, text="輸入/查詢區", padx=10, pady=10)
input_frame.pack(fill='x', padx=10, pady=5)
tk.Label(input_frame, text="課程代號:").grid(row=0, column=0, padx=5, pady=2, sticky='w')
self.entry_course_id = tk.Entry(input_frame)
self.entry_course_id.grid(row=0, column=1, padx=5, pady=2, sticky='w')
tk.Label(input_frame, text="課程名稱:").grid(row=0, column=2, padx=5, pady=2, sticky='w')
self.entry_name = tk.Entry(input_frame)
self.entry_name.grid(row=0, column=3, padx=5, pady=2, sticky='w')
tk.Label(input_frame, text="學分數:").grid(row=1, column=0, padx=5, pady=2, sticky='w')
self.entry_credits = tk.Entry(input_frame)
self.entry_credits.grid(row=1, column=1, padx=5, pady=2, sticky='w')
tk.Label(input_frame, text="老師編號:").grid(row=1, column=2, padx=5, pady=2, sticky='w')
self.entry_teacher_id = tk.Entry(input_frame)
self.entry_teacher_id.grid(row=1, column=3, padx=5, pady=2, sticky='w')
# 按鈕區塊
button_frame = tk.Frame(self, padx=10, pady=5)
button_frame.pack(fill='x')
buttons = [
("1. 建立資料庫", self.create_db_command),
("2. 新增資料", self.add_data_command),
("3. 更正資料", self.update_data_command),
("4. 刪除資料", self.delete_data_command),
("5. 顯示所有資料", self.load_data_to_treeview),
("6. 刪除資料庫", self.delete_db_command)
]
for i, (text, command) in enumerate(buttons):
btn = tk.Button(button_frame, text=text, command=command, width=12)
btn.grid(row=0, column=i, padx=5, pady=5)
# Treeview 顯示區
self.tree = ttk.Treeview(self, columns=('代號', '名稱', '學分', '老師編號', '老師姓名'), show='headings')
self.tree.heading('代號', text='課程代號')
self.tree.column('代號', width=100, anchor='center')
self.tree.heading('名稱', text='課程名稱')
self.tree.column('名稱', width=180, anchor='w')
self.tree.heading('學分', text='學分數')
self.tree.column('學分', width=80, anchor='center')
self.tree.heading('老師編號', text='老師編號')
self.tree.column('老師編號', width=120, anchor='center')
self.tree.heading('老師姓名', text='老師姓名')
self.tree.column('老師姓名', width=180, anchor='w')
self.tree.pack(fill='both', expand=True, padx=10, pady=10)
self.tree.bind('<<TreeviewSelect>>', self.on_select)
def on_select(self, event):
"""點擊 Treeview 項目時,將資料填入輸入框"""
selected_item = self.tree.focus()
if selected_item:
values = self.tree.item(selected_item, 'values')
self.clear_entries()
self.entry_course_id.insert(0, values[0]) # 課程代號
self.entry_name.insert(0, values[1]) # 課程名稱
self.entry_credits.insert(0, values[2]) # 學分數
self.entry_teacher_id.insert(0, values[3])# 老師編號
def clear_entries(self):
"""清空所有輸入框"""
self.entry_course_id.delete(0, tk.END)
self.entry_name.delete(0, tk.END)
self.entry_credits.delete(0, tk.END)
self.entry_teacher_id.delete(0, tk.END)
def load_data_to_treeview(self):
"""5. 顯示所有資料"""
for item in self.tree.get_children():
self.tree.delete(item)
data = self.db.get_all_data()
# 處理 'None' 顯示
processed_data = []
for row in data:
# 將 None 替換為空字串或 'N/A' 以便顯示
processed_data.append(tuple('' if x is None else x for x in row))
for row in processed_data:
self.tree.insert('', tk.END, values=row)
def create_db_command(self):
"""1. 建立資料庫"""
if self.db.create_table():
messagebox.showinfo("成功", f"資料庫 {DB_NAME} 及其資料表 {TABLE_NAME} 已成功建立或已存在。")
self.load_data_to_treeview()
def add_data_command(self):
"""2. 新增資料"""
course_id = self.entry_course_id.get().strip()
name = self.entry_name.get().strip()
credits_str = self.entry_credits.get().strip()
teacher_id = self.entry_teacher_id.get().strip()
if not course_id or not name or not credits_str:
messagebox.showwarning("警告", "課程代號、課程名稱和學分數為必填!")
return
try:
credits = int(credits_str)
except ValueError:
messagebox.showwarning("警告", "學分數必須是數字!")
return
if self.db.insert_data(course_id, name, credits, teacher_id):
messagebox.showinfo("成功", "資料新增成功!")
self.clear_entries()
self.load_data_to_treeview()
def update_data_command(self):
"""3. 更正資料"""
course_id = self.entry_course_id.get().strip()
name = self.entry_name.get().strip()
credits_str = self.entry_credits.get().strip()
teacher_id = self.entry_teacher_id.get().strip()
if not course_id or not name or not credits_str:
messagebox.showwarning("警告", "請輸入要更正的課程代號和新的課程名稱!")
return
try:
credits = int(credits_str)
except ValueError:
messagebox.showwarning("警告", "學分數必須是數字!")
return
if self.db.update_data(course_id, name, credits, teacher_id):
messagebox.showinfo("成功", f"課程代號 {course_id} 資料更正成功!")
self.clear_entries()
self.load_data_to_treeview()
def delete_data_command(self):
"""4. 刪除資料"""
course_id = self.entry_course_id.get().strip()
if not course_id:
messagebox.showwarning("警告", "請輸入要刪除的課程代號!")
return
if messagebox.askyesno("確認刪除", f"確定要刪除課程代號 {course_id} 的資料嗎?"):
if self.db.delete_data(course_id):
messagebox.showinfo("成功", "資料刪除成功!")
self.clear_entries()
self.load_data_to_treeview()
def delete_db_command(self):
"""6. 刪除資料庫檔案"""
if messagebox.askyesno("確認刪除資料庫", f"**警告:** 確定要刪除整個資料庫檔案 {DB_NAME} 嗎?所有資料將會遺失!"):
if self.db.delete_database_file():
messagebox.showinfo("成功", "資料庫檔案已刪除。")
self.load_data_to_treeview()
if __name__ == '__main__':
app = Application()
app.mainloop()








沒有留言:
張貼留言