Desktop apps with Tkinter, automation, professional habits like testing and virtual environments, and five complete projects.
Every code example has a ▶ Run button. Click ✎ Edit & Run, or press L, to open the Python Lab.
D:\python-practice).1import tkinter as tk23root = tk.Tk() # the main window4root.title("BrainBit Infotech Welcome")5root.geometry("360x150")67label = tk.Label(root, text="Namaste, Ranchi!", font=("Segoe UI", 18))8label.pack(pady=10)910def say_hi():11 label.config(text="Welcome to Python GUI!")1213button = tk.Button(root, text="Click me", command=say_hi)14button.pack()1516root.mainloop() # keep the window open
A working preview of what the code shows on Windows. Click the button!
tk.Tk() creates the windowLabel, Button) and place them with pack()command=say_hi: the function runs when clicked (an event)mainloop() waits for clicks and key presses| Widget | Use | Read value |
|---|---|---|
Label | show text / image | |
Entry | one-line input | e.get() |
Text | multi-line input | t.get("1.0", "end") |
Button | run a function | command=f |
Checkbutton | yes / no | BooleanVar |
Radiobutton | one of many | StringVar |
ttk.Combobox | drop-down | cb.get() |
| Layout | How it places widgets |
|---|---|
pack() | one after another (top to bottom) |
grid(row=, column=) | in a table: best for forms |
place(x=, y=) | exact position (rarely needed) |
pack and grid in the same container.1import tkinter as tk2from tkinter import messagebox34def calculate():5 try:6 bill = float(bill_e.get())7 pct = float(pct_e.get())8 people = int(ppl_e.get())9 except ValueError:10 messagebox.showerror("Oops", "Please enter numbers only")11 return12 tip = bill * pct / 10013 result.config(text=f"Tip: Rs {tip:.2f}\nTotal: Rs {bill + tip:.2f}\nEach pays: Rs {(bill + tip) / people:.2f}")1415root = tk.Tk()16root.title("Tip Calculator")17for r, text in enumerate(["Bill (Rs)", "Tip %", "People"]):18 tk.Label(root, text=text).grid(row=r, column=0, sticky="w", padx=8, pady=4)19bill_e, pct_e, ppl_e = tk.Entry(root), tk.Entry(root), tk.Entry(root)20for r, e in enumerate([bill_e, pct_e, ppl_e]):21 e.grid(row=r, column=1, padx=8)22pct_e.insert(0, "10")23ppl_e.insert(0, "1")24tk.Button(root, text="Calculate", command=calculate).grid(row=3, column=0, columnspan=2, pady=6)25result = tk.Label(root, text="", justify="left")26result.grid(row=4, column=0, columnspan=2)27root.mainloop()
Live preview: change the numbers and press Calculate.
entry.get() reads text from an Entryfloat() / int() inside try (Module 13)label.config(text=...) updates the screenmessagebox.showerror shows a pop-up1from openpyxl import Workbook, load_workbook2from openpyxl.styles import Font, PatternFill34wb = Workbook()5ws = wb.active6ws.title = "Class 9A"7ws.append(["Name", "Maths", "Science", "Total"])8for name, m, s in [("Aman", 78, 85), ("Priya", 95, 91), ("Rahul", 65, 58)]:9 r = ws.max_row + 110 ws.append([name, m, s, f"=B{r}+C{r}"]) # real Excel formula11for cell in ws[1]:12 cell.font = Font(bold=True, color="FFFFFF")13 cell.fill = PatternFill("solid", fgColor="3776AB")14ws.column_dimensions["A"].width = 1415wb.save("class_9a.xlsx")1617ws2 = load_workbook("class_9a.xlsx").active18for row in ws2.iter_rows(values_only=True):19 print(row)
1Sheet "Class 9A" (Excel shows formulas as results)2Name | Maths | Science | Total3Aman | 78 | 85 | =B2+C24Priya | 95 | 91 | =B3+C35Rahul | 65 | 58 | =B4+C4
pip install openpyxlWorkbook() = new file, ws.append(list) = new row=Font and PatternFill, then save()1from PIL import Image, ImageDraw, ImageFont23def font(size, bold=False):4 for name in (["arialbd.ttf", "DejaVuSans-Bold.ttf"] if bold else ["arial.ttf", "DejaVuSans.ttf"]):5 try:6 return ImageFont.truetype(name, size)7 except OSError:8 pass9 return ImageFont.load_default()1011def certificate(name, course, grade, filename):12 img = Image.new("RGB", (900, 560), "#fffdf5")13 d = ImageDraw.Draw(img)14 d.rectangle([15, 15, 885, 545], outline="#b07d00", width=8)15 d.rectangle([32, 32, 868, 528], outline="#3776ab", width=2)16 d.text((450, 95), "BRAINBIT INFOTECH PVT LTD, RANCHI", fill="#3776ab", font=font(30, True), anchor="mm")17 d.text((450, 165), "Certificate of Completion", fill="#0b1f3a", font=font(44, True), anchor="mm")18 d.text((450, 235), "This is to certify that", fill="#5b6577", font=font(22), anchor="mm")19 d.text((450, 295), name, fill="#c92a2a", font=font(48, True), anchor="mm")20 d.text((450, 365), f"has completed the {course} course with grade {grade}", fill="#0b1f3a", font=font(24), anchor="mm")21 d.text((450, 470), "Director 08 October 2026", fill="#5b6577", font=font(20), anchor="mm")22 img.save(filename)2324for i, (name, grade) in enumerate([("Priya Kumari", "A+"), ("Aman Oraon", "A")], 1):25 certificate(name, "Python Programming", grade, f"cert_{i}.png")26 print("Created", f"cert_{i}.png", "for", name)
1from pathlib import Path23folder = Path("photos")4photos = sorted(p for p in folder.iterdir() if p.suffix.lower() in (".jpg", ".jpeg"))5for i, p in enumerate(photos, start=1):6 new = folder / f"sarhul_trip_{i:03d}{p.suffix.lower()}"7 print(f"{p.name:<32} -> {new.name}")8 p.rename(new)910print("Now:", sorted(x.name for x in folder.iterdir()))
{i:03d}p.rename(new) renames on disk1from reportlab.lib.pagesizes import A42from reportlab.pdfgen import canvas34c = canvas.Canvas("fee_receipt.pdf", pagesize=A4)5c.setFont("Helvetica-Bold", 18)6c.drawString(72, 780, "BrainBit Infotech Pvt Ltd - Fee Receipt")7c.setFont("Helvetica", 12)8c.drawString(72, 750, "Student: Riya Kumari Course: Python")9c.drawString(72, 730, "Amount paid: Rs 4,500 Date: 08-10-2026")10c.save()
1import smtplib2from email.message import EmailMessage34msg = EmailMessage()5msg["Subject"] = "Monthly report - Class 9A"6msg["From"] = "office@yourschool.in"7msg["To"] = "principal@yourschool.in"8msg.set_content("Please find the report attached.")9with open("class_9a.xlsx", "rb") as f:10 msg.add_attachment(f.read(), maintype="application", subtype="octet-stream", filename="class_9a.xlsx")1112with smtplib.SMTP_SSL("smtp.gmail.com", 465) as s:13 s.login("office@yourschool.in", "APP_PASSWORD") # use an app password14 s.send_message(msg)
roll_01.pdf, roll_02.pdf ...photo_01.jpggrid for labels and entriesroot.bind("<Return>", ...)Interactive preview of the finished window.
1import tkinter as tk2from tkinter import messagebox34def calculate():5 try:6 bill = float(bill_e.get())7 pct = float(pct_e.get())8 people = int(ppl_e.get())9 except ValueError:10 messagebox.showerror("Oops", "Please enter numbers only")11 return12 tip = bill * pct / 10013 result.config(text=f"Tip: Rs {tip:.2f}\nTotal: Rs {bill + tip:.2f}\nEach pays: Rs {(bill + tip) / people:.2f}")1415root = tk.Tk()16root.title("Tip Calculator")17for r, text in enumerate(["Bill (Rs)", "Tip %", "People"]):18 tk.Label(root, text=text).grid(row=r, column=0, sticky="w", padx=8, pady=4)19bill_e, pct_e, ppl_e = tk.Entry(root), tk.Entry(root), tk.Entry(root)20for r, e in enumerate([bill_e, pct_e, ppl_e]):21 e.grid(row=r, column=1, padx=8)22pct_e.insert(0, "10")23ppl_e.insert(0, "1")24tk.Button(root, text="Calculate", command=calculate).grid(row=3, column=0, columnspan=2, pady=6)25def clear():26 for e in (bill_e, pct_e, ppl_e):27 e.delete(0, tk.END)28 result.config(text="")2930tk.Button(root, text="Clear", command=clear).grid(row=5, column=0, columnspan=2)31root.bind("<Return>", lambda event: calculate())32result = tk.Label(root, text="", justify="left")33result.grid(row=4, column=0, columnspan=2)34root.mainloop()
show="*")state="disabled" after 3Try it: admin / Ranchi@123 (and some wrong attempts).
1import tkinter as tk23attempts = 045def login():6 global attempts7 if user.get().strip().lower() == "admin" and pwd.get() == "Ranchi@123":8 msg.config(text="Login successful! Opening dashboard...", fg="green")9 return10 attempts += 111 if attempts >= 3:12 msg.config(text="Too many attempts. Locked.", fg="red")13 btn.config(state="disabled")14 else:15 msg.config(text=f"Wrong username or password ({3 - attempts} left)", fg="red")1617root = tk.Tk()18root.title("Staff Login")19tk.Label(root, text="Username").grid(row=0, column=0, padx=8, pady=6)20tk.Label(root, text="Password").grid(row=1, column=0, padx=8)21user = tk.Entry(root)22pwd = tk.Entry(root, show="*")23user.grid(row=0, column=1, padx=8)24pwd.grid(row=1, column=1, padx=8)25btn = tk.Button(root, text="Login", command=login)26btn.grid(row=2, column=0, columnspan=2, pady=8)27msg = tk.Label(root, text="")28msg.grid(row=3, column=0, columnspan=2)29root.mainloop()
root.after(ms, func) to update the screen regularly.tick() updates the label and schedules itself with after(100, tick)Interactive preview: Start, Stop and Reset.
1import tkinter as tk2import time34running = False5start = 0.06elapsed = 0.078def tick():9 if running:10 shown = elapsed + (time.time() - start)11 label.config(text=f"{shown:.1f} s")12 root.after(100, tick) # call again in 100 ms1314def toggle():15 global running, start, elapsed16 if running:17 elapsed += time.time() - start18 running = False19 btn.config(text="Start")20 else:21 start = time.time()22 running = True23 btn.config(text="Stop")24 tick()2526def reset():27 global running, elapsed28 running, elapsed = False, 0.029 label.config(text="0.0 s")30 btn.config(text="Start")3132root = tk.Tk()33root.title("Exam Stopwatch")34label = tk.Label(root, text="0.0 s", font=("Consolas", 40))35label.pack(padx=20, pady=10)36btn = tk.Button(root, text="Start", width=8, command=toggle)37btn.pack(side="left", padx=20, pady=10)38tk.Button(root, text="Reset", width=8, command=reset).pack(side="right", padx=20)39root.mainloop()
batch.csv with csv.DictReadercertificate() function idea from the slidescertificates/roll_XX.png1from PIL import Image, ImageDraw, ImageFont23def font(size, bold=False):4 for name in (["arialbd.ttf", "DejaVuSans-Bold.ttf"] if bold else ["arial.ttf", "DejaVuSans.ttf"]):5 try:6 return ImageFont.truetype(name, size)7 except OSError:8 pass9 return ImageFont.load_default()1011def certificate(name, course, grade, filename):12 img = Image.new("RGB", (900, 560), "#fffdf5")13 d = ImageDraw.Draw(img)14 d.rectangle([15, 15, 885, 545], outline="#b07d00", width=8)15 d.rectangle([32, 32, 868, 528], outline="#3776ab", width=2)16 d.text((450, 95), "BRAINBIT INFOTECH PVT LTD, RANCHI", fill="#3776ab", font=font(30, True), anchor="mm")17 d.text((450, 165), "Certificate of Completion", fill="#0b1f3a", font=font(44, True), anchor="mm")18 d.text((450, 235), "This is to certify that", fill="#5b6577", font=font(22), anchor="mm")19 d.text((450, 295), name, fill="#c92a2a", font=font(48, True), anchor="mm")20 d.text((450, 365), f"has completed the {course} course with grade {grade}", fill="#0b1f3a", font=font(24), anchor="mm")21 d.text((450, 470), "Director 08 October 2026", fill="#5b6577", font=font(20), anchor="mm")22 img.save(filename)2324import csv25from pathlib import Path2627out = Path("certificates")28out.mkdir(exist_ok=True)29count = 030with open("batch.csv", encoding="utf-8") as f:31 for row in csv.DictReader(f):32 path = out / f"roll_{int(row['roll']):02d}.png"33 certificate(row["name"], row["course"], row["grade"], path)34 count += 135 print("Saved", path)36print(count, "certificates ready to print!")
roll_XX.jpg1from pathlib import Path2from openpyxl import Workbook, load_workbook34mapping = {"IMG_20260915_101233.jpg": 7, "IMG_20260915_101245.jpg": 12, "IMG_20260916_083010.jpg": 3}5folder = Path("photos")6wb = Workbook()7ws = wb.active8ws.append(["Old name", "New name"])9for old, roll in mapping.items():10 src = folder / old11 if src.exists():12 dst = folder / f"roll_{roll:02d}.jpg"13 src.rename(dst)14 ws.append([old, dst.name])15wb.save("rename_log.xlsx")1617for row in load_workbook("rename_log.xlsx").active.iter_rows(values_only=True):18 print(f"{row[0]:<26} {row[1]}")19print("Folder now:", sorted(p.name for p in folder.iterdir()))
tk.Tk(), widgets (Label, Entry, Button), mainloop()pack() or grid(); do not mix in one containercommand=function, bind("<Return>", ...), after(ms, f)entry.get(); update with label.config(text=...)Which call keeps a Tkinter window open?
How do you read text typed in an Entry e?
Which library edits .xlsx files?
requirements.txt and runs in a virtual environmentsource .venv/bin/activate. In VS Code, choose the interpreter .venv (bottom-right corner).python -m venv .venv creates the environment(.venv)pip install now installs only into this projectpip freeze > requirements.txt records exact versionspip install -r requirements.txt to get the same setupdef CalcFee(a,b,c):
x=a*b
if c==True: x=x-500
return xdef calculate_fee(months, monthly_fee, has_scholarship):
fee = months * monthly_fee
if has_scholarship:
fee -= 500
return fee| PEP 8 rule | Example |
|---|---|
| snake_case names | total_marks |
| CapWords classes | class FeeReceipt |
| UPPER constants | GST_RATE = 18 |
| 4-space indent, spaces around = | x = a + b |
| lines under ~80-100 chars |
pip install black ruff, then black . formats and ruff check . finds problems.1"""gst.py - GST helpers for the billing app."""23RATES: dict[str, float] = {"food": 5, "books": 0, "electronics": 18, "clothes": 12}456def gst_amount(price: float, category: str) -> float:7 """Return the GST on one item, rounded to 2 decimals."""8 if price < 0:9 raise ValueError("price cannot be negative")10 rate = RATES.get(category)11 if rate is None:12 raise KeyError(f"unknown category: {category}")13 return round(price * rate / 100, 2)141516def bill_total(items: list[tuple[str, float, int]]) -> float:17 """items = [(category, price, qty), ...]; returns the grand total with GST."""18 total = 0.019 for category, price, qty in items:20 total += (price + gst_amount(price, category)) * qty21 return round(total, 2)222324print(gst_amount(1299, "electronics"))25print(bill_total([("food", 120, 3), ("clothes", 899, 1)]))26print(gst_amount.__annotations__)
mypy checks the whole project for type mistakes1from mypy import api23open("buggy.py", "w").write('''4def area(length: float, breadth: float) -> float:5 return length * breadth67print(area("12", 10))8total: int = area(3.5, 2)9''')10report, errors, status = api.run(["buggy.py", "--no-error-summary"])11print(report)
pip install mypymypy buggy.py in the terminal (here we call it from Python)1import logging23logging.basicConfig(4 filename="bank.log", level=logging.INFO,5 format="%(asctime)s %(levelname)-7s %(name)s: %(message)s", datefmt="%d-%m-%Y %H:%M",6)7log = logging.getLogger("atm")89def withdraw(balance, amount):10 log.info("withdraw requested: %s", amount)11 if amount > balance:12 log.warning("insufficient balance: has %s, asked %s", balance, amount)13 return balance14 log.debug("this DEBUG line is hidden at INFO level")15 return balance - amount1617b = withdraw(5000, 1200)18b = withdraw(b, 9000)19try:20 1 / 021except ZeroDivisionError:22 log.exception("unexpected error while printing receipt")23print("Done. Check bank.log")
108-10-2026 00:09 INFO atm: withdraw requested: 1200208-10-2026 00:09 INFO atm: withdraw requested: 9000308-10-2026 00:09 WARNING atm: insufficient balance: has 3800, asked 9000408-10-2026 00:09 ERROR atm: unexpected error while printing receipt5Traceback (most recent call last):6 File "main.py", line 20, in <module>7 1 / 08 ~~^~~9ZeroDivisionError: division by zero
| Level | Use for |
|---|---|
| DEBUG | details while developing |
| INFO | normal events (login, payment ok) |
| WARNING | something odd but handled |
| ERROR | an operation failed |
| CRITICAL | the app cannot continue |
1import pytest23# runs the same as typing "pytest -q" in the terminal4pytest.main(["-q", "-p", "no:cacheprovider", "test_gst.py"])
1import pytest2from gst import gst_amount, bill_total345def test_electronics_rate():6 assert gst_amount(1000, "electronics") == 180789def test_books_are_exempt():10 assert gst_amount(450, "books") == 0111213def test_bill_total():14 items = [("food", 100, 2), ("electronics", 500, 1)]15 assert bill_total(items) == 800.0 # 210 + 590161718def test_negative_price_rejected():19 with pytest.raises(ValueError):20 gst_amount(-5, "food")212223def test_unknown_category():24 with pytest.raises(KeyError):25 gst_amount(100, "gold")26
pytest after every change: if something breaks, you know immediately.1git init # start tracking this folder2git add . # stage all changes3git commit -m "Add GST module" # save a version4git log --oneline # history56git branch feature-receipts # work on something new7git switch feature-receipts89git remote add origin https://github.com/yourname/fee_app.git10git push -u origin main # upload to GitHub
1.venv/2__pycache__/3*.log4.env # passwords and API keys stay private
.env file (listed in .gitignore) and read them with os.environ.def late_fee(days: int) -> intmain() function and if __name__ == "__main__":1"""fees.py - fee rules for BrainBit Infotech Pvt Ltd."""2from datetime import date34MONTHLY_FEE: int = 15005LATE_FEE_PER_DAY: int = 206SIBLING_DISCOUNT: float = 0.10789def late_fee(days_late: int) -> int:10 """Late fee for paying after the due date."""11 return max(0, days_late) * LATE_FEE_PER_DAY121314def fee_due(months: int, has_sibling: bool = False) -> float:15 """Fee for a number of months, with sibling discount."""16 fee = months * MONTHLY_FEE17 return fee * (1 - SIBLING_DISCOUNT) if has_sibling else float(fee)181920def days_between(start: date, end: date) -> int:21 """Whole days from start to end."""22 return (end - start).days232425def main() -> None:26 late = days_between(date(2026, 10, 10), date(2026, 10, 18))27 total = fee_due(3, has_sibling=True) + late_fee(late)28 print(f"3 months with sibling discount + {late} days late = Rs {total:,.2f}")293031if __name__ == "__main__":32 main()
calc.py: percentage() and grade()test_calc.py: one test per rulepytest.raises for invalid input@pytest.mark.parametrize to test many grades at once1import pytest2from calc import percentage, grade34def test_percentage():5 assert percentage(412, 500) == 82.467@pytest.mark.parametrize("pct, expected", [(95, "A+"), (90, "A+"), (80, "A"), (61, "B"), (33, "C"), (32.9, "F")])8def test_grade(pct, expected):9 assert grade(pct) == expected1011@pytest.mark.parametrize("o, t", [(10, 0), (-1, 100), (120, 100)])12def test_invalid(o, t):13 with pytest.raises(ValueError):14 percentage(o, t)
1import pytest2pytest.main(["-q", "-p", "no:cacheprovider", "test_calc.py"])
basicConfig to write to atm.loglog.exception inside except1import logging2logging.basicConfig(filename="atm.log", level=logging.INFO, format="%(levelname)-7s %(message)s")3log = logging.getLogger("atm")45accounts = {"XX4521": 8000, "XX7730": 1200}67def withdraw(acc: str, amount: int) -> None:8 try:9 bal = accounts[acc]10 if amount > bal:11 log.warning("%s refused: balance %s < %s", acc, bal, amount)12 return13 accounts[acc] = bal - amount14 log.info("%s withdrew %s, balance %s", acc, amount, accounts[acc])15 except KeyError:16 log.exception("unknown account %s", acc)1718for acc, amt in [("XX4521", 2000), ("XX7730", 5000), ("XX0000", 100), ("XX4521", 500)]:19 withdraw(acc, amt)20print(open("atm.log").read())
grade() function and constants1l=[["Aman",78,85],["Riya",25,40],["Om",90,95]]2for i in range(len(l)):3 a=(l[i][1]+l[i][2])/24 if a>=90:g="A+"5 elif a>=75:g="A"6 elif a>=33:g="PASS"7 else:g="FAIL"8 print(l[i][0]+" "+str(a)+" "+g)
1PASS_MARK = 332STUDENTS = [("Aman", 78, 85), ("Riya", 25, 40), ("Om", 90, 95)]345def average(*marks: int) -> float:6 return sum(marks) / len(marks)789def grade(avg: float) -> str:10 if avg >= 90:11 return "A+"12 if avg >= 75:13 return "A"14 if avg >= PASS_MARK:15 return "PASS"16 return "FAIL"171819for name, maths, science in STUDENTS:20 avg = average(maths, science)21 print(f"{name} {avg} {grade(avg)}")
pathlib to create folders and files1from pathlib import Path23def scaffold(name: str) -> Path:4 root = Path(name)5 files = {6 f"{name}/__init__.py": "",7 f"{name}/utils.py": 'def hello() -> str:\n return "hello"\n',8 "tests/test_utils.py": f"from {name}.utils import hello\n\ndef test_hello():\n assert hello() == 'hello'\n",9 "main.py": f"from {name}.utils import hello\nprint(hello())\n",10 "requirements.txt": "pytest\n",11 "README.md": f"# {name}\n\npython -m venv .venv\npip install -r requirements.txt\npython main.py\n",12 ".gitignore": ".venv/\n__pycache__/\n*.log\n.env\n",13 }14 for rel, text in files.items():15 p = root / rel16 p.parent.mkdir(parents=True, exist_ok=True)17 p.write_text(text)18 return root1920def tree(p: Path, indent: str = "") -> None:21 for child in sorted(p.iterdir(), key=lambda c: (c.is_file(), c.name)):22 print(indent + ("|-- " if indent else "") + child.name + ("/" if child.is_dir() else ""))23 if child.is_dir():24 tree(child, indent + " ")2526project = scaffold("fee_app")27print("fee_app/")28tree(project, " ")
python -m venv .venv + activate; pip freeze > requirements.txtdef f(x: int) -> str; check with mypylogging with levels, formats and files instead of printpytest: small test_ functions with assert, raises, parametrizeCommand to create a virtual environment?
pytest finds tests in functions named...
Which should NOT be pushed to GitHub?
students.csv at startreport.csv and save on exit| File | Purpose |
|---|---|
main.py | menu and program flow |
students.csv | saved data |
report.csv | exported results |
Student class and its methods1roll,name,percent,grade21,Aman Oraon,78.3,A32,Priya Kumari,91.3,A+43,Rahul Mahto,64.3,B54,Sneha Toppo,86.3,A65,Kiran Lakra,83.0,A
1import csv23SUBJECTS = ["maths", "science", "english"]45class Student:6 def __init__(self, roll, name, marks):7 self.roll, self.name, self.marks = int(roll), name, {s: int(marks[s]) for s in SUBJECTS}8 def percent(self):9 return sum(self.marks.values()) / len(SUBJECTS)10 def grade(self):11 p = self.percent()12 return "A+" if p >= 90 else "A" if p >= 75 else "B" if p >= 60 else "C" if p >= 33 else "F"1314def load(path="students.csv"):15 with open(path, encoding="utf-8") as f:16 return [Student(r["roll"], r["name"], r) for r in csv.DictReader(f)]1718def save(students, path="students.csv"):19 with open(path, "w", newline="", encoding="utf-8") as f:20 w = csv.writer(f)21 w.writerow(["roll", "name"] + SUBJECTS)22 for s in students:23 w.writerow([s.roll, s.name] + [s.marks[x] for x in SUBJECTS])2425def ask_mark(subject):26 while True:27 try:28 m = int(input(f" {subject} marks: "))29 if 0 <= m <= 100:30 return m31 except ValueError:32 pass33 print(" enter 0-100")3435students = load()36print(f"Loaded {len(students)} students")37while True:38 ch = input("\n1 Add 2 Rank list 3 Topper 4 Export & exit: ")39 if ch == "1":40 name = input(" Name: ").strip().title()41 roll = max(s.roll for s in students) + 142 students.append(Student(roll, name, {s: ask_mark(s) for s in SUBJECTS}))43 print(f" Added roll {roll}")44 elif ch == "2":45 for i, s in enumerate(sorted(students, key=lambda s: -s.percent()), 1):46 print(f" {i}. {s.name:<13} {s.percent():5.1f}% {s.grade()}")47 elif ch == "3":48 t = max(students, key=lambda s: s.percent())49 print(f" Topper: {t.name} ({t.percent():.1f}%)")50 print(" Subject averages:", {x: round(sum(s.marks[x] for s in students) / len(students), 1) for x in SUBJECTS})51 elif ch == "4":52 save(students)53 with open("report.csv", "w", newline="") as f:54 w = csv.writer(f)55 w.writerow(["roll", "name", "percent", "grade"])56 for s in students:57 w.writerow([s.roll, s.name, round(s.percent(), 1), s.grade()])58 print(" Saved students.csv and report.csv. Bye!")59 break
expenses.db| File | Purpose |
|---|---|
tracker.py | database + menu |
expenses.db | SQLite database |
report.png | chart for the month |
add(), month_summary() and budget_alerts()1import sqlite32import matplotlib.pyplot as plt34con = sqlite3.connect("expenses.db")5con.execute("DROP TABLE IF EXISTS expenses")6con.execute("CREATE TABLE expenses (id INTEGER PRIMARY KEY, day TEXT, category TEXT, amount REAL)")7BUDGET = {"Food": 5000, "Transport": 1500, "Shopping": 2000}89def add(day, category, amount):10 with con:11 con.execute("INSERT INTO expenses (day, category, amount) VALUES (?, ?, ?)", (day, category.title(), amount))1213def month_summary(month):14 return con.execute("SELECT category, SUM(amount) FROM expenses WHERE day LIKE ? GROUP BY category ORDER BY 2 DESC", (month + "%",)).fetchall()1516def budget_alerts(summary):17 for cat, total in summary:18 if cat in BUDGET and total > BUDGET[cat]:19 print(f" ALERT: {cat} Rs {total:,.0f} is over budget Rs {BUDGET[cat]:,}")2021for row in [("2026-10-01", "Food", 1450), ("2026-10-02", "Transport", 300), ("2026-10-03", "Bills", 1180), ("2026-10-04", "Food", 2600),22 ("2026-10-05", "Shopping", 1899), ("2026-10-06", "Transport", 650), ("2026-10-07", "Food", 1200)]:23 add(*row)24for _ in range(2):25 add("2026-10-08", input("Category: "), float(input("Amount: ")))2627summary = month_summary("2026-10")28print("October 2026")29for cat, total in summary:30 print(f" {cat:<10} Rs {total:>8,.2f}")31print(f" {'TOTAL':<10} Rs {sum(t for _, t in summary):>8,.2f}")32budget_alerts(summary)3334plt.figure(figsize=(5, 3.2))35plt.pie([t for _, t in summary], labels=[c for c, _ in summary], autopct="%1.0f%%", startangle=90)36plt.title("October spending")37plt.tight_layout()38plt.savefig("report.png", dpi=80)
questions.json (easy to add more)highscore.json| File | Purpose |
|---|---|
quiz.py | game logic |
questions.json | question bank |
highscore.json | best score so far |
load_questions(), ask() and update_high_score()1import json, random, os23def load_questions(path="questions.json"):4 with open(path, encoding="utf-8") as f:5 qs = json.load(f)6 random.shuffle(qs)7 return qs89def ask(n, item):10 opts = item["options"][:]11 random.shuffle(opts)12 print(f"\nQ{n}. {item['q']}")13 for i, o in enumerate(opts, 1):14 print(f" {i}) {o}")15 while True:16 try:17 pick = opts[int(input(" Your answer: ")) - 1]18 break19 except (ValueError, IndexError):20 print(" Type an option number")21 ok = pick == item["answer"]22 print(" Correct!" if ok else f" Wrong, answer: {item['answer']}")23 return ok2425def update_high_score(score, path="highscore.json"):26 best = json.load(open(path))["best"] if os.path.exists(path) else 027 if score > best:28 json.dump({"best": score}, open(path, "w"))29 print(f"NEW HIGH SCORE: {score}!")30 else:31 print(f"High score is still {best}")3233questions = load_questions()34score = sum(ask(i, q) for i, q in enumerate(questions, 1))35print(f"\nScore: {score}/{len(questions)} ({score / len(questions):.0%})")36update_high_score(score)
| File | Purpose |
|---|---|
library.py | Library class + demo |
library.db | SQLite database |
Library class methodsLibraryError for rule breaks1import sqlite32from datetime import date, timedelta34class LibraryError(Exception):5 pass67class Library:8 LOAN_DAYS, MAX_BOOKS, FINE_PER_DAY = 14, 2, 2910 def __init__(self, path=":memory:"):11 self.db = sqlite3.connect(path)12 self.db.executescript("""13 CREATE TABLE books (id INTEGER PRIMARY KEY, title TEXT, author TEXT);14 CREATE TABLE loans (book_id INT, member TEXT, issued TEXT, due TEXT, returned TEXT, fine INT DEFAULT 0);""")1516 def add_book(self, title, author):17 self.db.execute("INSERT INTO books (title, author) VALUES (?, ?)", (title, author))1819 def _open_loans(self, where, arg):20 return self.db.execute(f"SELECT COUNT(*) FROM loans WHERE {where} = ? AND returned IS NULL", (arg,)).fetchone()[0]2122 def issue(self, book_id, member, on):23 if self._open_loans("book_id", book_id):24 raise LibraryError(f"book {book_id} is already issued")25 if self._open_loans("member", member) >= self.MAX_BOOKS:26 raise LibraryError(f"{member} already has {self.MAX_BOOKS} books")27 due = on + timedelta(days=self.LOAN_DAYS)28 self.db.execute("INSERT INTO loans (book_id, member, issued, due) VALUES (?, ?, ?, ?)", (book_id, member, on.isoformat(), due.isoformat()))29 print(f"Issued book {book_id} to {member}, due {due:%d %b}")3031 def give_back(self, book_id, on):32 due = date.fromisoformat(self.db.execute("SELECT due FROM loans WHERE book_id = ? AND returned IS NULL", (book_id,)).fetchone()[0])33 fine = max(0, (on - due).days) * self.FINE_PER_DAY34 self.db.execute("UPDATE loans SET returned = ?, fine = ? WHERE book_id = ? AND returned IS NULL", (on.isoformat(), fine, book_id))35 print(f"Book {book_id} returned on {on:%d %b}. Fine: Rs {fine}")3637 def overdue(self, today):38 q = "SELECT b.title, l.member, l.due FROM loans l JOIN books b ON b.id = l.book_id WHERE l.returned IS NULL AND l.due < ?"39 return self.db.execute(q, (today.isoformat(),)).fetchall()4041lib = Library()42for t, a in [("Wings of Fire", "A.P.J. Abdul Kalam"), ("Godan", "Premchand"), ("Malgudi Days", "R.K. Narayan"), ("The Discovery of India", "Jawaharlal Nehru")]:43 lib.add_book(t, a)44lib.issue(1, "Asha", date(2026, 9, 10))45lib.issue(2, "Asha", date(2026, 9, 20))46for action in [lambda: lib.issue(3, "Asha", date(2026, 9, 21)), lambda: lib.issue(1, "Vikas", date(2026, 9, 22))]:47 try:48 action()49 except LibraryError as e:50 print("Refused:", e)51lib.issue(3, "Vikas", date(2026, 10, 1))52lib.give_back(1, date(2026, 10, 2))53print("Overdue on 08 Oct:", lib.overdue(date(2026, 10, 8)))54print("Fines collected: Rs", lib.db.execute("SELECT SUM(fine) FROM loans").fetchone()[0])
?city=)| File | Purpose |
|---|---|
app.py | Flask routes + API call |
templates/ | HTML pages (inline here) |
requirements.txt | flask, requests |
python app.py and open http://127.0.0.1:5000. The previews show the pages for Ranchi and Netarhat (sample API data).city, call the API, render the templatepip install flask requests1import requests2from flask import Flask, request, render_template_string34app = Flask(__name__)5CITIES = {"ranchi": (23.34, 85.31), "jamshedpur": (22.80, 86.18), "dhanbad": (23.80, 86.43), "netarhat": (23.47, 84.27)}6PAGE = """<h2>Jharkhand Weather Dashboard</h2>7<form><select name="city">{% for c in cities %}<option {{ "selected" if c == city }}>{{ c }}</option>{% endfor %}</select>8<button>Show</button></form>9{% if w %}<h3>{{ city.title() }}: {{ w.current.temperature_2m }} °C, humidity {{ w.current.relative_humidity_2m }}%</h3>10<table border=1 cellpadding=6><tr><th>Day</th><th>Min</th><th>Max</th><th>Rain (mm)</th></tr>11{% for d, lo, hi, r in days %}<tr><td>{{ d }}</td><td>{{ lo }}</td><td>{{ hi }}</td><td>{{ r }}{{ " ☔" if r > 1 }}</td></tr>{% endfor %}</table>12{% else %}<p style="color:red">{{ error }}</p>{% endif %}"""1314def get_weather(city):15 lat, lon = CITIES[city]16 r = requests.get("https://api.open-meteo.com/v1/forecast", timeout=10, params={17 "latitude": lat, "longitude": lon, "current": "temperature_2m,relative_humidity_2m",18 "daily": "temperature_2m_max,temperature_2m_min,precipitation_sum", "timezone": "Asia/Kolkata"})19 r.raise_for_status()20 return r.json()2122@app.route("/")23def home():24 city = request.args.get("city", "ranchi")25 try:26 w = get_weather(city)27 d = w["daily"]28 days = zip(d["time"], d["temperature_2m_min"], d["temperature_2m_max"], d["precipitation_sum"])29 return render_template_string(PAGE, cities=CITIES, city=city, w=w, days=days)30 except (KeyError, requests.exceptions.RequestException) as e:31 return render_template_string(PAGE, cities=CITIES, city=city, w=None, error=f"Could not load weather: {e}")3233if __name__ == "__main__":34 app.run(debug=True)
Best first step for a new project?
Where should the quiz questions live?
Which capstone uses an API?
You finished Pro Skills & Capstone Projects: 3 modules and 15 practice examples.
D:\python-practice\part5