diff --git a/40 Simple GUI Quiz/eiffel_tower.png b/40 Simple GUI Quiz/eiffel_tower.png new file mode 100644 index 0000000..89404d7 Binary files /dev/null and b/40 Simple GUI Quiz/eiffel_tower.png differ diff --git a/40 Simple GUI Quiz/landmark_icon.png b/40 Simple GUI Quiz/landmark_icon.png new file mode 100644 index 0000000..80736e4 Binary files /dev/null and b/40 Simple GUI Quiz/landmark_icon.png differ diff --git a/40 Simple GUI Quiz/landmark_quiz.py b/40 Simple GUI Quiz/landmark_quiz.py new file mode 100644 index 0000000..a652b78 --- /dev/null +++ b/40 Simple GUI Quiz/landmark_quiz.py @@ -0,0 +1,69 @@ +import tkinter as tk +from tkinter import messagebox +from pathlib import Path +from PIL import Image, ImageTk + +ROOT = Path(__file__).resolve().parent + +root = tk.Tk() +root.title("Landmark Quiz") +root.geometry("600x560") +root.resizable(False, False) + +# Load and resize landmark image +image = Image.open(ROOT / "eiffel_tower.png") +image = image.resize((300, 300)) + +landmark = ImageTk.PhotoImage(image) + +# Window icon +icon = tk.PhotoImage(file=ROOT / "landmark_icon.png") +root.iconphoto(False, icon) + +title = tk.Label(root, text="Famous Landmark Quiz", font=("Arial", 20, "bold")) +title.grid(row=0, column=0, columnspan=2, pady=(15, 10)) + +picture = tk.Label(root, image=landmark) +picture.grid(row=1, column=0, columnspan=2, padx=20, pady=10) + +question = tk.Label( + root, + text="Which city is this landmark situated in?", + font=("Arial", 13) +) +question.grid(row=2, column=0, columnspan=2, pady=8) + +entry = tk.Entry(root, width=30, font=("Arial", 12)) +entry.grid(row=3, column=0, columnspan=2, pady=8) +entry.focus() + +result = tk.Label(root, text="", font=("Arial", 12)) +result.grid(row=5, column=0, columnspan=2, pady=10) + +def check_answer(): + answer = entry.get().strip().lower() + + if answer == "paris": + result.config(text="Correct! The Eiffel Tower is in Paris.") + messagebox.showinfo("Correct", "Well done! Paris is the correct answer.") + entry.delete(0, tk.END) + else: + result.config(text="That answer is incorrect. Try again.") + messagebox.showerror("Try again", "Incorrect. Please try again.") + entry.select_range(0, tk.END) + entry.focus() + +def clear_answer(): + entry.delete(0, tk.END) + result.config(text="") + entry.focus() + +check_button = tk.Button(root, text="Check answer", command=check_answer, width=16) +check_button.grid(row=4, column=0, padx=10, pady=5) + +clear_button = tk.Button(root, text="Clear", command=clear_answer, width=16) +clear_button.grid(row=4, column=1, padx=10, pady=5) + +root.bind("", lambda event: check_answer()) + +root.mainloop() diff --git a/41 Complete the update feature/address_book.db b/41 Complete the update feature/address_book.db new file mode 100644 index 0000000..83c094e Binary files /dev/null and b/41 Complete the update feature/address_book.db differ diff --git a/41 Complete the update feature/address_book.py b/41 Complete the update feature/address_book.py new file mode 100644 index 0000000..41d9170 --- /dev/null +++ b/41 Complete the update feature/address_book.py @@ -0,0 +1,215 @@ +import tkinter as tk +from tkinter import ttk, messagebox +import sqlite3 +from pathlib import Path + +ROOT = Path(__file__).resolve().parent +DB = ROOT / "address_book.db" + +root = tk.Tk() +root.title("Address Book") +root.geometry("620x620") +root.resizable(False, False) + +def connect(): + return sqlite3.connect(DB) + +def clear_entries(): + f_name.delete(0, tk.END) + s_name.delete(0, tk.END) + mob.delete(0, tk.END) + email.delete(0, tk.END) + +def submit(): + values = ( + f_name.get().strip(), + s_name.get().strip(), + mob.get().strip(), + email.get().strip(), + ) + + if not all(values): + messagebox.showwarning("Missing data", "Please fill in all four fields.") + return + + conn = connect() + c = conn.cursor() + c.execute( + """ + INSERT INTO address_book (f_name, s_name, mob, email) + VALUES (?, ?, ?, ?) + """, + values, + ) + conn.commit() + conn.close() + + clear_entries() + search() + +def search(): + conn = connect() + c = conn.cursor() + c.execute(""" + SELECT f_name, s_name, mob, email, oid + FROM address_book + ORDER BY oid + """) + records = c.fetchall() + conn.close() + + output.delete("1.0", tk.END) + + if not records: + output.insert(tk.END, "No records found.") + return + + for record in records: + output.insert( + tk.END, + f"OID: {record[4]} | " + f"{record[0]} {record[1]} | " + f"Mobile: {record[2]} | " + f"Email: {record[3]}\n" + ) + +def delete_rec(): + oid = id_no.get().strip() + + if not oid.isdigit(): + messagebox.showwarning("Invalid OID", "Enter a valid numeric OID.") + return + + conn = connect() + c = conn.cursor() + c.execute("DELETE FROM address_book WHERE oid = ?", (int(oid),)) + deleted = c.rowcount + conn.commit() + conn.close() + + id_no.delete(0, tk.END) + + if deleted: + messagebox.showinfo("Deleted", f"Record {oid} was deleted.") + else: + messagebox.showwarning("Not found", f"No record with OID {oid} exists.") + + search() + +def update_rec(): + oid = id_no.get().strip() + column = update_column.get() + value = update_value.get().strip() + + allowed_columns = { + "First Name": "f_name", + "Surname": "s_name", + "Mobile": "mob", + "Email": "email", + } + + if not oid.isdigit(): + messagebox.showwarning("Invalid OID", "Enter a valid numeric OID.") + return + + if not value: + messagebox.showwarning("Missing value", "Enter the new value.") + return + + # The column name cannot be passed as a normal SQL parameter, + # so only a value from this fixed whitelist is allowed. + db_column = allowed_columns[column] + + conn = connect() + c = conn.cursor() + + sql = "UPDATE address_book SET {} = ? WHERE oid = ?".format(db_column) + c.execute(sql, (value, int(oid))) + updated = c.rowcount + + conn.commit() + conn.close() + + if updated: + messagebox.showinfo( + "Updated", + f"Record {oid} was updated successfully." + ) + id_no.delete(0, tk.END) + update_value.delete(0, tk.END) + search() + else: + messagebox.showwarning( + "Not found", + f"No record with OID {oid} exists." + ) + +# Create database/table if it does not already exist. +conn = connect() +c = conn.cursor() +c.execute(""" +CREATE TABLE IF NOT EXISTS address_book ( + f_name TEXT, + s_name TEXT, + mob TEXT, + email TEXT +) +""") +conn.commit() +conn.close() + +# ---------- GUI ---------- +tk.Label(root, text="First Name").grid(row=0, column=0, sticky="e", padx=8, pady=6) +f_name = tk.Entry(root, width=35) +f_name.grid(row=0, column=1, padx=8, pady=6) + +tk.Label(root, text="Surname").grid(row=1, column=0, sticky="e", padx=8, pady=6) +s_name = tk.Entry(root, width=35) +s_name.grid(row=1, column=1, padx=8, pady=6) + +tk.Label(root, text="Mobile").grid(row=2, column=0, sticky="e", padx=8, pady=6) +mob = tk.Entry(root, width=35) +mob.grid(row=2, column=1, padx=8, pady=6) + +tk.Label(root, text="Email").grid(row=3, column=0, sticky="e", padx=8, pady=6) +email = tk.Entry(root, width=35) +email.grid(row=3, column=1, padx=8, pady=6) + +tk.Button(root, text="Save", command=submit, width=14).grid( + row=4, column=0, padx=8, pady=10 +) +tk.Button(root, text="Display all", command=search, width=14).grid( + row=4, column=1, sticky="w", padx=8, pady=10 +) + +tk.Label(root, text="Enter OID").grid(row=5, column=0, sticky="e", padx=8, pady=6) +id_no = tk.Entry(root, width=35) +id_no.grid(row=5, column=1, padx=8, pady=6) + +tk.Button(root, text="Delete", command=delete_rec, width=14).grid( + row=6, column=0, padx=8, pady=8 +) + +tk.Label(root, text="Update field").grid(row=7, column=0, sticky="e", padx=8, pady=6) +update_column = ttk.Combobox( + root, + values=("First Name", "Surname", "Mobile", "Email"), + state="readonly", + width=32, +) +update_column.current(0) +update_column.grid(row=7, column=1, padx=8, pady=6) + +tk.Label(root, text="New value").grid(row=8, column=0, sticky="e", padx=8, pady=6) +update_value = tk.Entry(root, width=35) +update_value.grid(row=8, column=1, padx=8, pady=6) + +tk.Button(root, text="Update", command=update_rec, width=14).grid( + row=9, column=0, columnspan=2, pady=8 +) + +output = tk.Text(root, width=72, height=13, wrap="word") +output.grid(row=10, column=0, columnspan=2, padx=10, pady=12) + +search() +root.mainloop()