Presentationer Slide 1 / 1

Kap 5.6 · Guidad övning

Skämtregister med GUI, API och databas

Bygg ett skämtregister med tkinter, requests och SQLite.

Mål

Tre tekniker i ett program

  • tkinter skapar gränssnittet.
  • requests hämtar data från ett API.
  • sqlite3 sparar data lokalt.
  • Funktioner kopplar ihop hela flödet.

Projektet

Skämtregister

  1. Hämta ett skämt från ett API.
  2. Visa skämtet i GUI:t.
  3. Spara skämtet i SQLite.
  4. Visa alla sparade skämt.

Helhetsbild

Så samarbetar delarna

Klick i tkinter

startar en Pythonfunktion

requests → API → JSON

hämtar ett nytt skämt

sqlite3 → skamt.db

sparar skämtet permanent

Förberedelse

Installera requests

python3 -m pip install requests

tkinter och sqlite3 ingår normalt i Python.

requests behöver vanligtvis installeras separat.

Hela programmet · del 1

Import, konstanter och databas

import tkinter as tk
import sqlite3
from contextlib import closing

import requests

DB_FIL = "skamt.db"
API_URL = "https://official-joke-api.appspot.com/random_joke"
senaste_skamt = None

def skapa_databas():
    try:
        with closing(sqlite3.connect(DB_FIL)) as conn:
            conn.execute("CREATE TABLE IF NOT EXISTS skamt (...)")
            conn.commit()
    except sqlite3.Error as error:
        skamt_label.config(text=f"Databasfel: {error}")

Hela programmet · del 2

Hämta säkert från API

def hamta_skamt():
    global senaste_skamt
    try:
        response = requests.get(API_URL, timeout=10)
        response.raise_for_status()
        data = response.json()
        senaste_skamt = (data["setup"], data["punchline"])
        skamt_label.config(text=" - ".join(senaste_skamt))
    except (requests.RequestException, KeyError, ValueError):
        senaste_skamt = None
        skamt_label.config(text="Kunde inte hämta ett skämt.")

Hela programmet · del 3

Spara och läsa med stängda anslutningar

def spara_skamt():
    if senaste_skamt is None:
        skamt_label.config(text="Hämta ett skämt först.")
        return

    with closing(sqlite3.connect(DB_FIL)) as conn:
        conn.execute(
            "INSERT INTO skamt (setup, punchline) VALUES (?, ?)",
            senaste_skamt
        )
        conn.commit()

def visa_skamt():
    with closing(sqlite3.connect(DB_FIL)) as conn:
        rader = conn.execute("SELECT * FROM skamt").fetchall()

Databas

Skapa fil och tabell

with closing(sqlite3.connect(DB_FIL)) as conn:
    conn.execute("CREATE TABLE IF NOT EXISTS skamt (...)")

closing() ser till att anslutningen alltid stängs.

CREATE TABLE IF NOT EXISTS skamt (...)

Tabellen skapas bara om den inte redan finns.

Tillstånd

Kom ihåg det senast hämtade skämtet

senaste_skamt = None

None betyder att inget skämt har hämtats ännu.

senaste_skamt = (
    data["setup"],
    data["punchline"]
)

Efter API-anropet innehåller variabeln en tuple med två texter.

global

Funktionen ändrar den yttre variabeln

def hamta_skamt():
    global senaste_skamt

global anger att funktionen ska ändra variabeln utanför funktionen.

Utan raden skulle en tilldelning skapa en lokal variabel med samma namn.

API

Hämta och tolka ett skämt

response = requests.get(API_URL, timeout=10)
response.raise_for_status()
data = response.json()

requests.get() skickar en GET-förfrågan.

raise_for_status() upptäcker HTTP-fel.

json() gör svaret till Pythondata.

Visa resultat

API-data blir text i GUI:t

skamt_label.config(
    text=f"{data['setup']} - {data['punchline']}"
)

setup innehåller skämtets första del.

punchline innehåller poängen.

config() ändrar texten i etiketten.

Spara

Spara bara när ett skämt finns

if senaste_skamt is not None:
    with closing(sqlite3.connect(DB_FIL)) as conn:
        conn.execute(
            "INSERT INTO skamt (setup, punchline) VALUES (?, ?)",
            senaste_skamt
        )
        conn.commit()

? är platshållare för värdena i tuplen.

commit() sparar ändringen permanent.

Läsa

Hämta alla databasrader

with closing(sqlite3.connect(DB_FIL)) as conn:
    rader = conn.execute("SELECT * FROM skamt").fetchall()

fetchall() ger en lista med alla rader.

output = "\n".join(
    [f"{rad[1]} - {rad[2]}" for rad in rader]
)

Varje rad görs om till en text på en egen rad.

GUI

Tre knappar, tre funktioner

tk.Button(
    root,
    text="Hämta skämt",
    command=hamta_skamt
).pack()

Varje knapp får sin funktion via command.

  • hamta_skamt använder API:et.
  • spara_skamt skriver till databasen.
  • visa_skamt läser från databasen.

Prova själv

Interaktiv simulering: skämtregister

Skämtregister

Vad händer i koden?

Programmet har skapat skamt.db och väntar på ett knapptryck.

Hela flödet

Från klick till databas

  1. Användaren klickar på Hämta skämt.
  2. API-svaret visas och sparas i senaste_skamt.
  3. Användaren klickar på Spara skämt.
  4. INSERT lägger in tuplen i databasen.
  5. commit() sparar ändringen.
  6. Visa alla läser och visar raderna.

Testning

Kontrollera fler än normalfallet

  • Klicka på Spara innan något har hämtats.
  • Spara flera olika skämt.
  • Spara samma skämt flera gånger.
  • Starta om programmet och visa sparade skämt.
  • Testa när API:et eller nätverket inte svarar.

Förbättring

API-anropet behöver felhantering

try:
    response = requests.get(url, timeout=10)
    response.raise_for_status()
    data = response.json()
except requests.exceptions.RequestException:
    skamt_label.config(
        text="Kunde inte hämta ett skämt."
    )

Övningens kod använder denna robustare variant för nätverks- och HTTP-fel.

Bygg vidare

Nästa steg

  • lägg till ett sökfält
  • ta bort valda skämt
  • visa skämten i en lista
  • förhindra dubbletter
  • dela upp GUI, API och databas i egna moduler
  • kontrollera tabellen med SQLite Viewer

Sammanfattning

Kom ihåg

  • GUI:t startar funktioner genom knapptryck.
  • API:et levererar data som JSON.
  • En tuple håller det senast hämtade skämtet.
  • SQLite sparar skämt mellan programkörningar.
  • Funktionerna binder ihop GUI, API och databas.
  • Testning och felhantering behövs för ett robust program.

Använd arbetsgången som stöd, men lämna inte in grundversionen oförändrad som Inlämningsuppgift 5.