Python Programming
Assignment 5
Due not later than 11:59 PM on Sunday
On my honor as a student, I have neither given nor received any unauthorized aid on this
assignment/exam.
Program 1. Read and Write From a File
Question 1a
#!/usr/bin/env python3
# Q1a — Create [Link] (<5 names) and [Link] (5–7 names)
# Imports — GUI toolkit, dialogs, filesystem, typing
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from pathlib import Path
from typing import List
# Variables — fixed save folder + default file paths
SCRIPT_DIR = Path(__file__).[Link]()
F1 = SCRIPT_DIR / "[Link]"
F2 = SCRIPT_DIR / "[Link]"
# Variables — starter lists honoring the size rules
SEED_1: List[str] = ["Maya", "Ethan", "Ivy", "Leo"] #
4 (<5)
SEED_2: List[str] = ["Aria", "Noah", "Liam", "Olivia", "Zoe"] #
5 (5–7)
# Variables — theme
CLR_BG = "#083b2b"
CLR_PANEL = "#0b4735"
CLR_TEXT = "#e6fffb"
CLR_HINT = "#9ad9d0"
CLR_ACCENT = "#22d3ee"
CLR_GO = "#fb923c"
# Function — split multiline text into trimmed, non-empty names
def collect_names(text_widget: [Link]) -> List[str]:
raw = text_widget.get("1.0", "end").splitlines()
return [[Link]() for s in raw if [Link]()]
# Function — enforce file size rules for the two lists
def valid_counts(n1: List[str], n2: List[str]) -> bool:
return (len(n1) < 5) and (5 <= len(n2) <= 7)
# Function — write a list of lines to a file (one per line, trailing
newline)
def write_lines(path: Path, lines: List[str], overwrite: bool = True) ->
None:
if overwrite or not [Link]():
path.write_text("\n".join(lines) + "\n", encoding="utf-8")
# Function — read a file or return a friendly hint string
def read_or_hint(path: Path) -> str:
return path.read_text(encoding="utf-8") if [Link]() else
f"{[Link]} not found."
# Class — professional GUI for composing and saving friends1/2 files
class FriendsCreatorPro([Link]):
# Build window, menus, widgets, and styles
def __init__(self) -> None:
super().__init__()
[Link]("Q1a — Friends File Creator ")
[Link]("980x580")
[Link](bg=CLR_BG)
# ttk theme + styles
s = [Link](self)
try:
s.theme_use("clam")
except [Link]:
pass
[Link](".", background=CLR_BG, foreground=CLR_TEXT)
[Link]("TFrame", background=CLR_BG)
[Link]("TLabel", background=CLR_BG, foreground=CLR_TEXT)
[Link]("[Link]", background=CLR_BG,
foreground=CLR_HINT)
[Link]("[Link]", font=("Segoe UI", 15, "bold"),
foreground=CLR_ACCENT, background=CLR_BG)
[Link]("[Link]", padding=(12, 7), font=("Segoe UI", 10,
"bold"))
[Link]("[Link]",
background=[("!disabled", CLR_GO)],
foreground=[("!disabled", "#1b1208")])
[Link]("[Link]", padding=(10, 6))
[Link]("[Link]",
background=[("!disabled", CLR_ACCENT)],
foreground=[("!disabled", "#062028")])
# Menu bar (File: Save, Save As Folder…, Reset, Quit)
m = [Link](self)
fm = [Link](m, tearoff=False)
fm.add_command(label="Save (Overwrite)",
command=self.save_overwrite)
fm.add_command(label="Save As Folder…",
command=self.save_as_folder)
fm.add_separator()
fm.add_command(label="Reset", command=self.reset_ui)
fm.add_separator()
fm.add_command(label="Quit", command=[Link])
m.add_cascade(label="File", menu=fm)
[Link](menu=m)
# Header + help
[Link](self, text="Create two files of names (validated
live)", style="[Link]")\
.pack(anchor="w", padx=16, pady=(16, 6))
[Link](self,
text="[Link]: fewer than 5 names • [Link]:
5–7 names",
style="[Link]").pack(anchor="w", padx=16, pady=(0,
10))
# Main panels (side-by-side editors)
wrap = [Link](self); [Link](fill="both", expand=True,
padx=16, pady=8)
left = [Link](wrap); [Link](side="left", fill="both",
expand=True, padx=(0, 8))
right = [Link](wrap); [Link](side="left", fill="both",
expand=True, padx=(8, 0))
# Left editor — [Link]
[Link](left, text="[Link] (target: < 5
names)").pack(anchor="w")
self.t1 = [Link](left, height=13, wrap="none", relief="flat",
bg=CLR_PANEL, fg=CLR_TEXT,
insertbackground=CLR_TEXT)
[Link](fill="both", expand=True, pady=(4, 0))
[Link]("1.0", "\n".join(SEED_1))
self.c1 = [Link](left, text="Count: 0", style="[Link]")
[Link](anchor="w", pady=(4, 0))
# Right editor — [Link]
[Link](right, text="[Link] (target: 5–7
names)").pack(anchor="w")
self.t2 = [Link](right, height=13, wrap="none", relief="flat",
bg=CLR_PANEL, fg=CLR_TEXT,
insertbackground=CLR_TEXT)
[Link](fill="both", expand=True, pady=(4, 0))
[Link]("1.0", "\n".join(SEED_2))
self.c2 = [Link](right, text="Count: 0", style="[Link]")
[Link](anchor="w", pady=(4, 0))
# Action bar — Validate, Save, Preview, Demo
bar = [Link](self); [Link](fill="x", padx=16, pady=(8, 10))
[Link](bar, text="Validate", style="[Link]",
command=self.validate_rules)\
.pack(side="left")
[Link](bar, text="Save (Overwrite)", style="[Link]",
command=self.save_overwrite)\
.pack(side="left", padx=8)
[Link](bar, text="Preview", style="[Link]",
command=[Link])\
.pack(side="left", padx=8)
[Link](bar, text="Fill Demo", style="[Link]",
command=self.fill_demo)\
.pack(side="left", padx=8)
# Output viewer — shows what’s on disk
[Link](self, text="Preview (on disk):",
style="[Link]").pack(anchor="w", padx=16)
[Link] = [Link](self, height=10, wrap="none",
relief="flat",
bg=CLR_PANEL, fg=CLR_TEXT,
insertbackground=CLR_TEXT)
[Link](fill="both", expand=False, padx=16, pady=(4,
12))
# Status bar — save path + validation status
[Link] = [Link](value=f"Save folder: {SCRIPT_DIR}")
[Link](self, textvariable=[Link], style="[Link]")\
.pack(anchor="w", padx=16, pady=(0, 4))
# Live counters (update on keystroke)
[Link]("<<Modified>>", lambda _e: self._update_counts())
[Link]("<<Modified>>", lambda _e: self._update_counts())
self._update_counts()
[Link]()
# Update name counters and modified flags
def _update_counts(self) -> None:
if self.t1.edit_modified():
[Link](text=f"Count: {len(collect_names(self.t1))}")
self.t1.edit_modified(False)
if self.t2.edit_modified():
[Link](text=f"Count: {len(collect_names(self.t2))}")
self.t2.edit_modified(False)
# Validate rules (<5 and 5–7) and notify user
def validate_rules(self) -> None:
n1 = collect_names(self.t1)
n2 = collect_names(self.t2)
[Link](text=f"Count: {len(n1)}")
[Link](text=f"Count: {len(n2)}")
ok = valid_counts(n1, n2)
[Link]("Validation: OK" if ok else "Validation: FAILED
(check counts)")
[Link]("Validation", "Rules satisfied." if ok else
"friends1: < 5 names\nfriends2: between 5 and
7.")
# Save to the script folder (overwrite both files)
def save_overwrite(self) -> None:
try:
n1 = collect_names(self.t1)
n2 = collect_names(self.t2)
if not valid_counts(n1, n2):
raise ValueError("Rule mismatch: friends1 < 5; friends2 =
5–7.")
write_lines(F1, n1, overwrite=True)
write_lines(F2, n2, overwrite=True)
[Link](f"Saved: {[Link]}, {[Link]} →
{SCRIPT_DIR}")
[Link]()
except Exception as e:
[Link]("Save Error", str(e))
# Choose a folder and save there
def save_as_folder(self) -> None:
try:
folder = [Link](title="Choose folder to save
files",
initialdir=SCRIPT_DIR)
if not folder:
return
out = Path(folder)
n1 = collect_names(self.t1)
n2 = collect_names(self.t2)
if not valid_counts(n1, n2):
raise ValueError("Rule mismatch: friends1 < 5; friends2 =
5–7.")
write_lines(out / "[Link]", n1, overwrite=True)
write_lines(out / "[Link]", n2, overwrite=True)
# mirror so 1b finds them without browsing
write_lines(F1, n1, overwrite=True)
write_lines(F2, n2, overwrite=True)
[Link](f"Saved to: {out}")
[Link]()
except Exception as e:
[Link]("Save Error", str(e))
# Show both files from disk in the viewer
def preview(self) -> None:
[Link]("1.0", "end")
[Link]("end", f"--- {[Link]} ---\
n{read_or_hint(F1)}\n")
[Link]("end", f"--- {[Link]} ---\
n{read_or_hint(F2)}\n")
# Refill editors with demo data and refresh counters
def fill_demo(self) -> None:
[Link]("1.0", "end"); [Link]("1.0", "\
n".join(SEED_1))
[Link]("1.0", "end"); [Link]("1.0", "\
n".join(SEED_2))
self._update_counts()
# Reset both editors and viewer to clean state
def reset_ui(self) -> None:
[Link]("1.0", "end")
[Link]("1.0", "end")
[Link]("1.0", "end")
[Link](f"Save folder: {SCRIPT_DIR}")
self._update_counts()
# Entrypoint — launch the professional creator window
if __name__ == "__main__":
app = FriendsCreatorPro()
[Link]()
Results
Question1b
#!/usr/bin/env python3
# Q1b — Open [Link]/[Link] and show only the first five lines
# Imports — GUI, dialogs, filesystem, OS helpers
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from pathlib import Path
import platform, subprocess, os
# Variables — prefer script directory; default filename
SCRIPT_DIR = Path(__file__).[Link]() if "__file__" in globals()
else [Link]()
DEFAULT_NAME = "[Link]"
# Variables
CLR_BG = "#d8f6e3"
CLR_PANEL = "#f0fff7"
CLR_TEXT = "#0f2a1f"
CLR_HINT = "#4c6f63"
CLR_ACCENT = "#22d3ee"
CLR_GO = "#fb923c"
CLR_HILITE = "#e0f2fe"
# Function — add .txt if user omits an extension
def normalize_name(name: str) -> str:
n = [Link]()
return n if Path(n).suffix else f"{n}.txt"
# Function — resolve user input against CWD and script folder
def resolve_path(raw: str) -> Path:
n = normalize_name(raw) if raw else DEFAULT_NAME
candidates = [Path(n), [Link]() / n, SCRIPT_DIR / n]
for p in candidates:
if [Link]():
return [Link]()
return Path(n)
# Function — read up to the first five lines; entire file if <5 lines
def read_first_five(p: Path) -> tuple[str, int]:
if not [Link]():
raise FileNotFoundError(f"{[Link]} not found.")
lines = p.read_text(encoding="utf-8").splitlines()
take = min(5, len(lines))
return "\n".join(lines[:take]) + ("\n" if take else ""), len(lines)
# Function — read full text for the “Show Full” action
def read_full(p: Path) -> tuple[str, int]:
if not [Link]():
raise FileNotFoundError(f"{[Link]} not found.")
txt = p.read_text(encoding="utf-8")
return txt, len([Link]())
# Function — open the file’s folder in Files
def reveal_in_folder(p: Path) -> None:
folder = (p if p.is_dir() else [Link])
if [Link]() == "Windows":
[Link](folder) # type: ignore[attr-defined]
elif [Link]() == "Darwin":
[Link](["open", folder])
else:
[Link](["xdg-open", folder])
# Class — professional light-green GUI to preview first five lines
class FirstFivePro([Link]):
# Build window, theme, menu, inputs, and viewer
def __init__(self) -> None:
super().__init__()
[Link]("Q1b — First Five Preview ")
[Link]("980x560")
[Link](bg=CLR_BG)
# ttk theme
s = [Link](self)
try: s.theme_use("clam")
except [Link]: pass
[Link](".", background=CLR_BG, foreground=CLR_TEXT)
[Link]("TFrame", background=CLR_BG)
[Link]("TLabel", background=CLR_BG, foreground=CLR_TEXT)
[Link]("[Link]", background=CLR_BG,
foreground=CLR_HINT)
[Link]("[Link]", font=("Segoe UI", 15, "bold"),
foreground=CLR_ACCENT)
[Link]("[Link]", padding=(10,6), font=("Segoe
UI",10,"bold"))
[Link]("[Link]", background=[("!disabled", CLR_GO)],
foreground=[("!disabled", "#1b1208")])
[Link]("[Link]", padding=(9,5))
[Link]("[Link]", background=[("!disabled", CLR_ACCENT)],
foreground=[("!disabled", "#062028")])
[Link]("[Link]", fieldbackground="#ffffff",
foreground=CLR_TEXT, insertcolor=CLR_TEXT)
# Menu (File → Reset, Reveal Folder…, Quit)
m = [Link](self)
fm = [Link](m, tearoff=False)
fm.add_command(label="Reset", command=self.reset_ui)
fm.add_command(label="Reveal Folder…", command=lambda:
self._reveal_current())
fm.add_separator()
fm.add_command(label="Quit", command=[Link])
m.add_cascade(label="File", menu=fm)
[Link](menu=m)
# Header + hint
[Link](self, text="Preview only the first five lines of a
file", style="[Link]")\
.pack(anchor="w", padx=16, pady=(16, 6))
[Link](self, text="Enter [Link] or [Link] (created
in Q1a).", style="[Link]")\
.pack(anchor="w", padx=16, pady=(0, 10))
# Controls row (filename, browse, actions)
row = [Link](self); [Link](fill="x", padx=16, pady=(0, 10))
[Link](row, text="Filename:").pack(side="left", padx=(0, 8))
self.name_var = [Link](value=DEFAULT_NAME)
self.name_entry = [Link](row, textvariable=self.name_var,
width=36, style="[Link]")
self.name_entry.pack(side="left")
[Link](row, text="Browse…", style="[Link]",
command=[Link]).pack(side="left", padx=8)
[Link](row, text="Open (First 5)", style="[Link]",
command=self.preview_first5).pack(side="left", padx=8)
[Link](row, text="Show Full", style="[Link]",
command=self.preview_full).pack(side="left", padx=8)
[Link](row, text="Copy Visible", style="[Link]",
command=self.copy_visible).pack(side="left", padx=8)
# Resolved path + counts
self.path_var = [Link](value="Resolved path: (none)")
self.info_var = [Link](value="Lines in file: - • Showing:
-")
[Link](self, textvariable=self.path_var,
style="[Link]").pack(anchor="w", padx=16, pady=(0, 2))
[Link](self, textvariable=self.info_var,
style="[Link]").pack(anchor="w", padx=16, pady=(0, 8))
# Viewer (highlight first five lines with a tag)
[Link] = [Link](self, height=20, wrap="none",
relief="flat",
bg=CLR_PANEL, fg=CLR_TEXT,
insertbackground=CLR_TEXT)
[Link].tag_configure("firstfive", background=CLR_HILITE)
[Link](fill="both", expand=True, padx=16, pady=(0, 14))
# Enter key opens first five
[Link]("<Return>", lambda _e: self.preview_first5())
# Function — file picker dialog
def browse(self) -> None:
p = [Link](
title="Select a text file",
initialdir=SCRIPT_DIR,
filetypes=[("Text files", "*.txt"), ("All files", "*.*")]
)
if p:
self.name_var.set(p)
self.preview_first5()
# Function — show only the first five lines with blue highlight
def preview_first5(self) -> None:
try:
p = resolve_path(self.name_var.get())
self._update_path_label(p)
text, total = read_first_five(p)
self._set_view(text)
# Tag-highlight the first five (or fewer) lines in the viewer
shown = len([Link]())
if shown > 0:
[Link].tag_add("firstfive", "1.0", f"{shown}.0
lineend")
self.info_var.set(f"Lines in file: {total} • Showing: {min(5,
total)}")
except Exception as e:
[Link]("Error", str(e))
# Function — show full file content (no highlight)
def preview_full(self) -> None:
try:
p = resolve_path(self.name_var.get())
self._update_path_label(p)
text, total = read_full(p)
self._set_view(text)
self.info_var.set(f"Lines in file: {total} • Showing:
{total}")
except Exception as e:
[Link]("Error", str(e))
# Function — copy current viewer text to clipboard
def copy_visible(self) -> None:
data = [Link]("1.0", "end").rstrip("\n")
if not data:
[Link]("Copy", "Nothing to copy.")
return
self.clipboard_clear()
self.clipboard_append(data)
[Link]("Copy", "Visible text copied to clipboard.")
# Function — reset filename, labels, and clear viewer
def reset_ui(self) -> None:
self.name_var.set(DEFAULT_NAME)
self.path_var.set("Resolved path: (none)")
self.info_var.set("Lines in file: - • Showing: -")
self._set_view("")
# Function — reveal current file’s folder via OS file manager
def _reveal_current(self) -> None:
try:
p = resolve_path(self.name_var.get())
if [Link]():
reveal_in_folder(p)
else:
[Link]("Reveal", "File not found; choose
Browse… first.")
except Exception as e:
[Link]("Reveal Error", str(e))
# Function — set viewer text and clear any highlights
def _set_view(self, text: str) -> None:
[Link](state="normal")
[Link]("1.0", "end")
[Link]("1.0", text)
[Link].tag_remove("firstfive", "1.0", "end")
[Link](state="normal") # keep editable for
selection/copy
# Function — update path label with resolved/absolute path
def _update_path_label(self, p: Path) -> None:
self.path_var.set(f"Resolved path: {p if [Link]() else '(not
found)'}")
# Entrypoint # Direct run guard: initialize and run the GUI.
if __name__ == "__main__":
app = FirstFivePro()
[Link]()
Results
Program 2. Counting Names
Question 2a
#!/usr/bin/env python3
# Q2a — Create [Link] with the required 20 names
# Imports — GUI toolkit, dialogs, filesystem
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from pathlib import Path
# Variables — output path and exact dataset
SCRIPT_DIR = Path(__file__).[Link]()
NAMES_FILE = SCRIPT_DIR / "[Link]"
NAMES_20 = [
"Liam","Emma","Noah","Olivia","William","Ava","James","Isabella","Oliver"
,"Sophia",
"Benjamin","Charlotte","Elijah","Mia","Lucas","Amelia","Mason","Harper","
Logan","Evelyn",
# Variables — color scheme inspired by your swatch
COL_BG = "#dbe9f6"
COL_PANEL = "#eef6ff"
COL_TEXT = "#0b2545"
COL_HINT = "#4a6b8a"
COL_ACCENT = "#16a34a"
COL_ACCENT_2 = "#22d3ee"
COL_ENTRY = "#fff6cf"
# Function — write all 20 names to [Link] (newline-separated)
def write_names(overwrite: bool = True) -> None:
text = "\n".join(NAMES_20) + "\n"
if overwrite or not NAMES_FILE.exists():
NAMES_FILE.write_text(text, encoding="utf-8")
# Function — read [Link] or return a helpful hint
def read_or_hint() -> str:
return NAMES_FILE.read_text(encoding="utf-8") if NAMES_FILE.exists()
else "[Link] not found."
# Function — open the output folder in the OS file manager
def reveal_folder() -> None:
path = NAMES_FILE.parent
try:
if Path().anchor and Path().anchor[0].isalpha(): # Windows
truthy check
import os
[Link](path) # type: ignore[attr-defined]
else:
import subprocess, platform
[Link](["open" if [Link]() == "Darwin" else
"xdg-open", str(path)])
except Exception:
[Link]("Reveal", f"Folder: {path}")
# Class — minimal pro UI to create/preview [Link]
class NamesMakerPro([Link]):
# Build window, theme, menu, buttons, and preview box
def __init__(self) -> None:
super().__init__()
[Link]("Q2a — [Link] Builder ")
[Link]("880x520")
[Link](bg=COL_BG)
# ttk styles (light-blue base, green CTAs, pale-yellow entry)
s = [Link](self)
try: s.theme_use("clam")
except [Link]: pass
[Link](".", background=COL_BG, foreground=COL_TEXT)
[Link]("TFrame", background=COL_BG)
[Link]("TLabel", background=COL_BG, foreground=COL_TEXT)
[Link]("[Link]", background=COL_BG,
foreground=COL_HINT)
[Link]("[Link]", font=("Segoe UI", 15, "bold"),
foreground=COL_ACCENT)
[Link]("[Link]", padding=(10,6), font=("Segoe
UI",10,"bold"))
[Link]("[Link]", background=[("!disabled", COL_ACCENT)],
foreground=[("!disabled", "white")])
[Link]("[Link]", padding=(9,5))
[Link]("[Link]", background=[("!disabled", COL_ACCENT_2)],
foreground=[("!disabled", COL_TEXT)])
[Link]("[Link]", fieldbackground=COL_ENTRY,
foreground=COL_TEXT, insertcolor=COL_TEXT)
# Menu — Reset, Reveal Folder, Quit
m = [Link](self)
fm = [Link](m, tearoff=False)
fm.add_command(label="Reset", command=self.reset_ui)
fm.add_command(label="Reveal Folder", command=reveal_folder)
fm.add_separator()
fm.add_command(label="Quit", command=[Link])
m.add_cascade(label="File", menu=fm)
[Link](menu=m)
# Header & hint
[Link](self, text="Create [Link] with 20 specified names",
style="[Link]")\
.pack(anchor="w", padx=16, pady=(16, 6))
[Link](self, text=f"Will save to: {NAMES_FILE}",
style="[Link]")\
.pack(anchor="w", padx=16, pady=(0, 10))
# Action row — Write, Create if Missing, Preview, Reveal
row = [Link](self); [Link](fill="x", padx=16, pady=(0, 10))
[Link](row, text="Write (Overwrite)", style="[Link]",
command=self.write_overwrite)\
.pack(side="left")
[Link](row, text="Create If Missing", style="[Link]",
command=self.create_if_missing)\
.pack(side="left", padx=8)
[Link](row, text="Preview", style="[Link]",
command=[Link])\
.pack(side="left", padx=8)
[Link](row, text="Reveal Folder", style="[Link]",
command=reveal_folder)\
.pack(side="left", padx=8)
# Preview box — pale panel, selectable text
[Link] = [Link](self, height=18, wrap="none",
relief="flat",
bg=COL_PANEL, fg=COL_TEXT,
insertbackground=COL_TEXT)
[Link](fill="both", expand=True, padx=16, pady=(0, 14))
# Status line — quick feedback
[Link] = [Link](value="Status: ready.")
[Link](self, textvariable=[Link], style="[Link]")\
.pack(anchor="w", padx=16, pady=(0, 6))
# First run: ensure file exists so 2b can find it
self.create_if_missing()
# Handler — overwrite file with the 20 names, then preview
def write_overwrite(self) -> None:
try:
write_names(overwrite=True)
[Link]("Status: wrote [Link] (20 names).")
[Link]()
except Exception as e:
[Link]("Error", str(e))
# Handler — only create if not present
def create_if_missing(self) -> None:
try:
write_names(overwrite=False)
[Link]("Status: ensured [Link] exists.")
[Link]()
except Exception as e:
[Link]("Error", str(e))
# Handler — display file contents in the viewer
def preview(self) -> None:
[Link]("1.0", "end")
[Link]("1.0", read_or_hint())
# Handler — clear preview and reset status line
def reset_ui(self) -> None:
[Link]("1.0", "end")
[Link]("Status: ready.")
# Entry — build window and start Tk event loop
if __name__ == "__main__":
app = NamesMakerPro()
[Link]()
Results
Question2b
#!/usr/bin/env python3
# Q2b — Count names in a picked file (priming read + while)
# Imports — GUI, dialogs, filesystem, typing
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from pathlib import Path
from typing import Optional
# Variables — default folder + palette
SCRIPT_DIR = Path(__file__).[Link]()
COL_BG="#dbe9f6"; COL_PANEL="#eef6ff"; COL_TEXT="#0b2545";
COL_HINT="#4a6b8a"
COL_ACCENT="#16a34a"; COL_ACCENT_2="#22d3ee"
# Function — count lines via priming read + while (counts every line)
def count_names_priming(path: Path) -> int:
if not [Link]():
raise FileNotFoundError("Pick a valid file first.")
total = 0
line = '' # sentinel
f = [Link]("r", encoding="utf-8")
try:
line = [Link]() # priming read
while line != '':
total += 1 # count line
line = [Link]()
finally:
[Link]()
return total
# Function — read whole file or hint
def read_all_or_hint(path: Optional[Path]) -> str:
return path.read_text(encoding="utf-8") if (path and [Link]())
else "No file selected."
# Class — picker-first UI: Pick → Preview → Count
class NamesCounterPicker([Link]):
# Build window, styles, menu, controls
def __init__(self) -> None:
super().__init__()
[Link]("Q2b — Count Names")
[Link]("900x560")
[Link](bg=COL_BG)
[Link]: Optional[Path] = None
s = [Link](self)
try: s.theme_use("clam")
except [Link]: pass
[Link](".", background=COL_BG, foreground=COL_TEXT)
[Link]("TFrame", background=COL_BG)
[Link]("TLabel", background=COL_BG, foreground=COL_TEXT)
[Link]("[Link]", background=COL_BG,
foreground=COL_HINT)
[Link]("[Link]", font=("Segoe UI", 15, "bold"),
foreground=COL_ACCENT)
[Link]("[Link]", padding=(10,6), font=("Segoe
UI",10,"bold"))
[Link]("[Link]", background=[("!disabled", COL_ACCENT)],
foreground=[("!disabled", "white")])
[Link]("[Link]", padding=(9,5))
[Link]("[Link]", background=[("!disabled", COL_ACCENT_2)],
foreground=[("!disabled", COL_TEXT)])
# Menu (File)
m = [Link](self)
fm = [Link](m, tearoff=False)
fm.add_command(label="Reset", command=self.reset_ui)
fm.add_command(label="Reveal Folder", command=self.reveal_folder)
fm.add_separator()
fm.add_command(label="Quit", command=[Link])
m.add_cascade(label="File", menu=fm)
[Link](menu=m)
# Header
[Link](self, text="Pick a text file, then count names (priming
read + while).",
style="[Link]").pack(anchor="w", padx=16,
pady=(16,6))
[Link](self, text="Tip: Use Pick File… — no typing.",
style="[Link]")\
.pack(anchor="w", padx=16, pady=(0,10))
# Controls
row = [Link](self); [Link](fill="x", padx=16, pady=(0,10))
self.btn_pick = [Link](row, text="Pick File…",
style="[Link]", command=self.pick_file)
self.btn_prev = [Link](row, text="Preview",
style="[Link]", command=[Link], state="disabled")
self.btn_cnt = [Link](row, text="Count Names",
style="[Link]", command=self.count_names, state="disabled")
self.btn_pick.pack(side="left")
self.btn_prev.pack(side="left", padx=8)
self.btn_cnt.pack(side="left", padx=8)
# Path + count labels
self.path_var = [Link](value="Selected file: (none)")
self.count_var = [Link](value="Names count: -")
[Link](self, textvariable=self.path_var,
style="[Link]").pack(anchor="w", padx=16, pady=(0,4))
[Link](self, textvariable=self.count_var).pack(anchor="w",
padx=16, pady=(0,8))
# Viewer
[Link] = [Link](self, height=20, wrap="none",
relief="flat",
bg=COL_PANEL, fg=COL_TEXT,
insertbackground=COL_TEXT)
[Link](fill="both", expand=True, padx=16, pady=(0,14))
# Enter → Count
[Link]("<Return>", lambda _e: self.count_names() if
[Link] else None)
# Pick a file and enable actions
def pick_file(self) -> None:
p = [Link](
title="Select a text file",
initialdir=SCRIPT_DIR,
filetypes=[("Text files", "*.txt"), ("All files", "*.*")]
if p:
[Link] = Path(p).resolve()
self.path_var.set(f"Selected file: {[Link]}")
self.btn_prev.configure(state="normal")
self.btn_cnt.configure(state="normal")
[Link]()
# Preview file contents
def preview(self) -> None:
[Link]("1.0", "end")
[Link]("1.0", read_all_or_hint([Link]))
# Count names with priming read
def count_names(self) -> None:
try:
if not [Link]:
raise FileNotFoundError("Pick a file first.")
total = count_names_priming([Link])
self.count_var.set(f"Names count: {total}")
[Link]()
except Exception as e:
[Link]("Error", str(e))
# Reset selection, labels, viewer, and buttons
def reset_ui(self) -> None:
[Link] = None
self.path_var.set("Selected file: (none)")
self.count_var.set("Names count: -")
[Link]("1.0", "end")
self.btn_prev.configure(state="disabled")
self.btn_cnt.configure(state="disabled")
# Open the selected file’s folder (or the script folder)
def reveal_folder(self) -> None:
try:
folder = ([Link] if [Link] else
SCRIPT_DIR)
import platform, subprocess, os
if [Link]() == "Windows":
[Link](folder) # type: ignore[attr-defined]
elif [Link]() == "Darwin":
[Link](["open", folder])
else:
[Link](["xdg-open", folder])
except Exception:
pass
# Entry — build window and start Tk loop
if __name__ == "__main__":
app = NamesCounterPicker()
[Link]()
Results
Question 3. Getting a Total
Question 3a
#!/usr/bin/env python3
# Q3a — Getting a Total ([Link]) with priming read + for
# Imports — GUI, dialogs, filesystem, typing
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from pathlib import Path
from typing import Optional
# Variables — default folder + palette
SCRIPT_DIR = Path(__file__).[Link]()
COL_BG="#dbe9f6"; COL_PANEL="#eef6ff"; COL_TEXT="#0b2545";
COL_HINT="#4a6b8a"
COL_ACCENT="#16a34a"; COL_ACCENT_2="#22d3ee"
# Function — create [Link] with required integers
def create_numbers_file(path: Path) -> None:
path.write_text("75\n63\n-8\n-99\n", encoding="utf-8")
# Function — compute total via priming read then for-loop
def total_from_file(path: Path) -> float:
if not [Link]():
raise FileNotFoundError("[Link] not found. Click “Create
File” first.")
total: float = 0.0
f = [Link]("r", encoding="utf-8")
try:
line: str = [Link]() # priming read
if line != '':
total += float([Link]() or 0.0)
for line in f: # process remaining lines
if [Link]():
total += float([Link]())
finally:
[Link]()
return total
# Function — read whole file or hint
def read_all_or_hint(path: Optional[Path]) -> str:
return path.read_text(encoding="utf-8") if (path and [Link]())
else "No file found."
# Class — UI: Create → Preview → Total
class NumbersTotalApp([Link]):
# Build window, styles, menu, controls
def __init__(self) -> None:
super().__init__()
[Link]("Q3a — Getting a Total ([Link])")
[Link]("900x560")
[Link](bg=COL_BG)
self.numbers_path: Path = SCRIPT_DIR / "[Link]"
s = [Link](self)
try: s.theme_use("clam")
except [Link]: pass
[Link](".", background=COL_BG, foreground=COL_TEXT)
[Link]("TFrame", background=COL_BG)
[Link]("TLabel", background=COL_BG, foreground=COL_TEXT)
[Link]("[Link]", background=COL_BG,
foreground=COL_HINT)
[Link]("[Link]", font=("Segoe UI", 15, "bold"),
foreground=COL_ACCENT)
[Link]("[Link]", padding=(10,6), font=("Segoe
UI",10,"bold"))
[Link]("[Link]", background=[("!disabled", COL_ACCENT)],
foreground=[("!disabled", "white")])
[Link]("[Link]", padding=(9,5))
[Link]("[Link]", background=[("!disabled", COL_ACCENT_2)],
foreground=[("!disabled", COL_TEXT)])
# Menu (File)
m = [Link](self)
fm = [Link](m, tearoff=False)
fm.add_command(label="Open Another File…",
command=self.pick_file)
fm.add_command(label="Reveal Folder", command=self.reveal_folder)
fm.add_separator()
fm.add_command(label="Quit", command=[Link])
m.add_cascade(label="File", menu=fm)
[Link](menu=m)
# Header
[Link](self,
text="Create [Link] (75, 63, -8, -99), then
compute the total (priming read + for).",
style="[Link]").pack(anchor="w", padx=16,
pady=(16,6))
[Link](self, text="Tip: Click Create File → Preview → Get
Total.",
style="[Link]").pack(anchor="w", padx=16,
pady=(0,10))
# Controls
row = [Link](self); [Link](fill="x", padx=16, pady=(0,10))
self.btn_create = [Link](row, text="Create File",
style="[Link]", command=self.create_file)
self.btn_prev = [Link](row, text="Preview",
style="[Link]", command=[Link])
self.btn_total = [Link](row, text="Get Total",
style="[Link]", command=self.get_total)
self.btn_create.pack(side="left")
self.btn_prev.pack(side="left", padx=8)
self.btn_total.pack(side="left", padx=8)
# Path + total labels
self.path_var = [Link](value=f"Target:
{self.numbers_path}")
self.total_var = [Link](value="Total: -")
[Link](self, textvariable=self.path_var,
style="[Link]").pack(anchor="w", padx=16, pady=(0,4))
[Link](self, textvariable=self.total_var).pack(anchor="w",
padx=16, pady=(0,8))
# Viewer
[Link] = [Link](self, height=20, wrap="none",
relief="flat",
bg=COL_PANEL, fg=COL_TEXT,
insertbackground=COL_TEXT)
[Link](fill="both", expand=True, padx=16, pady=(0,14))
# Enter → Get Total
[Link]("<Return>", lambda _e: self.get_total())
# Create [Link] with required values
def create_file(self) -> None:
try:
create_numbers_file(self.numbers_path)
self.path_var.set(f"Target: {self.numbers_path}
(created/updated)")
[Link]()
except Exception as e:
[Link]("Error", str(e))
# Preview file contents
def preview(self) -> None:
[Link]("1.0", "end")
[Link]("1.0", read_all_or_hint(self.numbers_path))
# Compute total (priming read + for loop)
def get_total(self) -> None:
try:
total = total_from_file(self.numbers_path)
self.total_var.set(f"Total: {total:.2f}")
[Link]()
except Exception as e:
[Link]("Error", str(e))
# Allow user to pick a different file
def pick_file(self) -> None:
p = [Link](
title="Select numbers file",
initialdir=SCRIPT_DIR,
filetypes=[("Text files", "*.txt"), ("All files", "*.*")]
)
if p:
self.numbers_path = Path(p).resolve()
self.path_var.set(f"Target: {self.numbers_path}")
[Link]()
# Open folder
def reveal_folder(self) -> None:
try:
folder = self.numbers_path.parent
import platform, subprocess, os
if [Link]() == "Windows":
[Link](folder) # type: ignore[attr-defined]
elif [Link]() == "Darwin":
[Link](["open", folder])
else:
[Link](["xdg-open", folder])
except Exception:
pass
# Entry — build window and start Tk loop
if __name__ == "__main__":
app = NumbersTotalApp()
[Link]()
Result
Question 3b
#!/usr/bin/env python3
# Q3b — Getting an Average ([Link]) with priming read + while
# Imports — GUI, dialogs, filesystem, typing
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from pathlib import Path
from typing import Optional, Tuple
# Variables — default folder
SCRIPT_DIR = Path(__file__).[Link]()
COL_BG="#dbe9f6"; COL_PANEL="#eef6ff"; COL_TEXT="#0b2545";
COL_HINT="#4a6b8a"
COL_ACCENT="#16a34a"; COL_ACCENT_2="#22d3ee"
# Function — create [Link] with the required integers
def create_numbers_file(path: Path) -> None:
path.write_text("75\n63\n-8\n-99\n", encoding="utf-8")
# Function — compute total, count, average via priming read + while
def stats_from_file(path: Path) -> Tuple[float, int, float]:
if not [Link]():
raise FileNotFoundError("[Link] not found. Click “Create
File” first or pick a file.")
total: float = 0.0
count: int = 0
f = [Link]("r", encoding="utf-8")
try:
line: str = [Link]() # priming read
while line != "": # EOF sentinel
val = [Link]()
if val: # skip blank lines
try:
num = float(val)
total += num
count += 1
except ValueError:
# Non-numeric line → ignore but continue
pass
line = [Link]() # advance
finally:
[Link]()
avg = (total / count) if count else 0.0
return total, count, avg
# Function — read whole file or hint
def read_all_or_hint(path: Optional[Path]) -> str:
return path.read_text(encoding="utf-8") if (path and [Link]())
else "No file found."
# Class — UI: Create/Pick → Preview → Average
class NumbersAverageApp([Link]):
def __init__(self) -> None:
super().__init__()
[Link]("Q3b — Getting an Average ([Link])")
[Link]("900x560")
[Link](bg=COL_BG)
self.numbers_path: Path = SCRIPT_DIR / "[Link]"
# Styles
s = [Link](self)
try: s.theme_use("clam")
except [Link]: pass
[Link](".", background=COL_BG, foreground=COL_TEXT)
[Link]("TFrame", background=COL_BG)
[Link]("TLabel", background=COL_BG, foreground=COL_TEXT)
[Link]("[Link]", background=COL_BG,
foreground=COL_HINT)
[Link]("[Link]", font=("Segoe UI", 15, "bold"),
foreground=COL_ACCENT)
[Link]("[Link]", padding=(10,6), font=("Segoe
UI",10,"bold"))
[Link]("[Link]", background=[("!disabled", COL_ACCENT)],
foreground=[("!disabled", "white")])
[Link]("[Link]", padding=(9,5))
[Link]("[Link]", background=[("!disabled", COL_ACCENT_2)],
foreground=[("!disabled", COL_TEXT)])
# Menu
m = [Link](self)
fm = [Link](m, tearoff=False)
fm.add_command(label="Open Another File…",
command=self.pick_file)
fm.add_command(label="Reveal Folder", command=self.reveal_folder)
fm.add_separator()
fm.add_command(label="Quit", command=[Link])
m.add_cascade(label="File", menu=fm)
[Link](menu=m)
# Header
[Link](self,
text="Compute the average from [Link] (priming
read + while).",
style="[Link]").pack(anchor="w", padx=16,
pady=(16,6))
[Link](self,
text="Tip: Create or Pick a file → Preview → Get
Average.",
style="[Link]").pack(anchor="w", padx=16,
pady=(0,10))
# Controls
row = [Link](self); [Link](fill="x", padx=16, pady=(0,10))
self.btn_create = [Link](row, text="Create File",
style="[Link]", command=self.create_file)
self.btn_prev = [Link](row, text="Preview",
style="[Link]", command=[Link])
self.btn_avg = [Link](row, text="Get Average",
style="[Link]", command=self.get_average)
self.btn_create.pack(side="left")
self.btn_prev.pack(side="left", padx=8)
self.btn_avg.pack(side="left", padx=8)
# Path + stats labels
self.path_var = [Link](value=f"Target:
{self.numbers_path}")
self.total_var = [Link](value="Total: -")
self.count_var = [Link](value="Count: -")
self.avg_var = [Link](value="Average: -")
[Link](self, textvariable=self.path_var,
style="[Link]").pack(anchor="w", padx=16, pady=(0,4))
[Link](self, textvariable=self.total_var).pack(anchor="w",
padx=16)
[Link](self, textvariable=self.count_var).pack(anchor="w",
padx=16)
[Link](self, textvariable=self.avg_var ).pack(anchor="w",
padx=16, pady=(0,8))
# Viewer
[Link] = [Link](self, height=20, wrap="none",
relief="flat",
bg=COL_PANEL, fg=COL_TEXT,
insertbackground=COL_TEXT)
[Link](fill="both", expand=True, padx=16, pady=(0,14))
# Enter → Get Average
[Link]("<Return>", lambda _e: self.get_average())
# Create [Link] with required values
def create_file(self) -> None:
try:
create_numbers_file(self.numbers_path)
self.path_var.set(f"Target: {self.numbers_path}
(created/updated)")
[Link]()
except Exception as e:
[Link]("Error", str(e))
# Preview file contents
def preview(self) -> None:
[Link]("1.0", "end")
[Link]("1.0", read_all_or_hint(self.numbers_path))
# Compute stats (priming read + while)
def get_average(self) -> None:
try:
total, count, avg = stats_from_file(self.numbers_path)
self.total_var.set(f"Total: {total:.2f}")
self.count_var.set(f"Count: {count}")
self.avg_var.set(f"Average: {avg:.2f}")
[Link]()
except Exception as e:
[Link]("Error", str(e))
# Allow user to pick a different file
def pick_file(self) -> None:
p = [Link](
title="Select numbers file",
initialdir=SCRIPT_DIR,
filetypes=[("Text files", "*.txt"), ("All files", "*.*")]
if p:
self.numbers_path = Path(p).resolve()
self.path_var.set(f"Target: {self.numbers_path}")
[Link]()
# Open folder
def reveal_folder(self) -> None:
try:
folder = self.numbers_path.parent
import platform, subprocess, os
if [Link]() == "Windows":
[Link](folder) # type: ignore[attr-defined]
elif [Link]() == "Darwin":
[Link](["open", folder])
else:
[Link](["xdg-open", folder])
except Exception:
pass
# Entry — build window and start Tk loop
if __name__ == "__main__":
app = NumbersAverageApp()
[Link]()
Result
Question 4. Random Number Generator
Question 4
#!/usr/bin/env python3
# Q4 — Random Number Generator
import random
# Imports — Tk UI, dialogs, filesystem paths, typing, RNG
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from pathlib import Path
from typing import Optional, List
# Variables — working folder + color palette
SCRIPT_DIR = Path(__file__).[Link]()
COL_BG="#dbe9f6"; COL_PANEL="#eef6ff"; COL_TEXT="#0b2545";
COL_HINT="#4a6b8a"
COL_ACCENT="#16a34a"; COL_ACCENT_2="#22d3ee"
# Constants — inclusive bounds per spec + default output name
LOW, HIGH = 1, 350
DEFAULT_OUT = SCRIPT_DIR / "[Link]"
# Function — write_numbers(path, nums): overwrite file with one integer
per line
def write_numbers(path: Path, nums: List[int]) -> None:
path.write_text("\n".join(map(str, nums)) + "\n", encoding="utf-8")
# Function — read_all_or_hint(path): show file contents or a friendly
hint
def read_all_or_hint(path: Optional[Path]) -> str:
return path.read_text(encoding="utf-8") if (path and [Link]())
else "No file found."
# Class — RandomNumbersApp: Generate → Write →
class RandomNumbersApp([Link]):
# __init__: window, styles, menu, controls, bindings
def __init__(self) -> None:
super().__init__()
[Link]("Q4 — Random Number Generator ([Link])")
[Link]("900x560")
[Link](bg=COL_BG)
# State — output path + in-memory generated numbers
self.output_path: Path = DEFAULT_OUT
[Link]: List[int] = []
# Styles — clam theme + consistent palette
s = [Link](self)
try: s.theme_use("clam")
except [Link]: pass
[Link](".", background=COL_BG, foreground=COL_TEXT)
[Link]("TFrame", background=COL_BG)
[Link]("TLabel", background=COL_BG, foreground=COL_TEXT)
[Link]("[Link]", background=COL_BG,
foreground=COL_HINT)
[Link]("[Link]", font=("Segoe UI", 15, "bold"),
foreground=COL_ACCENT)
[Link]("[Link]", padding=(10,6), font=("Segoe
UI",10,"bold"))
[Link]("[Link]", background=[("!disabled", COL_ACCENT)],
foreground=[("!disabled", "white")])
[Link]("[Link]", padding=(9,5))
[Link]("[Link]", background=[("!disabled", COL_ACCENT_2)],
foreground=[("!disabled", COL_TEXT)])
# Menu — Save As, Reveal folder, Quit
m = [Link](self)
fm = [Link](m, tearoff=False)
fm.add_command(label="Save As…", command=self.save_as)
fm.add_command(label="Reveal Folder", command=self.reveal_folder)
fm.add_separator()
fm.add_command(label="Quit", command=[Link])
m.add_cascade(label="File", menu=fm)
[Link](menu=m)
# Header — task instructions
[Link](self,
text="Generate random integers (1–350) → write to
[Link] (one per line).",
style="[Link]").pack(anchor="w", padx=16,
pady=(16,6))
[Link](self,
text="Enter how many numbers to generate, then Generate
→ Write File → Preview.",
style="[Link]").pack(anchor="w", padx=16,
pady=(0,10))
# Controls row — count input + action buttons
row = [Link](self); [Link](fill="x", padx=16, pady=(0,10))
[Link](row, text="How many numbers:").pack(side="left",
padx=(0,8))
self.var_count = [Link](value=20)
self.inp_count = [Link](row, from_=1, to=100000,
textvariable=self.var_count, width=8)
self.inp_count.pack(side="left")
self.btn_gen = [Link](row, text="Generate",
style="[Link]", command=[Link])
self.btn_save = [Link](row, text="Write File",
style="[Link]", command=self.write_file, state="disabled")
self.btn_prev = [Link](row, text="Preview",
style="[Link]", command=[Link], state="disabled")
self.btn_gen.pack(side="left", padx=8)
self.btn_save.pack(side="left", padx=8)
self.btn_prev.pack(side="left", padx=8)
# Labels — current target path + status meta
self.path_var = [Link](value=f"Output file:
{self.output_path}")
self.meta_var = [Link](value="Status: Ready")
[Link](self, textvariable=self.path_var,
style="[Link]").pack(anchor="w", padx=16, pady=(0,4))
[Link](self, textvariable=self.meta_var).pack(anchor="w",
padx=16, pady=(0,8))
# Viewer — shows quick preview and full file contents
[Link] = [Link](self, height=20, wrap="none",
relief="flat",
bg=COL_PANEL, fg=COL_TEXT,
insertbackground=COL_TEXT)
[Link](fill="both", expand=True, padx=16, pady=(0,14))
# Binding — Enter key triggers Generate
[Link]("<Return>", lambda _e: [Link]())
# Handler — generate(): validate count, build randint list in [LOW,
HIGH], enable Save
def generate(self) -> None:
try:
n = int(self.var_count.get())
if n <= 0:
raise ValueError("Please enter a positive count.")
[Link] = [[Link](LOW, HIGH) for _ in
range(n)]
self.meta_var.set(
f"Generated: {n} numbers (range {LOW}–{HIGH}) |
min={min([Link])} max={max([Link])}"
self.btn_save.configure(state="normal")
self.btn_prev.configure(state="disabled")
[Link]("1.0", "end")
head = ", ".join(map(str, [Link][:30]))
more = " …" if len([Link]) > 30 else ""
[Link]("1.0", f"Preview (first
{min(30,len([Link]))}): {head}{more}\n")
except Exception as e:
[Link]("Error", str(e))
# Handler — write_file(): require generated data, write to disk,
enable Preview
def write_file(self) -> None:
try:
if not [Link]:
raise RuntimeError("Generate numbers first.")
write_numbers(self.output_path, [Link])
self.meta_var.set(f"Wrote {len([Link])} numbers to
{self.output_path.name}")
self.btn_prev.configure(state="normal")
[Link]()
except Exception as e:
[Link]("Error", str(e))
# Handler — preview(): load current output file into the viewer (or
hint)
def preview(self) -> None:
[Link]("1.0", "end")
[Link]("1.0", read_all_or_hint(self.output_path))
# Handler — save_as(): choose a new filename; if data exists, write
immediately
def save_as(self) -> None:
p = [Link](
title="Save numbers file as…",
initialdir=SCRIPT_DIR,
defaultextension=".txt",
filetypes=[("Text files", "*.txt"), ("All files", "*.*")]
)
if p:
self.output_path = Path(p).resolve()
self.path_var.set(f"Output file: {self.output_path}")
if [Link]:
self.write_file(
# Handler — reveal_folder(): open folder in OS file explorer
def reveal_folder(self) -> None:
try:
folder = self.output_path.parent
import platform, subprocess, os
if [Link]() == "Windows":
[Link](folder) # type: ignore[attr-defined]
elif [Link]() == "Darwin":
[Link](["open", folder])
else:
[Link](["xdg-open", folder])
except Exception:
pass
# Entry — build window and start Tk loop
if __name__ == "__main__":
app = RandomNumbersApp()
[Link]()
Result
Question 5. Golf Scores
Question5a Code
#!/usr/bin/env python3
# Q5a — Golf Scores (enter name + score → save to [Link])
# Imports — Tk UI, dialogs, filesystem, typing
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from pathlib import Path
from typing import Optional, List, Tuple
# Variables — working folder + color palette
SCRIPT_DIR = Path(__file__).[Link]()
COL_BG="#dbe9f6"; COL_PANEL="#eef6ff"; COL_TEXT="#0b2545";
COL_HINT="#4a6b8a"
COL_ACCENT="#16a34a"; COL_ACCENT_2="#22d3ee"
# Constants — output file + basic validation ranges
GOLF_FILE = SCRIPT_DIR / "[Link]"
MIN_SCORE, MAX_SCORE = 1, 200
# Function — pack a (name, score) as a CSV line
def line_for(name: str, score: int) -> str:
return f"{[Link]()},{score}\n"
# Function — parse pending list to CSV text
def as_csv(lines: List[Tuple[str,int]]) -> str:
return "".join(line_for(n, s) for n, s in lines)
# Function — read whole file or hint
def read_all_or_hint(path: Optional[Path]) -> str:
return path.read_text(encoding="utf-8") if (path and [Link]())
else "No file found."
# Class — GolfScoresApp: Enter → Queue → Save/Append → Preview
class GolfScoresApp([Link]):
# __init__: window, styles, menu, controls, bindings, state
def __init__(self) -> None:
super().__init__()
[Link]("Q5a — Golf Scores ([Link])")
[Link]("900x620")
[Link](bg=COL_BG)
# State — pending entries [(name, score)]
[Link]: List[Tuple[str,int]] = []
self.output_path: Path = GOLF_FILE
# Styles — clam theme + palette
s = [Link](self)
try: s.theme_use("clam")
except [Link]: pass
[Link](".", background=COL_BG, foreground=COL_TEXT)
[Link]("TFrame", background=COL_BG)
[Link]("TLabel", background=COL_BG, foreground=COL_TEXT)
[Link]("[Link]", background=COL_BG,
foreground=COL_HINT)
[Link]("[Link]", font=("Segoe UI", 15, "bold"),
foreground=COL_ACCENT)
[Link]("[Link]", padding=(10,6), font=("Segoe
UI",10,"bold"))
[Link]("[Link]", background=[("!disabled", COL_ACCENT)],
foreground=[("!disabled", "white")])
[Link]("[Link]", padding=(9,5))
[Link]("[Link]", background=[("!disabled", COL_ACCENT_2)],
foreground=[("!disabled", COL_TEXT)])
# Menu — Save As, Reveal folder, Quit
m = [Link](self)
fm = [Link](m, tearoff=False)
fm.add_command(label="Save As…", command=self.save_as)
fm.add_command(label="Reveal Folder", command=self.reveal_folder)
fm.add_separator()
fm.add_command(label="Quit", command=[Link])
m.add_cascade(label="File", menu=fm)
[Link](menu=m)
# Header — task summary
[Link](self,
text="Enter Golfer Name + Score → queue entries → Save
(overwrite) or Append → Preview [Link].",
style="[Link]").pack(anchor="w", padx=16,
pady=(16,6))
[Link](self,
text="Tip: Add to List builds a batch before writing.",
style="[Link]").pack(anchor="w", padx=16,
pady=(0,10))
# Row — inputs + buttons
row = [Link](self); [Link](fill="x", padx=16, pady=(0,10))
[Link](row, text="Name:").pack(side="left", padx=(0,6))
self.var_name = [Link]()
self.inp_name = [Link](row, textvariable=self.var_name,
width=28)
self.inp_name.pack(side="left")
[Link](row, text="Score:").pack(side="left", padx=(12,6))
self.var_score = [Link](value="72")
self.inp_score = [Link](row, textvariable=self.var_score,
width=8)
self.inp_score.pack(side="left")
self.btn_add = [Link](row, text="Add to List",
style="[Link]", command=self.add_entry)
self.btn_save = [Link](row, text="Save (Overwrite)",
style="[Link]", command=self.save_overwrite)
self.btn_append= [Link](row, text="Append to File",
style="[Link]", command=self.append_file)
self.btn_prev = [Link](row, text="Preview File",
style="[Link]", command=[Link])
self.btn_clear = [Link](row, text="Clear List",
style="[Link]", command=self.clear_pending)
self.btn_add.pack(side="left", padx=8)
self.btn_save.pack(side="left", padx=8)
self.btn_append.pack(side="left", padx=8)
self.btn_prev.pack(side="left", padx=8)
self.btn_clear.pack(side="left", padx=8)
# Labels — file target + status
self.path_var = [Link](value=f"Output file:
{self.output_path}")
self.meta_var = [Link](value="Pending: 0")
[Link](self, textvariable=self.path_var,
style="[Link]").pack(anchor="w", padx=16, pady=(0,4))
[Link](self, textvariable=self.meta_var).pack(anchor="w",
padx=16, pady=(0,8))
# Frame — pending list + viewer
mid = [Link](self); [Link](fill="both", expand=True,
padx=16, pady=(0,14))
left = [Link](mid); [Link](side="left", fill="y")
right = [Link](mid); [Link](side="left", fill="both",
expand=True, padx=(12,0))
# Pending — listbox shows queued entries
[Link](left, text="Pending entries").pack(anchor="w")
[Link] = [Link](left, height=15, width=38, bg=COL_PANEL,
fg=COL_TEXT)
[Link](fill="y", padx=(0,0), pady=(4,0))
self.btn_remove = [Link](left, text="Remove Selected",
style="[Link]", command=self.remove_selected)
self.btn_remove.pack(anchor="w", pady=(8,0))
# Viewer — file preview
[Link](right, text="File preview").pack(anchor="w")
[Link] = [Link](right, height=18, wrap="none",
relief="flat",
bg=COL_PANEL, fg=COL_TEXT,
insertbackground=COL_TEXT)
[Link](fill="both", expand=True, pady=(4,0))
# Binding — Enter adds entry quickly
[Link]("<Return>", lambda _e: self.add_entry())
# Handler — add_entry(): validate inputs, push to pending list,
refresh UI
def add_entry(self) -> None:
try:
name = self.var_name.get().strip()
if not name:
raise ValueError("Enter golfer name.")
score = int(self.var_score.get().strip())
if not (MIN_SCORE <= score <= MAX_SCORE):
raise ValueError(f"Score must be {MIN_SCORE}–
{MAX_SCORE}.")
[Link]((name, score))
[Link]("end", f"{name} — {score}")
self.meta_var.set(f"Pending: {len([Link])}")
self.var_name.set("")
self.inp_name.focus_set()
except ValueError as e:
[Link]("Invalid input", str(e))
except Exception as e:
[Link]("Error", str(e))
# Handler — remove_selected(): drop chosen pending rows
def remove_selected(self) -> None:
try:
sel = list([Link]())
if not sel:
return
for idx in reversed(sel):
[Link](idx)
del [Link][idx]
self.meta_var.set(f"Pending: {len([Link])}")
except Exception as e:
[Link]("Error", str(e))
# Handler — clear_pending(): empty queue/listbox
def clear_pending(self) -> None:
[Link]()
[Link](0, "end")
self.meta_var.set("Pending: 0")
# Handler — save_overwrite(): write only queued entries (overwrite
file)
def save_overwrite(self) -> None:
try:
if not [Link]:
raise RuntimeError("Add at least one entry to save.")
self.output_path.write_text(as_csv([Link]),
encoding="utf-8")
self.meta_var.set(f"Saved {len([Link])} entries to
{self.output_path.name} (overwrite)")
[Link]()
except Exception as e:
[Link]("Error", str(e))
# Handler — append_file(): append queued entries to existing file
def append_file(self) -> None:
try:
if not [Link]:
raise RuntimeError("Add at least one entry to append.")
with self.output_path.open("a", encoding="utf-8") as f:
[Link](as_csv([Link]))
self.meta_var.set(f"Appended {len([Link])} entries to
{self.output_path.name}")
[Link]()
except Exception as e:
[Link]("Error", str(e))
# Handler — preview(): load file content
def preview(self) -> None:
[Link]("1.0", "end")
[Link]("1.0", read_all_or_hint(self.output_path))
# Handler — save_as(): choose alternative filename
def save_as(self) -> None:
p = [Link](
title="Save golf file as…",
initialdir=SCRIPT_DIR,
defaultextension=".txt",
filetypes=[("Text files", "*.txt"), ("All files", "*.*")]
if p:
self.output_path = Path(p).resolve()
self.path_var.set(f"Output file: {self.output_path}")
# Optionally write immediately if pending exists
# Handler — reveal_folder(): open OS file explorer at target
def reveal_folder(self) -> None:
try:
folder = self.output_path.parent
import platform, subprocess, os
if [Link]() == "Windows":
[Link](folder) # type: ignore[attr-defined]
elif [Link]() == "Darwin":
[Link](["open", folder])
else:
[Link](["xdg-open", folder])
except Exception:
pass
# Entry — start app
# build window and start Tk loop
if __name__ == "__main__":
app = GolfScoresApp()
[Link]()
Results
Question5b Code
#!/usr/bin/env python3
# Q5b — Golf Scores (read [Link] → sortable table + summary)
# Imports — build Tkinter UI, pick files, read Path objects, type
tuples/lists, and compute mean
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from pathlib import Path
from typing import List, Tuple, Optional
import statistics as stats
# Variables — base folder for default files + UI color palette used
across widgets
SCRIPT_DIR = Path(__file__).[Link]()
COL_BG="#dbe9f6"; COL_PANEL="#eef6ff"; COL_TEXT="#0b2545";
COL_HINT="#4a6b8a"
COL_ACCENT="#16a34a"; COL_ACCENT_2="#22d3ee"
# Constants — default input CSV (name,score) and allowed score range to
validate rows
GOLF_FILE = SCRIPT_DIR / "[Link]"
MIN_SCORE, MAX_SCORE = 1, 200
# Function — read_all_or_hint(path): returns full file text or a short
placeholder if missing
def read_all_or_hint(path: Optional[Path]) -> str:
return path.read_text(encoding="utf-8") if (path and [Link]())
else "No file found."
# Function — parse_golf_file(path): reads CSV lines "name,score",
enforces bounds, drops malformed rows
def parse_golf_file(path: Path) -> List[Tuple[str, int]]:
rows: List[Tuple[str, int]] = []
if not [Link]():
return rows
for i, line in enumerate(path.read_text(encoding="utf-
8").splitlines(), start=1):
raw = [Link]()
if not raw:
continue # skip empty lines
parts = [[Link]() for p in [Link](",")]
if len(parts) != 2:
continue # skip non-2-column rows
name, s = parts[0], parts[1]
if not name:
continue # require non-empty name
try:
score = int(s)
if not (MIN_SCORE <= score <= MAX_SCORE):
continue # enforce score bounds
[Link]((name, score))
except ValueError:
continue # skip non-integer score
return rows
# Function — compute_summary(rows): derives count/min/max/average
def compute_summary(rows: List[Tuple[str,int]]) -> Tuple[int,
Optional[int], Optional[int], Optional[float]]:
if not rows:
return (0, None, None, None)
scores = [s for _, s in rows]
return (len(rows), min(scores), max(scores),
float([Link](scores)))
# Class — GolfScoresViewerApp: loads [Link], shows a sortable table,
and displays live summary stats
class GolfScoresViewerApp([Link]):
# __init__: builds window, configures ttk styles, sets menus/buttons,
table, and summary labels
def __init__(self) -> None:
super().__init__()
[Link]("Q5b — Golf Scores (viewer)")
[Link]("980x640")
[Link](bg=COL_BG)
# State — currently opened file, parsed rows cache, and current
sort column/direction
self.input_path: Path = GOLF_FILE
[Link]: List[Tuple[str,int]] = []
self.sort_col: str = "score"
self.sort_asc: bool = True
# Styles — use 'clam' and apply palette to frames, labels,
buttons, and tree headings
s = [Link](self)
try: s.theme_use("clam")
except [Link]: pass
[Link](".", background=COL_BG, foreground=COL_TEXT)
[Link]("TFrame", background=COL_BG)
[Link]("TLabel", background=COL_BG, foreground=COL_TEXT)
[Link]("[Link]", background=COL_BG,
foreground=COL_HINT)
[Link]("[Link]", font=("Segoe UI", 15, "bold"),
foreground=COL_ACCENT)
[Link]("[Link]", padding=(10,6), font=("Segoe
UI",10,"bold"))
[Link]("[Link]", background=[("!disabled", COL_ACCENT)],
foreground=[("!disabled", "white")])
[Link]("[Link]", padding=(9,5))
[Link]("[Link]", background=[("!disabled", COL_ACCENT_2)],
foreground=[("!disabled", COL_TEXT)])
[Link]("Treeview", background=COL_PANEL,
fieldbackground=COL_PANEL, foreground=COL_TEXT, rowheight=26)
[Link]("[Link]", font=("Segoe UI", 10, "bold"))
# Menu — lets user pick a different file, reveal its folder, or
quit the app
m = [Link](self)
fm = [Link](m, tearoff=False)
fm.add_command(label="Pick File…", command=self.pick_file)
fm.add_command(label="Reveal Folder", command=self.reveal_folder)
fm.add_separator()
fm.add_command(label="Quit", command=[Link])
m.add_cascade(label="File", menu=fm)
[Link](menu=m)
# Header — concise instructions for workflow and a tip linking to
Q5a creation step
[Link](self,
text="Open [Link] → view records → click column
headers to sort → see summary.",
style="[Link]").pack(anchor="w", padx=16,
pady=(16,6))
[Link](self,
text="Tip: Use your writer to add records (Q5a), then
click Reload here.",
style="[Link]").pack(anchor="w", padx=16,
pady=(0,10))
# Controls — show current file path and quick actions (Pick,
Reload, Raw view)
top = [Link](self); [Link](fill="x", padx=16, pady=(0,10))
self.path_var = [Link](value=f"Input file:
{self.input_path}")
[Link](top, textvariable=self.path_var,
style="[Link]").pack(side="left")
btn_pick = [Link](top, text="Pick File…",
style="[Link]", command=self.pick_file)
btn_reload = [Link](top, text="Reload", style="[Link]",
command=[Link])
btn_view = [Link](top, text="View Raw", style="[Link]",
command=self.view_raw)
btn_pick.pack(side="right", padx=(8,0))
btn_reload.pack(side="right", padx=(8,0))
btn_view.pack(side="right")
# Table — [Link] showing (Name, Score) with clickable
headings to sort
mid = [Link](self); [Link](fill="both", expand=True,
padx=16, pady=(0,10))
cols = ("name", "score")
[Link] = [Link](mid, columns=cols, show="headings",
selectmode="browse")
[Link]("name", text="Name", command=lambda:
self.sort_by("name"))
[Link]("score", text="Score", command=lambda:
self.sort_by("score"))
[Link]("name", width=360, anchor="w")
[Link]("score", width=120, anchor="center")
vsb = [Link](mid, orient="vertical",
command=[Link])
[Link](yscrollcommand=[Link])
[Link](side="left", fill="both", expand=True)
[Link](side="left", fill="y")
# Summary — labels updated after each load/sort (Count, Min, Max,
Average)
bottom = [Link](self); [Link](fill="x", padx=16,
pady=(0,14))
self.count_var = [Link](value="Count: -")
self.min_var = [Link](value="Min: -")
self.max_var = [Link](value="Max: -")
self.avg_var = [Link](value="Average: -")
[Link](bottom, textvariable=self.count_var).pack(side="left",
padx=(0,16))
[Link](bottom, textvariable=self.min_var).pack(side="left",
padx=(0,16))
[Link](bottom, textvariable=self.max_var).pack(side="left",
padx=(0,16))
[Link](bottom, textvariable=self.avg_var).pack(side="left",
padx=(0,16))
# Initial load — populate table and stats if file currently
exists
[Link]()
# Handler — pick_file(): lets user choose a different golf file and
triggers reload
def pick_file(self) -> None:
p = [Link](
title="Open golf file",
initialdir=SCRIPT_DIR,
filetypes=[("Text files", "*.txt"), ("All files", "*.*")]
if p:
self.input_path = Path(p).resolve()
self.path_var.set(f"Input file: {self.input_path}")
[Link]()
# Handler — reload(): parses file, refreshes table rows, and
recalculates summary labels
def reload(self) -> None:
try:
[Link] = parse_golf_file(self.input_path)
self.fill_tree([Link])
self.update_summary([Link])
except Exception as e:
[Link]("Error", str(e))
# Helper — fill_tree(rows): clears the tree and inserts provided
(name, score) tuples
def fill_tree(self, rows: List[Tuple[str,int]]) -> None:
for iid in [Link].get_children():
[Link](iid)
for name, score in rows:
[Link]("", "end", values=(name, score))
# Helper — update_summary(rows): writes Count/Min/Max/Average into
bound StringVars
def update_summary(self, rows: List[Tuple[str,int]]) -> None:
cnt, lo, hi, avg = compute_summary(rows)
self.count_var.set(f"Count: {cnt}")
self.min_var.set(f"Min: {lo if lo is not None else '-'}")
self.max_var.set(f"Max: {hi if hi is not None else '-'}")
self.avg_var.set(f"Average: {avg:.2f}" if avg is not None else
"Average: -")
# Handler — sort_by(col): toggles ascending/descending, sorts cached
rows, and repaints table
def sort_by(self, col: str) -> None:
if self.sort_col == col:
self.sort_asc = not self.sort_asc
else:
self.sort_col, self.sort_asc = col, True
key = (lambda r: r[0].lower()) if col == "name" else (lambda r:
r[1])
rows = sorted([Link], key=key, reverse=not self.sort_asc)
self.fill_tree(rows)
self.update_summary(rows)
# Handler — view_raw(): opens a simple popup and shows the raw text
of the current file
def view_raw(self) -> None:
try:
txt = read_all_or_hint(self.input_path)
win = [Link](self)
[Link]("Raw file view")
[Link]("700x500")
[Link](bg=COL_BG)
t = [Link](win, bg=COL_PANEL, fg=COL_TEXT,
insertbackground=COL_TEXT, wrap="none")
[Link](fill="both", expand=True, padx=10, pady=10)
[Link]("1.0", txt)
except Exception as e:
[Link]("Error", str(e))
# Handler — reveal_folder(): opens OS file explorer in the directory
of the current file
def reveal_folder(self) -> None:
try:
folder = self.input_path.parent
import platform, subprocess, os
if [Link]() == "Windows":
[Link](folder) # type: ignore[attr-defined]
elif [Link]() == "Darwin":
[Link](["open", folder])
else:
[Link](["xdg-open", folder])
except Exception:
pass
# Entry — start the viewer app
if __name__ == "__main__":
app = GolfScoresViewerApp()
[Link]()
Results
Question 6. Total Sales
Question 6 code
#!/usr/bin/env python3
# Q6 — Total Sales (enter daily sales → total + average)
# Imports — Tkinter UI widgets, dialogs, filesystem paths, typing for
lists/tuples
import tkinter as tk
from tkinter import ttk, messagebox
from pathlib import Path
from typing import List
# Variables — base folder for default save file + color palette for
consistent styling
SCRIPT_DIR = Path(__file__).[Link]()
COL_BG="#dbe9f6"; COL_PANEL="#eef6ff"; COL_TEXT="#0b2545";
COL_HINT="#4a6b8a"
COL_ACCENT="#16a34a"; COL_ACCENT_2="#22d3ee"
# Constants — ordered list of days to prompt user
DAYS =
["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]
# Class — TotalSalesApp: allows user to enter 7 daily sales, computes
total and daily average
class TotalSalesApp([Link]):
def __init__(self) -> None:
super().__init__()
[Link]("Q6 — Total Weekly Sales")
[Link]("600x520")
[Link](bg=COL_BG)
# State — StringVars to hold user input for each day and
calculated outputs
self.sales_vars: List[[Link]] = [[Link](value="0.0")
for _ in DAYS]
self.total_var = [Link](value="Total: -")
self.avg_var = [Link](value="Average per day: -")
# Styles — apply clam theme and color palette
s = [Link](self)
try: s.theme_use("clam")
except [Link]: pass
[Link](".", background=COL_BG, foreground=COL_TEXT)
[Link]("TFrame", background=COL_BG)
[Link]("TLabel", background=COL_BG, foreground=COL_TEXT)
[Link]("[Link]", background=COL_BG,
foreground=COL_HINT)
[Link]("[Link]", font=("Segoe UI", 15, "bold"),
foreground=COL_ACCENT)
[Link]("[Link]", padding=(10,6), font=("Segoe
UI",10,"bold"))
[Link]("[Link]", background=[("!disabled", COL_ACCENT)],
foreground=[("!disabled", "white")])
[Link]("[Link]", padding=(9,5))
[Link]("[Link]", background=[("!disabled", COL_ACCENT_2)],
foreground=[("!disabled", COL_TEXT)])
# Header — instructions for user
[Link](self,
text="Enter the store’s sales for each day (Sunday →
Saturday).",
style="[Link]").pack(anchor="w", padx=16,
pady=(16,6))
[Link](self,
text="Press Calculate to see the weekly total and the
daily average.",
style="[Link]").pack(anchor="w", padx=16,
pady=(0,10))
# Entry frame — 7 labeled entry boxes (one for each day)
grid = [Link](self); [Link](padx=16, pady=(0,10), fill="x")
for i, day in enumerate(DAYS):
[Link](grid, text=f"{day}:", width=12,
anchor="w").grid(row=i, column=0, sticky="w", pady=4)
e = [Link](grid, textvariable=self.sales_vars[i],
width=12)
[Link](row=i, column=1, sticky="w", pady=4)
# Buttons — calculate totals, reset inputs
btn_frame = [Link](self); btn_frame.pack(pady=(10,10))
[Link](btn_frame, text="Calculate", style="[Link]",
command=[Link]).pack(side="left", padx=8)
[Link](btn_frame, text="Reset", style="[Link]",
command=[Link]).pack(side="left", padx=8)
# Result labels — display weekly total and daily average
[Link](self, textvariable=self.total_var, font=("Segoe UI",
12, "bold")).pack(pady=(12,4))
[Link](self, textvariable=self.avg_var, font=("Segoe UI", 12,
"bold")).pack(pady=(0,12))
# Binding — Enter key triggers calculation
[Link]("<Return>", lambda _e: [Link]())
# Handler — calculate(): validates all inputs, computes weekly total
and per-day average
def calculate(self) -> None:
try:
sales: List[float] = []
for day, var in zip(DAYS, self.sales_vars):
value = float([Link]().strip())
if value < 0:
raise ValueError(f"Sales for {day} cannot be
negative.")
[Link](value)
total = sum(sales)
average = total / len(sales)
self.total_var.set(f"Total: ${total:,.2f}")
self.avg_var.set(f"Average per day: ${average:,.2f}")
except ValueError as e:
[Link]("Invalid Input", str(e))
except Exception as e:
[Link]("Error", str(e))
# Handler — reset(): clears all entry boxes and resets result labels
def reset(self) -> None:
for var in self.sales_vars:
[Link]("0.0")
self.total_var.set("Total: -")
self.avg_var.set("Average per day: -")
# Entry — start the Total Sales app
if __name__ == "__main__":
app = TotalSalesApp()
[Link]()
Result
Question 7. Raining
Question 7 Code
#!/usr/bin/env python3
# Q7 — Raining (enter monthly rainfall → total, average, highest, lowest)
# Imports — Tkinter UI widgets, dialogs, path handling, and typing for
lists
import tkinter as tk
from tkinter import ttk, messagebox
from pathlib import Path
from typing import List
# Variables — base folder (reserved for future save/load) + shared color
palette
SCRIPT_DIR = Path(__file__).[Link]()
COL_BG="#dbe9f6"; COL_PANEL="#eef6ff"; COL_TEXT="#0b2545";
COL_HINT="#4a6b8a"
COL_ACCENT="#16a34a"; COL_ACCENT_2="#22d3ee"
# Constants — ordered month names used to label inputs and summarize
results
MONTHS = [
"January","February","March","April","May","June",
"July","August","September","October","November","December"
# Class — RainfallApp: collects 12 monthly values, computes totals,
averages, min/max with months
class RainfallApp([Link]):
# __init__: build window, styles, 12 entry boxes, buttons, and result
labels
def __init__(self) -> None:
super().__init__()
[Link]("Q7 — Yearly Rainfall Summary")
[Link]("760x640")
[Link](bg=COL_BG)
# State — StringVars for each month pre-filled to "0.0", and
result StringVars
[Link]: List[[Link]] = [[Link](value="0.0") for
_ in MONTHS]
self.total_var = [Link](value="Total rainfall: -")
self.avg_var = [Link](value="Average per month: -")
self.high_var = [Link](value="Highest month: -")
self.low_var = [Link](value="Lowest month: -")
# Styles — apply clam theme and color mapping for a consistent
look
s = [Link](self)
try: s.theme_use("clam")
except [Link]: pass
[Link](".", background=COL_BG, foreground=COL_TEXT)
[Link]("TFrame", background=COL_BG)
[Link]("TLabel", background=COL_BG, foreground=COL_TEXT)
[Link]("[Link]", background=COL_BG,
foreground=COL_HINT)
[Link]("[Link]", font=("Segoe UI", 15, "bold"),
foreground=COL_ACCENT)
[Link]("[Link]", padding=(10,6), font=("Segoe
UI",10,"bold"))
[Link]("[Link]", background=[("!disabled", COL_ACCENT)],
foreground=[("!disabled", "white")])
[Link]("[Link]", padding=(9,5))
[Link]("[Link]", background=[("!disabled", COL_ACCENT_2)],
foreground=[("!disabled", COL_TEXT)])
# Header — concise instructions and format hint
[Link](self,
text="Enter rainfall (inches, decimals allowed) for
each month.",
style="[Link]").pack(anchor="w", padx=16,
pady=(16,6))
[Link](self,
text="Press Calculate for yearly total, monthly
average, and the months with highest/lowest rainfall.",
style="[Link]").pack(anchor="w", padx=16,
pady=(0,10))
# Grid — two-column layout of month labels and entry boxes (12
rows)
grid = [Link](self); [Link](padx=16, pady=(0,12), fill="x")
for i, month in enumerate(MONTHS):
[Link](grid, text=f"{month}:", width=14,
anchor="w").grid(row=i, column=0, sticky="w", pady=4)
e = [Link](grid, textvariable=[Link][i], width=12)
[Link](row=i, column=1, sticky="w", pady=4)
# second column (for nicer spacing / future use)
[Link](grid, text="", width=2).grid(row=i, column=2)
# Buttons — calculate summary and reset all fields
btns = [Link](self); [Link](pady=(4,10))
[Link](btns, text="Calculate", style="[Link]",
command=[Link]).pack(side="left", padx=8)
[Link](btns, text="Reset", style="[Link]",
command=[Link]).pack(side="left", padx=8)
# Results — totals, average, highest month, lowest month
box = [Link](self); [Link](fill="x", padx=16, pady=(4,14))
res = [Link](box, height=8, wrap="none", relief="flat",
bg=COL_PANEL, fg=COL_TEXT,
insertbackground=COL_TEXT)
[Link](fill="both", expand=True)
self.res_view = res # keep reference for updates
# Binding — Enter triggers calculation for faster testing
[Link]("<Return>", lambda _e: [Link]())
# Initial render — show placeholders
self._render_results(total=None, avg=None, high=None, low=None)
# Handler — calculate(): validates 12 floats (>=0), computes total,
average, min/max and months
def calculate(self) -> None:
try:
vals: List[float] = []
for m, var in zip(MONTHS, [Link]):
v = float([Link]().strip())
if v < 0:
raise ValueError(f"Rainfall for {m} cannot be
negative.")
[Link](v)
total = sum(vals)
avg = total / len(vals)
hi_idx = max(range(len(vals)), key=lambda i: vals[i])
lo_idx = min(range(len(vals)), key=lambda i: vals[i])
high = (MONTHS[hi_idx], vals[hi_idx])
low = (MONTHS[lo_idx], vals[lo_idx])
self._render_results(total=total, avg=avg, high=high,
low=low)
except ValueError as e:
[Link]("Invalid Input", str(e))
except Exception as e:
[Link]("Error", str(e))
# Handler — reset(): restores all entries to "0.0" and clears the
result pane
def reset(self) -> None:
for var in [Link]:
[Link]("0.0")
self._render_results(total=None, avg=None, high=None, low=None)
# Helper — _render_results():
def _render_results(self, total, avg, high, low) -> None:
self.res_view.delete("1.0", "end")
if total is None:
self.res_view.insert("1.0",
"Total rainfall: -\n"
"Average per month: -\n"
"Highest month: -\n"
"Lowest month: -\n"
return
self.res_view.insert("1.0",
f"Total rainfall: {total:.2f} inches\n"
f"Average per month: {avg:.2f} inches\n"
f"Highest month: {high[0]} ({high[1]:.2f} inches)\n"
f"Lowest month: {low[0]} ({low[1]:.2f} inches)\n"
# Entry — start the rainfall app
if __name__ == "__main__":
app = RainfallApp()
[Link]()
#!/usr/bin/env python3
# Q7 — Raining (enter monthly rainfall → total, average, highest, lowest)
# Imports — Tkinter UI widgets, dialogs, path handling, and typing for
lists
import tkinter as tk
from tkinter import ttk, messagebox
from pathlib import Path
from typing import List
# Variables — base folder (reserved for future save/load) + shared color
palette
SCRIPT_DIR = Path(__file__).[Link]()
COL_BG="#dbe9f6"; COL_PANEL="#eef6ff"; COL_TEXT="#0b2545";
COL_HINT="#4a6b8a"
COL_ACCENT="#16a34a"; COL_ACCENT_2="#22d3ee"
# Constants — ordered month names used to label inputs and summarize
results
MONTHS = [
"January","February","March","April","May","June",
"July","August","September","October","November","December"
# Class — RainfallApp: collects 12 monthly values, computes totals,
averages, min/max with months
class RainfallApp([Link]):
# __init__: build window, styles, 12 entry boxes, buttons, and result
labels
def __init__(self) -> None:
super().__init__()
[Link]("Q7 — Yearly Rainfall Summary")
[Link]("760x640")
[Link](bg=COL_BG)
# State — StringVars for each month pre-filled to "0.0", and
result StringVars
[Link]: List[[Link]] = [[Link](value="0.0") for
_ in MONTHS]
self.total_var = [Link](value="Total rainfall: -")
self.avg_var = [Link](value="Average per month: -")
self.high_var = [Link](value="Highest month: -")
self.low_var = [Link](value="Lowest month: -")
# Styles — apply clam theme and color mapping for a consistent
look
s = [Link](self)
try: s.theme_use("clam")
except [Link]: pass
[Link](".", background=COL_BG, foreground=COL_TEXT)
[Link]("TFrame", background=COL_BG)
[Link]("TLabel", background=COL_BG, foreground=COL_TEXT)
[Link]("[Link]", background=COL_BG,
foreground=COL_HINT)
[Link]("[Link]", font=("Segoe UI", 15, "bold"),
foreground=COL_ACCENT)
[Link]("[Link]", padding=(10,6), font=("Segoe
UI",10,"bold"))
[Link]("[Link]", background=[("!disabled", COL_ACCENT)],
foreground=[("!disabled", "white")])
[Link]("[Link]", padding=(9,5))
[Link]("[Link]", background=[("!disabled", COL_ACCENT_2)],
foreground=[("!disabled", COL_TEXT)])
# Header — concise instructions and format hint
[Link](self,
text="Enter rainfall (inches, decimals allowed) for
each month.",
style="[Link]").pack(anchor="w", padx=16,
pady=(16,6))
[Link](self,
text="Press Calculate for yearly total, monthly
average, and the months with highest/lowest rainfall.",
style="[Link]").pack(anchor="w", padx=16,
pady=(0,10))
# Grid — two-column layout of month labels and entry boxes (12
rows)
grid = [Link](self); [Link](padx=16, pady=(0,12), fill="x")
for i, month in enumerate(MONTHS):
[Link](grid, text=f"{month}:", width=14,
anchor="w").grid(row=i, column=0, sticky="w", pady=4)
e = [Link](grid, textvariable=[Link][i], width=12)
[Link](row=i, column=1, sticky="w", pady=4)
# second column (for nicer spacing / future use)
[Link](grid, text="", width=2).grid(row=i, column=2)
# Buttons — calculate summary and reset all fields
btns = [Link](self); [Link](pady=(4,10))
[Link](btns, text="Calculate", style="[Link]",
command=[Link]).pack(side="left", padx=8)
[Link](btns, text="Reset", style="[Link]",
command=[Link]).pack(side="left", padx=8)
# Results — totals, average, highest month, lowest month
box = [Link](self); [Link](fill="x", padx=16, pady=(4,14))
res = [Link](box, height=8, wrap="none", relief="flat",
bg=COL_PANEL, fg=COL_TEXT,
insertbackground=COL_TEXT)
[Link](fill="both", expand=True)
self.res_view = res # keep reference for updates
# Binding — Enter triggers calculation for faster testing
[Link]("<Return>", lambda _e: [Link]())
# Initial render — show placeholders
self._render_results(total=None, avg=None, high=None, low=None)
# Handler — calculate(): validates 12 floats (>=0), computes total,
average, min/max and months
def calculate(self) -> None:
try:
vals: List[float] = []
for m, var in zip(MONTHS, [Link]):
v = float([Link]().strip())
if v < 0:
raise ValueError(f"Rainfall for {m} cannot be
negative.")
[Link](v)
total = sum(vals)
avg = total / len(vals)
hi_idx = max(range(len(vals)), key=lambda i: vals[i])
lo_idx = min(range(len(vals)), key=lambda i: vals[i])
high = (MONTHS[hi_idx], vals[hi_idx])
low = (MONTHS[lo_idx], vals[lo_idx])
self._render_results(total=total, avg=avg, high=high,
low=low)
except ValueError as e:
[Link]("Invalid Input", str(e))
except Exception as e:
[Link]("Error", str(e))
# Handler — reset(): restores all entries to "0.0" and clears the
result pane
def reset(self) -> None:
for var in [Link]:
[Link]("0.0")
self._render_results(total=None, avg=None, high=None, low=None)
# Helper — _render_results():
def _render_results(self, total, avg, high, low) -> None:
self.res_view.delete("1.0", "end")
if total is None:
self.res_view.insert("1.0",
"Total rainfall: -\n"
"Average per month: -\n"
"Highest month: -\n"
"Lowest month: -\n"
return
self.res_view.insert("1.0",
f"Total rainfall: {total:.2f} inches\n"
f"Average per month: {avg:.2f} inches\n"
f"Highest month: {high[0]} ({high[1]:.2f} inches)\n"
f"Lowest month: {low[0]} ({low[1]:.2f} inches)\n"
# Entry — start the rainfall app
if __name__ == "__main__":
app = RainfallApp()
[Link]()
Results
Question 8. Analysis of numbers in a list
Question 8 code
#!/usr/bin/env python3
# Q8 — Analysis of numbers in a list (enter 20 numbers → lowest, highest,
total, average)
# Imports — Tkinter UI widgets, dialogs, and typing for lists
import tkinter as tk
from tkinter import ttk, messagebox
from typing import List
# Variables — shared color palette for a consistent UI look
COL_BG="#dbe9f6"; COL_PANEL="#eef6ff"; COL_TEXT="#0b2545";
COL_HINT="#4a6b8a"
COL_ACCENT="#16a34a"; COL_ACCENT_2="#22d3ee"
# Function — parse_numbers(text): returns a list of floats parsed from
commas/spaces/newlines
def parse_numbers(text: str) -> List[float]:
cleaned = [Link](",", " ")
parts = [p for p in [Link]() if p]
return [float(p) for p in parts]
# Class — NumbersAnalysisApp: collects 20 numbers and reports
min/max/sum/average
class NumbersAnalysisApp([Link]):
# __init__: build window, styles, input area, buttons, and result
labels
def __init__(self) -> None:
super().__init__()
[Link]("Q8 — List Analysis (20 numbers)")
[Link]("820x520")
[Link](bg=COL_BG)
# Styles — clam theme and palette on labels/buttons/text
s = [Link](self)
try: s.theme_use("clam")
except [Link]: pass
[Link](".", background=COL_BG, foreground=COL_TEXT)
[Link]("TFrame", background=COL_BG)
[Link]("TLabel", background=COL_BG, foreground=COL_TEXT)
[Link]("[Link]", background=COL_BG,
foreground=COL_HINT)
[Link]("[Link]", font=("Segoe UI", 15, "bold"),
foreground=COL_ACCENT)
[Link]("[Link]", padding=(10,6), font=("Segoe
UI",10,"bold"))
[Link]("[Link]", background=[("!disabled", COL_ACCENT)],
foreground=[("!disabled", "white")])
[Link]("[Link]", padding=(9,5))
[Link]("[Link]", background=[("!disabled", COL_ACCENT_2)],
foreground=[("!disabled", COL_TEXT)])
# Header — instructions and formatting hint
[Link](self,
text="Enter exactly 20 numbers (comma, space, or
newline separated).",
style="[Link]").pack(anchor="w", padx=16,
pady=(16,6))
[Link](self,
text="Example: 5, 12, 7, 9, ... | Press Analyze.",
style="[Link]").pack(anchor="w", padx=16,
pady=(0,10))
# Input — multiline text box for the 20 numbers
box = [Link](self); [Link](fill="both", expand=False,
padx=16, pady=(0,10))
self.input_txt = [Link](box, height=8, wrap="word",
relief="flat",
bg=COL_PANEL, fg=COL_TEXT,
insertbackground=COL_TEXT)
self.input_txt.pack(fill="both", expand=True)
# Buttons — analyze and reset
btns = [Link](self); [Link](pady=(10,10))
[Link](btns, text="Analyze", style="[Link]",
command=[Link]).pack(side="left", padx=8)
[Link](btns, text="Reset", style="[Link]",
command=[Link]).pack(side="left", padx=8)
# Results — lowest, highest, total, average
res = [Link](self); [Link](fill="x", padx=16, pady=(0,14))
self.low_var = [Link](value="Lowest: -")
self.high_var = [Link](value="Highest: -")
self.sum_var = [Link](value="Total: -")
self.avg_var = [Link](value="Average: -")
[Link](res, textvariable=self.low_var, font=("Segoe UI", 12,
"bold")).pack(anchor="w", pady=(0,2))
[Link](res, textvariable=self.high_var, font=("Segoe UI", 12,
"bold")).pack(anchor="w", pady=(0,2))
[Link](res, textvariable=self.sum_var, font=("Segoe UI", 12,
"bold")).pack(anchor="w", pady=(0,2))
[Link](res, textvariable=self.avg_var, font=("Segoe UI", 12,
"bold")).pack(anchor="w", pady=(0,2))
# Binding — Enter key triggers analysis
[Link]("<Return>", lambda _e: [Link]())
# Handler — analyze(): validates 20 numbers, uses min/max/sum,
computes average, updates labels
def analyze(self) -> None:
try:
nums = parse_numbers(self.input_txt.get("1.0", "end"))
if len(nums) != 20:
raise ValueError(f"Expected 20 numbers, found
{len(nums)}.")
lowest = min(nums) # required: min()
highest = max(nums) # required: max()
total = sum(nums) # required: sum()
average = total / len(nums) # required: compute
average
self.low_var.set(f"Lowest: {lowest:.2f}")
self.high_var.set(f"Highest: {highest:.2f}")
self.sum_var.set(f"Total: {total:.2f}")
self.avg_var.set(f"Average: {average:.2f}")
except ValueError as e:
[Link]("Invalid Input", str(e))
except Exception as e:
[Link]("Error", str(e))
# Handler — reset()
def reset(self) -> None:
self.input_txt.delete("1.0", "end")
self.low_var.set("Lowest: -")
self.high_var.set("Highest: -")
self.sum_var.set("Total: -")
self.avg_var.set("Average: -")
# Entry — start the analysis app
if __name__ == "__main__":
app = NumbersAnalysisApp()
[Link]()
Result
Question 9. List items greater than some number
Question 9 code
#!/usr/bin/env python3
# Q9 — List items greater than some number n (enter list + n → show
filtered list)
# Imports — Tkinter UI widgets, dialogs, and typing for lists
import tkinter as tk
from tkinter import ttk, messagebox
from typing import List
# Variables — shared color palette for a consistent UI look
COL_BG="#dbe9f6"; COL_PANEL="#eef6ff"; COL_TEXT="#0b2545";
COL_HINT="#4a6b8a"
COL_ACCENT="#16a34a"; COL_ACCENT_2="#22d3ee"
# Function — parse_numbers(text): converts comma/space/newline separated
text to list of floats
def parse_numbers(text: str) -> List[float]:
cleaned = [Link](",", " ")
parts = [p for p in [Link]() if p]
return [float(p) for p in parts]
# Function — list_greater_than(nums, n): returns elements strictly
greater than n
def list_greater_than(nums: List[float], n: float) -> List[float]:
return [x for x in nums if x > n]
# Class — GreaterThanApp: collects a numeric list and n, displays items
greater than n
class GreaterThanApp([Link]):
# __init__: build window, styles, inputs, action buttons, and output
area
def __init__(self) -> None:
super().__init__()
[Link]("Q9 — Numbers Greater Than n")
[Link]("860x560")
[Link](bg=COL_BG)
# Styles — clam theme + palette across labels/buttons/text
s = [Link](self)
try: s.theme_use("clam")
except [Link]: pass
[Link](".", background=COL_BG, foreground=COL_TEXT)
[Link]("TFrame", background=COL_BG)
[Link]("TLabel", background=COL_BG, foreground=COL_TEXT)
[Link]("[Link]", background=COL_BG,
foreground=COL_HINT)
[Link]("[Link]", font=("Segoe UI", 15, "bold"),
foreground=COL_ACCENT)
[Link]("[Link]", padding=(10,6), font=("Segoe
UI",10,"bold"))
[Link]("[Link]", background=[("!disabled", COL_ACCENT)],
foreground=[("!disabled", "white")])
[Link]("[Link]", padding=(9,5))
[Link]("[Link]", background=[("!disabled", COL_ACCENT_2)],
foreground=[("!disabled", COL_TEXT)])
# Header — concise instructions + sample input hint
[Link](self,
text="Enter a list of numbers and a threshold n, then
show items greater than n.",
style="[Link]").pack(anchor="w", padx=16,
pady=(16,6))
[Link](self,
text="Example list: 3, 8, 12, 5, 19, 7 | Example n:
10",
style="[Link]").pack(anchor="w", padx=16,
pady=(0,10))
# Inputs — multiline list box + single-line threshold entry
top = [Link](self); [Link](fill="x", padx=16, pady=(0,10))
[Link](top, text="Numbers:", width=10).pack(side="left",
padx=(0,6))
self.txt_numbers = [Link](top, height=6, wrap="word",
relief="flat",
bg=COL_PANEL, fg=COL_TEXT,
insertbackground=COL_TEXT)
self.txt_numbers.pack(side="left", fill="x", expand=True)
right = [Link](top); [Link](side="left", fill="y",
padx=(10,0))
[Link](right, text="n:", width=4).pack(anchor="w")
self.var_n = [Link](value="10")
[Link](right, textvariable=self.var_n,
width=14).pack(anchor="w")
# Buttons — analyze, reset
btns = [Link](self); [Link](pady=(10,10))
[Link](btns, text="Show > n", style="[Link]",
command=self.show_greater).pack(side="left", padx=8)
[Link](btns, text="Reset", style="[Link]",
command=[Link]).pack(side="left", padx=8)
# Output — filtered list + simple stats (count)
out = [Link](self); [Link](fill="both", expand=True,
padx=16, pady=(0,14))
[Link](out, text="Result (numbers > n):").pack(anchor="w")
[Link] = [Link](out, height=10, wrap="none", relief="flat",
bg=COL_PANEL, fg=COL_TEXT,
insertbackground=COL_TEXT)
[Link](fill="both", expand=True, pady=(4,0))
self.meta_var = [Link](value="Count: -")
[Link](out, textvariable=self.meta_var).pack(anchor="w",
pady=(6,0))
# Binding — Enter triggers analysis for quick testing
[Link]("<Return>", lambda _e: self.show_greater())
# Handler — show_greater(): parses list and n, filters, sorts for
readability, and renders output
def show_greater(self) -> None:
try:
nums = parse_numbers(self.txt_numbers.get("1.0", "end"))
n = float(self.var_n.get().strip())
greater = list_greater_than(nums, n)
greater_sorted = sorted(greater)
[Link]("1.0", "end")
if greater_sorted:
# display as comma-separated on one line plus a pretty
block
[Link]("1.0", ", ".join(f"{x:g}" for x in
greater_sorted) + "\n")
[Link]("end", "\nPretty list:\n" + "\
n".join(f"• {x:g}" for x in greater_sorted))
else:
[Link]("1.0", "(no numbers greater than n)\
n")
self.meta_var.set(f"Count: {len(greater_sorted)}")
except ValueError:
[Link]("Invalid Input", "Please enter valid
numbers for the list and for n.")
except Exception as e:
[Link]("Error", str(e))
# Handler — reset(): clears input fields and result panel
def reset(self) -> None:
self.txt_numbers.delete("1.0", "end")
self.var_n.set("10")
[Link]("1.0", "end")
self.meta_var.set("Count: -")
# Entry — start the greater-than app
if __name__ == "__main__":
app = GreaterThanApp()
[Link]()
Result