Fönsterhanteraren
Ditt program får ett ansikte
Alla program du skrivit hittills pratar med användaren via terminalen. Text ut, text in. Det fungerar, men det kräver att användaren vet vad en terminal är. De flesta vet inte det, och de behöver inte veta det.
Biblioteket som ger Python grafiska fönster heter tkinter. Det är inbyggt, ingen installation krävs, och det är byggt kring något du redan kan: funktioner. Varje knapp kopplas till en funktion, och när användaren klickar körs den.
Händelsestyrt, programmet som väntar
Men något mer förändras, och det är större än fönstret. Ända sedan din första rad Python har koden körts uppifrån och ned, en gång, och sedan tagit slut. I Anropsstacken lärde du dig att funktioner kan bryta den raka linjen och hoppa runt, men programmet läste fortfarande dina rader i tur och ordning och bestämde själv vad som hände.
Ett grafiskt program vänder på det. Det kör igenom sin uppställning en gång, bygger fönstret, och sedan stannar det och väntar. Det gör ingenting förrän användaren gör något. Klickar de en knapp körs en funktion, sedan väntar programmet igen. Det är inte längre din kod som bestämmer ordningen, det är användaren.
Programmet startar
Bygger fönster och knappar
|
v
väntar <───────────┐
| │
användaren klickar │
| │
funktionen körs ────┘ Den sortens programmering heter Händelsestyrd programmering Ett program som väntar på att användaren ska göra något i stället för att köras uppifrån och ned en gång. Ett klick eller en tangenttryckning är en händelse som utlöser en funktion. Grunden för grafiska gränssnitt. (event-driven). Frågan “när körs den här koden?” har fått ett nytt svar. Förr var det “när turen kommer till raden”. Nu är det “när användaren gör något”. Det är ett skifte i hur du tänker på ett program, inte bara i hur det ser ut, och resten av temat bygger på det.
Fyra recept
Ett tkinter-program byggs av några få byggstenar som återkommer i varje fönster du någonsin skriver. Precis som mönstren på Mönsterkortet lär du dig dem en gång och sätter sedan ihop dem på nya sätt. Här är de fyra du behöver: rotfönstret, etiketten, knappen och hopsättningen.
Rotfönstret
import tkinter as tk
root = tk.Tk()
root.title("Mitt program")
root.geometry("400x300")
root.mainloop() Kör det i VS Code. Ett tomt fönster öppnas och stannar. Det är mainloop() som håller det vid liv, ta bort den raden och fönstret stängs innan du hinner se det.
Etiketten
En Widget Ett synligt element i ett GUI: en etikett, en knapp, ett textfält. Allt du ser i ett tkinter-fönster är en widget. (Label) visar text i fönstret:
import tkinter as tk
root = tk.Tk()
root.title("Etikett")
root.geometry("400x300")
etikett = tk.Label(root, text="Hej från Python!", font=("Arial", 18))
etikett.pack(pady=40)
root.mainloop() root som första argument talar om att etiketten tillhör huvudfönstret. .pack(pady=40) placerar den med lite luft runt.
Knappen
En knapp (Button) kopplas till en funktion med command=:
import tkinter as tk
def knappen_klickades():
print("Klickad!")
root = tk.Tk()
root.title("Knapp")
root.geometry("400x300")
knapp = tk.Button(root, text="Tryck här", command=knappen_klickades)
knapp.pack(pady=20)
root.mainloop() Kör det och klicka på knappen några gånger, men titta i terminalen medan du gör det. Klickad! skrivs ut en gång per klick. Fönstret står stilla och väntar, och varje utskrift är ett bevis på att funktionen körs i det ögonblick du klickar, inte innan. Det är händelsestyrningen gjord synlig med en enda print().
Vill du se det ännu tydligare, sätt en brytpunkt inuti funktionen och kör i debuggern. Programmet startar, fönstret öppnas, och ingenting pausar. Först när du klickar stannar debuggern inne i funktionen. En brytpunkt som bara slår till vid klicket är händelsestyrningen fångad på bar gärning.
Lägg också märke till command=knappen_klickades, utan parenteser. Det är samma sak du provade i Din första funktion: utan parenteser refererar du bara till funktionen, du ber den inte att köra. Det är precis vad du vill här. Du lämnar funktionen till knappen, och knappen kör den när den klickas.
Funktionen måste dessutom definieras innan knappen skapas. När raden tk.Button(...) körs slår Python upp namnet direkt, och finns det inte ännu blir det NameError.
Sätt ihop
Knappen ändrar etikettens text med .config():
import tkinter as tk
def ändra_text():
etikett.config(text="Du klickade!")
root = tk.Tk()
root.title("Komplett")
root.geometry("400x300")
etikett = tk.Label(root, text="Ingenting har hänt än.", font=("Arial", 16))
etikett.pack(pady=40)
knapp = tk.Button(root, text="Klicka mig", command=ändra_text, padx=20, pady=10)
knapp.pack()
root.mainloop() Vart fönster du bygger följer samma form: skapa fönstret, skapa Widget Ett synligt element i ett GUI: en etikett, en knapp, ett textfält. Allt du ser i ett tkinter-fönster är en widget. , koppla funktionerna, starta Händelseloop Den slinga som håller ett grafiskt program vid liv: den väntar, tar emot händelser som klick och tangenttryck, och kör den funktion som är kopplad till händelsen. I tkinter startas den med mainloop(), som därför alltid är sista raden. med mainloop(). Allt i det här området vilar på den strukturen.
Lägg märke till att ändra_text() uppdaterar gränssnittet men inte lämnar tillbaka något. Det är Presentatören från Funktionen svarar, en funktion som visar i stället för att svara. I nästa del ser du hur ett program håller reda på sitt tillstånd och låter Presentatören hålla gränssnittet i takt med datan.
Alla widgetar hittills har staplats med .pack(), en under den andra. Vill du lägga dem i rad i stället, till exempel två knappar bredvid varandra, skriver du .pack(side="left"). Det räcker långt, och .pack() används genom hela kursen.
Uppgift: Ditt första fönster
Skapa filen fonster.py och kör den i VS Code.
Bygg ett fönster med din applikations namn som titel och storleken
500x350.Lägg till en etikett med en välkomsttext.
Lägg till en knapp som anropar en funktion och ändrar etikettens text.
Lägg till en andra knapp som återställer etiketten till välkomsttexten.
Pusha via Source Control-panelen.
Vad händer om
Du ändrar command=din_funktion till command=din_funktion(). Vad händer när programmet startar, och vad händer sedan när du klickar knappen?
Motivera & reflektera
Motivera varför command kopplas utan parenteser. Knyt an till skillnaden mellan att referera till en funktion och att anropa den, som du såg redan i Anropsstacken.
Nästa nivå
Testa med tangentbordet. Går det att nå och aktivera knappen med bara Tab och Enter? Många använder datorn helt utan mus, av vana eller för att de måste. Vad säger ditt resultat om vem som annars stängs ute?
Lägg widgetarna i ett rutnät. .pack() staplar, men det finns ett kraftfullare system som heter .grid(), där du placerar widgetar i rader och kolumner. Bygg om ditt fönster med det. Tkinter-dokumentationen om grid är på engelska, men sök gärna vidare på egen hand, det här är precis den sortens sak du kommer att slå upp resten av din tid som programmerare.