0% found this document useful (0 votes)
2 views92 pages

Python Assignment 5

The document outlines a Python programming assignment involving the creation of a GUI application for managing text files containing names. It includes two main parts: one for creating and validating two files with specific name count requirements, and another for displaying the first five lines of these files. The assignment emphasizes the importance of adhering to specified rules and provides a structured approach to file handling and user interaction.

Uploaded by

Joshua Kimmich
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views92 pages

Python Assignment 5

The document outlines a Python programming assignment involving the creation of a GUI application for managing text files containing names. It includes two main parts: one for creating and validating two files with specific name count requirements, and another for displaying the first five lines of these files. The assignment emphasizes the importance of adhering to specified rules and provides a structured approach to file handling and user interaction.

Uploaded by

Joshua Kimmich
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

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

You might also like