Kertas Jawapan
Anonymous
plain_text
02/24/2026 12:50 AM
13.9 KB
28
Indexable
import tkinter as tk
from tkinter import ttk
TOTAL_QUESTIONS = 100
OPTIONS = ["A", "B", "C", "D"]
COLOR_NORMAL = "#e8e8e8"
COLOR_SELECTED_ANSWER = "#43A047"
COLOR_SELECTED_SCHEME = "#FB8C00"
COLOR_CORRECT = "#1565C0"
COLOR_WRONG = "#C62828"
BTN_W = 5
BTN_PAD = 3
class AnswerSheetApp(tk.Tk):
def __init__(self):
super().__init__()
self.title("Kertas Jawapan & Skema")
self.resizable(True, True)
self.answers = {}
self.scheme = {}
self.answer_buttons = {}
self.scheme_buttons = {}
self.result_labels = {}
self._build_ui()
self._fit_window()
# ──────────────────────────────────────── BUILD UI
def _build_ui(self):
style = ttk.Style(self)
style.configure("TNotebook.Tab",
font=("Segoe UI", 11, "bold"), padding=[14, 6])
# Footer packed FIRST (bottom), then notebook fills the rest
self._build_footer()
self.nb = ttk.Notebook(self)
self.nb.pack(fill="both", expand=True, padx=6, pady=(6, 0))
# ── TAB JAWAPAN
tab_ans = ttk.Frame(self.nb)
self.nb.add(tab_ans, text=" 📝 Jawapan ")
self.ans_canvas, self.ans_inner = self._make_scrollable(tab_ans)
ttk.Label(self.ans_inner,
text="Pilih jawapan anda untuk setiap soalan",
font=("Segoe UI", 10, "italic"), foreground="#666"
).pack(pady=(8, 4))
for q in range(1, TOTAL_QUESTIONS + 1):
self._make_answer_row(q)
# ── TAB SKEMA
tab_sch = ttk.Frame(self.nb)
self.nb.add(tab_sch, text=" 🗝️ Skema Jawapan ")
self.sch_canvas, self.sch_inner = self._make_scrollable(tab_sch)
ttk.Label(self.sch_inner,
text="Masukkan skema / kunci jawapan",
font=("Segoe UI", 10, "italic"), foreground="#666"
).pack(pady=(8, 4))
for q in range(1, TOTAL_QUESTIONS + 1):
self._make_scheme_row(q)
# ──────────────────────────────────────── FOOTER (packed to bottom FIRST)
def _build_footer(self):
ttk.Separator(self, orient="horizontal").pack(side="bottom", fill="x")
self.footer = tk.Frame(self, bg="#f0f4f0", pady=10)
self.footer.pack(side="bottom", fill="x")
# Row 1: Semak button — centered
row1 = tk.Frame(self.footer, bg="#f0f4f0")
row1.pack(fill="x")
self.check_btn = tk.Button(
row1,
text="✔ Semak Jawapan",
command=self._check,
bg="#43A047", fg="white",
font=("Segoe UI", 11, "bold"),
relief="flat", padx=20, pady=7,
cursor="hand2",
activebackground="#388E3C", activeforeground="white"
)
self.check_btn.pack(anchor="center")
# Row 2: Betul & Salah — centered
row2 = tk.Frame(self.footer, bg="#f0f4f0")
row2.pack(fill="x", pady=(6, 0))
self.lbl_correct = tk.Label(
row2, text="✔ Betul: —",
font=("Segoe UI", 10, "bold"),
fg=COLOR_CORRECT, bg="#f0f4f0"
)
self.lbl_correct.pack(side="left", expand=True)
self.lbl_wrong = tk.Label(
row2, text="✘ Salah: —",
font=("Segoe UI", 10, "bold"),
fg=COLOR_WRONG, bg="#f0f4f0"
)
self.lbl_wrong.pack(side="left", expand=True)
# Row 3: Markah badge — centered
row3 = tk.Frame(self.footer, bg="#f0f4f0")
row3.pack(fill="x", pady=(4, 0))
tk.Label(row3, text="Markah:",
font=("Segoe UI", 10, "bold"),
fg="#333", bg="#f0f4f0"
).pack(side="left", expand=True, anchor="e", padx=(0, 6))
self.lbl_score = tk.Label(
row3, text="—",
font=("Segoe UI", 11, "bold"),
fg="white", bg="#9e9e9e",
padx=12, pady=4
)
self.lbl_score.pack(side="left", expand=True, anchor="w")
# ──────────────────────────────────────── SCROLLABLE
def _make_scrollable(self, parent):
outer = ttk.Frame(parent)
outer.pack(fill="both", expand=True)
canvas = tk.Canvas(outer, highlightthickness=0)
sb = ttk.Scrollbar(outer, orient="vertical", command=canvas.yview)
inner = ttk.Frame(canvas)
inner.bind("<Configure>",
lambda e: canvas.configure(
scrollregion=canvas.bbox("all")))
canvas.create_window((0, 0), window=inner, anchor="nw")
canvas.configure(yscrollcommand=sb.set)
canvas.pack(side="left", fill="both", expand=True)
sb.pack(side="right", fill="y")
for w in (canvas, inner):
w.bind("<MouseWheel>",
lambda e, c=canvas: c.yview_scroll(
int(-1 * (e.delta / 120)), "units"))
return canvas, inner
def _bind_scroll(self, widget, canvas):
widget.bind("<MouseWheel>",
lambda e, c=canvas: c.yview_scroll(
int(-1 * (e.delta / 120)), "units"))
for child in widget.winfo_children():
self._bind_scroll(child, canvas)
# ──────────────────────────────────────── ROWS
def _make_answer_row(self, q):
row = ttk.Frame(self.ans_inner)
row.pack(fill="x", padx=16, pady=2)
ttk.Label(row, text=f"{q:>3}.", width=4,
font=("Segoe UI", 10)).pack(side="left")
self.answers[q] = None
self.answer_buttons[q] = {}
for opt in OPTIONS:
btn = tk.Button(
row, text=opt, width=BTN_W, bg=COLOR_NORMAL,
font=("Segoe UI", 9, "bold"), relief="flat", bd=0,
command=lambda o=opt, n=q: self._sel_ans(n, o)
)
btn.pack(side="left", padx=BTN_PAD)
self.answer_buttons[q][opt] = btn
lbl = ttk.Label(row, width=9, font=("Segoe UI", 9))
lbl.pack(side="left", padx=8)
self.result_labels[q] = lbl
self._bind_scroll(row, self.ans_canvas)
def _make_scheme_row(self, q):
row = ttk.Frame(self.sch_inner)
row.pack(fill="x", padx=16, pady=2)
ttk.Label(row, text=f"{q:>3}.", width=4,
font=("Segoe UI", 10)).pack(side="left")
self.scheme[q] = None
self.scheme_buttons[q] = {}
for opt in OPTIONS:
btn = tk.Button(
row, text=opt, width=BTN_W, bg=COLOR_NORMAL,
font=("Segoe UI", 9, "bold"), relief="flat", bd=0,
command=lambda o=opt, n=q: self._sel_sch(n, o)
)
btn.pack(side="left", padx=BTN_PAD)
self.scheme_buttons[q][opt] = btn
self._bind_scroll(row, self.sch_canvas)
# ──────────────────────────────────────── SELECTION
def _sel_ans(self, q, opt):
# klik jawapan sama → clear; klik lain → pilih
if self.answers[q] == opt:
self.answer_buttons[q][opt].config(bg=COLOR_NORMAL, fg="black")
self.answers[q] = None
else:
for b in self.answer_buttons[q].values():
b.config(bg=COLOR_NORMAL, fg="black")
self.answer_buttons[q][opt].config(bg=COLOR_SELECTED_ANSWER, fg="white")
self.answers[q] = opt
def _sel_sch(self, q, opt):
# klik skema sama → clear; klik lain → pilih
if self.scheme[q] == opt:
self.scheme_buttons[q][opt].config(bg=COLOR_NORMAL, fg="black")
self.scheme[q] = None
else:
for b in self.scheme_buttons[q].values():
b.config(bg=COLOR_NORMAL, fg="black")
self.scheme_buttons[q][opt].config(bg=COLOR_SELECTED_SCHEME, fg="white")
self.scheme[q] = opt
# ──────────────────────────────────────── CHECK
def _check(self):
correct = wrong = skipped = 0
for q in range(1, TOTAL_QUESTIONS + 1):
ua = self.answers.get(q)
sa = self.scheme.get(q)
if not ua or not sa:
self.result_labels[q].config(text="—", foreground="#aaa")
skipped += 1
elif ua == sa:
self.result_labels[q].config(text="✔ Betul", foreground=COLOR_CORRECT)
correct += 1
else:
self.result_labels[q].config(text="✘ Salah", foreground=COLOR_WRONG)
wrong += 1
pct = (correct / TOTAL_QUESTIONS) * 100
self.lbl_correct.config(text=f"✔ Betul: {correct}")
self.lbl_wrong.config(text=f"✘ Salah: {wrong}")
self.lbl_score.config(text=f" {pct:.1f}% ")
if pct >= 80:
badge_bg = "#1565C0"
elif pct >= 60:
badge_bg = "#F57F17"
else:
badge_bg = "#C62828"
self.lbl_score.config(bg=badge_bg)
# ──────────────────────────────────────── FIT WINDOW
def _fit_window(self):
self.update_idletasks()
self.ans_inner.update_idletasks()
inner_w = self.ans_inner.winfo_reqwidth()
win_w = inner_w + 18 + 30
screen_h = self.winfo_screenheight()
max_h = int(screen_h * 0.60)
content_h = self.ans_inner.winfo_reqheight()
footer_h = self.footer.winfo_reqheight() + 10
tab_bar_h = 48
canvas_h = min(content_h, max_h - tab_bar_h - footer_h)
self.ans_canvas.config(width=inner_w, height=canvas_h)
self.sch_canvas.config(width=inner_w, height=canvas_h)
total_h = canvas_h + tab_bar_h + footer_h + 18
self.geometry(f"{win_w}x{total_h}")
if __name__ == "__main__":
AnswerSheetApp().mainloop()Editor is loading...
Leave a Comment