6. Moment06 - Moduler och paket

Tidigare version

Det finns en tidigare version av detta moment. I den versionen fanns det ett avsnitt som handlade om några paket som var användbara för att göra matematiska beräkningar och även skriva ut olika typer av diagram.

Moduler, paket och bibliotek i python har alla gemensamt att de hänvisar till saker som måste importeras till din applikation för att kunna användas. En modul i python har du redan tillverkat själv i bankapplikationen i moment05. Modulen du tillverkade hette i tutorialen "functions.py". En modul är helt enkelt en fil som bara innehåller funktioner.

Eftersom moduler är något som vi redan arbetat med och även tillverkat själva är det paket och några olika externa sådana vi kommer titta på i moment06. Ett paket, även kallat bibliotek, är en samling moduler som ökar funktionaliteten i python. Ett paket du använt sedan du första gången använde python är "python standard library", som är ett standardpaket i python som tillåter dig att använda alla de funktioner och datatyper vi använt hittills.

I moment06 kommer du få studera några olika paket och moduler för att få en känsla av den breda funktionalitet som python erbjuder oavsett om du vill tillverka spel, skapa grafiska applikationer eller något däremellan.

Info

Till detta moment finns en sida med lösningsförslag och videoklipp.

6.1 Använda paket i PyCharm

När vi arbetar i PyCharm och vill använda ett paket eller en modul som inte ingår i pythons standardbibliotek måste vi genomgå följande steg:

  1. Uppe i statusbaren (på mac), tryck på PyCharm -> Preferences... Klicka för bild
  2. Klicka på Project: dittProjektNamn och sedan på Project Interpreter Klicka för bild
  3. För att söka upp och installera nya paket och moduler, klicka på "+" nere till vänster i rutan. Sök sedan upp paket du vill installera och klicka på "Install package". Klicka för bild

Skillnad mellan macOS och Windows?

I exemplet ovan står det beskrivet hur man installerar ett nytt paket i macOS, troligtvis ser det up på liknande sätt i Windows. Om det inte gör det så får denna instruktion bytas ut.

6.2 Turtle graphics: En del av pythons standardbibliotek

Genomgång [klicka för att visa]

Momentet har bytt innehåll

I filmen så stämmer inte numreringen för avsnitten eller uppgifternas namn och det beror på att visst material har tagits bort sedan filmen spelades in.

Turtle graphics (turtle) är en modul som ingår i pythons standardbibliotek och är en modul för att rita ut saker i ett fönster på skärmen. Eftersom turtle ingår i pythons standardbibliotek behöver vi inte installera något utan kan bara importera modulen på samma sätt som t.ex. math. När vi importerat turtle behöver definiera två variabler för klasserna Turtle och Screen och på sista raden i en fil som använder turtle-grafik måste vi skriva turtle.done().

Problem med namngivning

När du leker med Turtle så kan du inte skapa en fil som heter turtle.py för då kommer du få ett error när du skall köra din applikation. Orsaken till detta är att du då försöker importer din egna fil istället för att importera paketet turtle och då finns ju inte alla funktioner du behöver. Detta fel inträffar om du har någon fil som heter turtle.py i ditt projekt.

Felmeddelandet som dyker upp kan/bör vara;

AttributeError: partially initialized module 'turtle' has no attribute 'Turtle' (most likely due to a circular import)

Kodexempel: Skapa ett fönster med Turtle

# Importerar turtle modulen
import turtle

# Definierar variablerna fönster och t som
# turtle.Screen() och turtle.Turtle()
fönster = turtle.Screen()
t = turtle.Turtle()

# Saknas detta kommando på sista raden kommer
# fönstret stängas när programmet är klart
turtle.done()

6.2.1 Screen

Klassen Screen kontrollerar ritfönster och genom att anropa den i början av applikationen kan vi ställa in fönsterstorlek och bakgrundsfärg.

Kodexempel: Ställa in ritfönstret

import turtle

fönster = turtle.Screen()

# Sätter bakgrundsfärgen till orange och storleken på fönstret till 800x800 pixlar
fönster.screensize(800, 800)
fönster.bgcolor('orange')

turtle.done()

I fönster.setup() anges först fönstrets bredd (x-led) följt av fönstrets höjd (y-led) och i fönster.bgcolor() anges antingen någon av de fördefinierade färgerna som exempelvis 'orange', 'black', 'pink', 'green' osv., men du kan också ange färger på några andra sätt som förklaras här.

6.2.2 Turtle

Klassen Turtle kan vi tänka på som en penna som kan rita diverse figurer genom att röra sig på skärmen. I uppgift m06u01 får du själv studera några olika funktioner till Turtle-klassen och förklara vad de gör.

På pythons hemsida finns mer information om paketet Turtle för den som vill lära sig mer om de olika funktionerna.

Uppgift: m06u01

Kopiera och kör koden och kommentera sedan vad varje rad gör.

import turtle
t = turtle.Turtle()
t.forward(100)
t.left(45)
t.backward(200)
t.right(90)
t.forward(350)
t.goto(-200, 200)
t.penup()
t.forward(50)
t.pendown()
t.color('red', 'green')
t.begin_fill()
t.circle(100)
t.end_fill()
t.forward(500)
turtle.done()

Hjälp [klicka för att visa]

I Turtles koordinationssystem så ligger origo (x=0, y=0) i mitten på fönstret. För den som jobbat med spelutveckling eller skapande av GUI tidigare så kan det verka märkligt.

Här finns ett kodexempel som använder sig av funktionerna pos(), xcor() och ycor() vilket gör att du kan fråga vilken position din turtle har.

import turtle

def print_turtleinfo(t):
    print("\nInformation om turtelns position")
    print("Pos():", t.pos())        # Anger position för din turtle (x, y)
    print("XCor():", t.xcor())      # Anger x-position för din turtle
    print("YCor():", t.ycor())      # Anger y-position för din turtle


# Origo i mitten på skärmen
t = turtle.Turtle()
print_turtleinfo(t)
t.goto(50, 50)              # Gå till koordidnat x:50, y:50
print_turtleinfo(t)
t.goto(100, 0)
print_turtleinfo(t)
t.goto(0, 0)
print_turtleinfo(t)
t.left(105)                 # Sväng vänster 105 grader
t.forward(100)              # Gå 100 steg (pixlar) framåt
print_turtleinfo(t)

turtle.done()

I m06u01 ser du att riktningen blir lite konstig i slutet. Vill vi kontrollera utgångsriktningen vår Turtle ska ha kan vi använda metoden setheading() där vi i parentesen anger en riktning i grader (0 - 360).

Uppgift: m06u02

  1. Konstruera en applikation som ritar ut en cirkel, en kvadrat, en rätvinklig triangel och en liksidig triangel. Figuererna får inte överlappa varandra och 2 av figurerna ska vara i fyllda med olika färger.
  2. Utvidga antalet figurer till 4 cirklar, 4 kvadrater, 7 rätvinkliga trianglar och 2 liksidiga trianglar.
    Här kan det vara bra att konstruera egna funktioner för de olika figurerna för att undvika upprepning i din kod.
  3. Konstruera en applikation som genererar en bild som är så lik bilden nedan som möjligt. Det är endast figurerna du ska härma. De svarta punkterna med text är för att visa koordinaterna i några delar av bilden som en hjälp för att kopian ska bli så nära originalet som möjligt.
    bild
    (Koordinaterna syns bättre om du zoomar in bilden)

6.2.3 Eventbaserad programmering

Genomgång [klicka för att visa]

I turtle modulen kan vi låta olika funktioner köras baserat på knapptryckningar på tangentbord eller musklick på skärmen. En sådan händelse kallas för ett event och när vi skapar applikationer som beror på event får vi tänka om lite i strukturen. Eventbaserad programmering kan ses som att vi hela tiden har en loop som inväntar olika event från användaren. Vi ska inte gå in så mycket mer i eventbaserad programmering, men en inblick kan nog vara bra för att hänga med i exemplen när vi börjar styra vår sköldpadda med piltangenterna och muspekaren.

Vill vi styra vår sköldpadda med till exempel piltangenterna behöver vi ha en metod som dels säger åt sköldpaddan att göra något baserat på vilken tangent vi trycker på, men också en metod som säger till applikationen att kolla om användaren tryckt på någon knapp eller ej.

I turtle är metoden för att ritfönstret ska kolla om användaren tryckt på någon knapp eller klickat någonstans på skärmen listen(). När vi i koden implementerat listen måste vi ha någon metod som kopplar ihop ett visst knapptryck med en viss funktion. Metoden som används för detta är onkey() och i den måste vi ange ett funktionsnamn samt vilken knapptryckning som ska starta funktionen.

Tutorials

Här nedanför kommer jag först presentera två kortare kodexemepl på hur vi kan styra händelser i Turtle med tangentbord och musen. Sedan kommer jag lista ett antal tutorials som du kan följa för att bygga olika spel. Spelen visar på olika spellogik och om du senare vill bygga ett eget spel som slutprojekt så kan du behöva använda logik från flera av dessa tutorials. För varje tutorial försöker jag presentera vilken logik som används. Jag har också försökt lägga tutorals i svårighetsgrad med den enklaste först. Ingen bygger på någon annan men flera av dem innehåller samma typ av logik, tex. kollisionshantering.

När du letar efter egna tutorials så kommer du märka att många av dem bygger på objektorientering. Objektorientering kommer först i nästa kurs, så jag har valt att inte länka in sådana tutorials. Vill du följa en tutorial och jobba med objektorientering så är det ok, men eftersom det inte ingår i kursen så kommer det inte vägas in i bedömningen av ett betyg.

Kodexempel: Enkelt styrsystem [klicka för att visa]

import turtle

# Setup
fönster = turtle.Screen()
seb = turtle.Turtle()
fönster.bgcolor('lightblue')
fönster.title('Flygande sköldpaddan')
seb.color('red')
seb.shape('turtle')
seb.turtlesize(5)
seb.penup()
seb.speed(5)


# Funktioner
def färdas():
    """Får sköldpaddan att hela tiden färdas framåt"""
    seb.forward(5)
    # Uppdaterar ritfönstret
    fönster.update()
    # Upprepar funktionen 1 gång per milisekund
    fönster.ontimer(färdas, 1)


def vänster():
    """Byter riktningen till vänster"""
    seb.setheading(180)


def höger():
    """Byter riktning till höger"""
    seb.setheading(0)


def upp():
    """Byter riktning till upp"""
    seb.setheading(90)


def ner():
    """Byter riktning till ner"""
    seb.setheading(270)


def avsluta():
    """Avslutar programmet"""
    fönster.bye()


# Ber ritfönster kolla efter specifika knapptryckningar
fönster.onkey(vänster, 'Left')
fönster.onkey(höger, 'Right')
fönster.onkey(upp, 'Up')
fönster.onkey(ner, 'Down')
fönster.onkey(avsluta, 'Escape')
fönster.listen()

# Anropar funktionen färdas
färdas()

# Det obligatoriska kommandot på sista raden i en turtleapplikation
turtle.done()

Kodexempel: Rita en slumpmässig kvadrat då vi trycker på mellanslag [klicka för att visa]

# nödvändiga importer
import random
import turtle

# Setup
fönster = turtle.Screen()
t = turtle.Turtle()
fönster.screensize(800, 800)
t.speed(0)


def slump_kvadrat():
    """Genererar en slumpmässig kvadrat i ritfönstet"""

    # Genererar slumpade x och y koordinater
    # och flyttar sköldpaddan dit
    x = random.randint(-400, 200)
    y = random.randint(-400, 200)
    t.penup()
    t.goto(x, y)
    t.pendown()

    # Genererar en slumpmässig sidstorlek och färger
    sida = random.randint(50, 200)
    r = random.random()
    g = random.random()
    b = random.random()
    t.color((r, g, b))

    # Ritar en kvadrat
    t.begin_fill()
    for i in range(4):
        t.forward(sida)
        t.left(90)
    t.end_fill()


# Anger att när funktionen slump_kvadrat körs när användaren trycker på mellanslag
fönster.onkey(slump_kvadrat, 'space')

# Säger till ritfönstret att lyssna efter event
fönster.listen()

# Det obligatoriska kommandot på sista raden i en turtleapplikation
turtle.done()

Kodexempel: Styra med musen [klicka för att visa]

Här följer två tutorials där du även kan använda musen för att påverka din turtle.

Styra musen med touchpad på en mac

I de tutorials som finns nedan så pratar han om tre musknappar. När du jobbar med touchpad som finns på en mac så används bara två knappar, vänster som aktiveras med onscreenclick(clickleft, 1) och höger som aktiveras med onscreenclick(clickright, 2).

Videotutorial: Kollisionshantering mot vägg (tid: ca 25 min) [klicka för att visa]

När vi bygger spel så är det viktigt att saker kan kollidera, det gör vi för att både kunna hålla koll på att saker som rör sig finns kvar inne i skärmen men även för att kunna samla poäng eller förlora när vi träffar ett annat objekt.

Här kommer en tutorial i fyra delar där bollar studsar i en avgränsad yta.

Videotutorial: A simple game (tid: ca 65 min) [klicka för att visa]

A simple game

I denna tutorial så byggs ett enkelt spel upp där du kommer lära dig en hel del matnyttiga saker för att kunna bygga ett spel. Fokus i denna tutorial ligger på kollisionshantering, att kunna spela mot flera fiender.

Spelet är kodat i Python 2.7 men jag har kört igenom hela tutorialen och det enda som behöver ändras är sista raden där du skall byta ut delay = raw_input("Press Enter to finish.") mot turtle.done().

Ljud och bilder som används i slutet av tutorial hittar du här.

Uppgift: m06u03

Här vill jag att du själv undersöker eller arbetar vidare med någon uppgift/kodexempel/tutorial som du har gjort här ovan. Exakt vad du vill göra avgör du själv.

  • Bygg vidare på ett av de spelen som du har jobbat med?
  • Bygg ihop delar av två spel till ett nytt?
  • Bygg ihop de två kodexemplen enkelt styrsystem och Rita en slumpmässig kvadrat för att skapa något nytt/annat.

Gör ett kommentarshuvud längst upp där du förklarar vad du lagt till för funktionalitet, varför du lade till den och förslag på ytterligare utveckling av applikationen.

6.2.4 Python Game Engines

Nu när du har testat på att bygga väldigt enkla spel i Python så blir ju den naturliga frågan hur man går vidare. Det finns ett antal game engines för Python där PyGame är den mest välkända.

I denna kursen kommer vi inte jobba mer med någon specifik spelmotor men om du är intresserad av att undersöka på egen hand så är det naturligtvis kul om du gör detta. Nedan finns en film som visar på några olika spelmotorer. Filmen börjar få några år på nacken och det kan vara så att alla spelmotorer inte längre är aktiva men det ger ändå en bra introduktion till spelmotorer inom Python. Tänk på att filken är en väldigt övergripande uppräkning av olika spelmotorer.

Gamefromscratch: Python Game Engine

Den elev som vill testa någon spelmotor och även göra ett slutprojekt med denna får på egen hand utforska detta bibliotek. Lite vaksamhet krävs dock, väldigt mycket av det material som finns bygger på objektorientering vilket är en teknik som ligger utanför denna kursen. Att använda objektorientering inom ramen för kursen är ok, men du kan inte höja ett betyg genom att göra det.

6.3 GUI

Genomgång [klicka för att visa]

Momentet har bytt innehåll

I filmen så stämmer inte numreringen för avsnitten eller uppgifternas namn och det beror på att visst material har tagits bort sedan filmen spelades in.

GUI är förkortningen för Graphical User Interface och på svenska brukar vi prata om grafiska användargränssnitt.

Grunden i allt arbete med GUI är att skapa fönster och sedan ett antal komponenter som har olika egenskaper, en knapp skall tryckas på, en text skall visas, en textruta skall innehålla något som programmet behöver använda sig av osv. Det är enkelt att hitta listor på alla aktuella komponenter som du behöver använda, men svårigheten är ofta att bygga användarvänliga GUI som utför det som du vill. I ett antal exempel nedan kommer grunderna gås igenom och när du kan detta så kommer du troligtvis känna sådan trygghet i att du själv kan lägga till de komponenter som just du behöver för att klara av att bygga det GUI som du behöver till din applikation. Ett tips är att först rita ut det GUI som du vill bygga så vet du exakt hur du vill ha det och du vet också om när du är färdig. Att överarbeta ett GUI är lätt gjort.

Denna avsnitt är upplagt lite annorlunda än de flesta i denna kursen. Orsaken till detta är att jag först kommer presentera biblioteket Tkinter. Tkinter är ett inbyggt paket i Python som är väldigt kraftfult och som väldigt många använder när de bygger GUI för Python. Att det är kraftfullt är också ett problem då det i många fall kan kännas onödigt komplicerat och för många känns det som en ganska hög tröskel att ta sig över.

Därför kommer jag också presentera ett paket som bygger på Tkinter men som förenklar en hel del. PySimpleGUI är ett paket som togs fram våren 2020 och som känns väldigt genomarbetat och användarvänligt.

6.3.1 Tkinter

Nästa bibliotek vi skall kika på heter Tkinter och det är ett inbyggt bibliotek för att bygga GUI-applikationer.

Kodexempel: Att skapa ett enkelt fönster [klicka för att visa]

from tkinter import *

# Skapa ett fönster som döps till window
window = Tk()

# Sätter storleken på fönstret till 400x200px
window.geometry("400x200")

# Sätter bakgrundsfärg på fönstret
window.configure(background="green")

# Sätter titeln på fönstret
window.title("Hello")

# Skapar en label (text) som skall ligga i "window"
label = Label(window, text="Hello World")

# pack() är funktionen för att lägga till komponenter till fönstret
label.pack()

# mainloop för att applikationen skall köras.
window.mainloop()

Fönster vid körning

Jag har inte satt någon bakgrund på label, därför blir den vit. Testa att byta färg på båda bakgrund och text genom att ge attributen bg="green", fg="white".

Nästa komponent som skall användas är knappar.

Kodexempel: Att skapa ett enkelt fönster med knappar [klicka för att visa]

from tkinter import *

# Skapa ett fönster som döps till window
window = Tk()

# Sätter storleken på fönstret till 400x200px
window.geometry("400x200")

# Sätter bakgrundsfärg på fönstret
window.configure(bg="green")

# Sätter titeln på fönstret
window.title("Hello")

# Skapar en label (text) som skall ligga i "window"
label = Label(window, text="Hello World", bg="green", fg="white")

# pack() är funktionen för att "bygga" komponenter
label.pack()

# Skapar 2 knappar, en för att logga in och och för att kunna registrera
button1 = Button(window, text="Logga in")
button1.pack()
button2 = Button(window, text="Registrera")
button2.pack()

# mainloop för att applikationen skall köras.
window.mainloop()

Fönster vid körning

Här ser vi direkt ett problem med utseendet. När vi lägger till komponenter till vårt window så läggs de under varandra. Här behöver vi hitta ett bättre sätt att placera ut våra komponenter som vi vill ha dem. Detta är ett vanligt problem inom de flesta programmeringsspråk och varje språk har sina speciallösningar för problemet.
Inom Python finns det två tekniker som är relativt enkla och som ofta används. Den ena tekniken kallas frames och där tänker vi oss att vi skapar olika ramar i vår applikation och sedan kan vi placera innehållet i en ram så som vi vill och sedan kan vi placera ramarna inbördes som vi vill.
Den andra tekniken som vi skall kika närmare på är grid. Grid innebär att vi bygger vår applikation med hjälp av rader och kolumner. Det är också möjligt att slå ihop Frames och Grid och bygga applikationer precis som du vill med hjälp av dessa tekniker.

Dags att kolla på hur vi kan använda Grid. Jag vill bygga en inloggningsapplikation.

Kodexempel: Exempel på Grid-layout [klicka för att visa]

from tkinter import *

# Skapa ett fönster som döps till window
window = Tk()

# Sätter storleken på fönstret till 400x200px
window.geometry("400x200")

# Sätter bakgrundsfärg på fönstret
window.configure(background="green")

# Sätter titeln på fönstret
window.title("Inloggningsapplikationen")

# Deklarerar mina komponenter
label1 = Label(window, text="Username", bg="green", fg="white")
label2 = Label(window, text="Password", bg="green", fg="white")
entry1 = Entry(window)      # Entry är en inmatningsruta
entry2 = Entry(window)
button1 = Button(window, text="Logga in")

# Placerar textrutorna på rad 0 och 1, vilket är rad 1 och 2
label1.grid(row=0)
label2.grid(row=1)

# Inmatningsrutorna placeras i kolumn 1, vilket är kolumn 2
entry1.grid(row=0, column=1)
entry2.grid(row=1, column=1)

# Placerar knappen på rad 2, column 2.
button1.grid(row=2, column=1)

# Sätt fokus till entry1
entry1.focus_set()

# mainloop för att applikationen skall köras.
window.mainloop()

Fönster vid körning

Det ser ju betydligt bättre ut. Nu har vi tagit kontrollen på hur vi skall placera ut våra komponenter. Det finns nu ett antal attribut för att kunna skapa luft i våra GUI och då kan vi använda padx och pady för att skapa avstånd mellan celler i x- eller y-led.

label1.grid(row=0, padx=30, pady=15)
label2.grid(row=1, pady=15)

Vi har ju en fin applikation just nu men den kan ju inte göra någonting. Vi behöver koppla en händelse och jag vill, när användaren klickar på knappen, kontrollera om användarnamn och lösenord är korrekt så att användaren kan få logga in.

Kodexempel: Inloggningsapplikation [klicka för att visa]

from tkinter import *

def check_login():
    """
    Funktion som kollar om inmatade värden är korrekta för att
    loggas in eller inte.
    :return: ingenting
    """
    # Hämta värden från inmatningsrutorna
    username = entry1.get()
    password = entry2.get()

    # Kollar om användarnamn OCH lösenord är korrekta.
    if username == "admin" and password == "admin":
        output.config(text="Korrekt inloggning", fg="white")
    else:
        output.config(text="Felaktig inloggning", fg="yellow")

    # Töm inmatningsrutorna från tecken 0 - END
    entry1.delete(0, END)
    entry2.delete(0, END)
    # Sätt fokus på entry1
    entry1.focus_set()


# Skapa ett fönster som döps till window
window = Tk()

# Sätter storleken på fönstret till 400x200px
window.geometry("400x200")

# Sätter bakgrundsfärg på fönstret
window.configure(background="green")

# Sätter titeln på fönstret
window.title("Inloggningsapplikationen")

# Deklarerar mina komponenter
label1 = Label(window, text="Username", bg="green", fg="white")
label2 = Label(window, text="Password", bg="green", fg="white")
entry1 = Entry(window)      # Entry är en inmatningsruta
entry2 = Entry(window, show="*")
# command anropar funktionen jag skapat. OBS: utan parenteser i anropet.
button1 = Button(window, text="Logga in", command=check_login)
output = Label(window, text="Klicka på knappen för att logga in!", bg="green", fg="white")

# Placerar textrutorna på rad 0 och 1, vilket är rad 1 och 2
label1.grid(row=0, padx=30, pady=15)    # padx styr hela kolumnen
label2.grid(row=1, pady=15)

# Inmatningsrutorna placeras i kolumn 1, vilket är kolumn 2
entry1.grid(row=0, column=1)
entry2.grid(row=1, column=1)

# Placerar knappen på rad 2, column 2.
button1.grid(row=2, column=1)

# Placerar utskriften på rad 3, column 1 (2 kolumner bred..)
output.grid(row=3, column=0, columnspan=2, pady=15)

# Sätt fokus till entry1
entry1.focus_set()

# mainloop för att applikationen skall köras.
window.mainloop()

Fönster vid körning

Tutorial: To Do List [klicka för att visa]

ToDo List

En tutorial där en ToDo-app byggs upp steg för steg. Du kommer lära dig att bygga upp en fönsterapplikation och koppla olika händelser till knappar och den vägen kunna styra applikationen.

Applikationen är kodad i Python 2.7 men det enda som behöver ändras är att klassen skrivs med gemener i Python 3.x tkinter istället för Tkinter som det skrivs i Python 2.7.

I film 6 så jobbar han med funktionalitet kring MessageBox. Den kosen som han skriver i Python 2.7 fungerar inte i Python 3.x. import tkMessageBox skall ersättas med from tkinter import messagebox och confirmed = tkMessageBox.askyesno("Please confirm", "Do you really want to delete all?") skall ersättas med confirmed = messagebox.askyesno("Please confirm", "Do you really want to delete all?").

Slutligen ersätts tkMessageBox.showwarning("Warning", "You need to enter a task") med messagebox.showwarning("Warning", "You need to enter a task") för att visa ett felmeddelande när vi försöker lägga till en tom uppgift.

Värt att notera här är att själva händelsen som kopplas till knappen skapas i en funktion för att det skall vara lätt att anropa denna. Alla komponenter i vårt GUI går lika bra att nå ifrån funktionen som ifrån själva programmet.

Länkar

Här finns en sammanställning på enkla komponenter (widgets på engelska) som är användbara när du jobbar med Tkinter. Vet du bara vad komponenten heter så kommer du hitta mängder av kodexempel på nätet.

Här finns en bra bild på samtliga namngivna färger som går att använda i Tkinter.

Youtube: Python GUI with Tkinter (thenewboston) [klicka för att visa]

Här finns en serie om 14 videoklipp från thenewboston som går igenom ett antal widgets genom olika exempel.

Som en förberedelse för ett av slutprojekten så skall jag visa ett exempel på hur vi kan använda olika komponenter för att simulera ett eller flera tärningsslag. Först visar jag hur en knapp kan användas för att slå en tärning och detta värde skrivs ut.

Kodexempel: Tärningsapplikation 1 [klicka för att visa]

# Importer av bibliotek
from tkinter import *
from random import randint

def hit():
    """
    Slår ett tärningsslag och skriver ut tärningen värde
    :return: ingen retur
    """
    # Slumpa ett värde
    dice = randint(1, 6)
    # Skriver ut en text
    dice_text.config(text="Tärningens värde: {}".format(dice))


# Skapa ett fönster som döps till window
window = Tk()

# Sätter storleken på fönstret
window.geometry("400x200")

# Sätter bakgrundsfärg på fönstret
window.configure(background="cornsilk2")

# Sätter titeln på fönstret
window.title("Slå en tärning")

#
# Deklarerar mina komponenter
info = Label(window, text="Tryck på knappen för att slå ett tärningsslag", bg="cornsilk2")
button = Button(window, text="Slå", command=hit, bg="cornsilk2")
dice_text = Label(window, text="Du har inte slagit ännu", bg="cornsilk2")

# Placera ut widgets i min grid
info.grid(row=0, column=0, columnspan=2, pady=15, padx=20)
button.grid(row=1, column=0, pady=15)
dice_text.grid(row=1, column=1, pady=15)

# mainloop för att applikationen skall köras.
window.mainloop()

Fönster vid körning

Vid programstart

Efter att jag har klickat på knappen och slagit en tärning.

Kodexempel: Tärningsapplikation 2 [klicka för att visa]

# Importer av bibliotek
from tkinter import *
from random import randint

def hit():
    """
    Slår ett tärningsslag och skriver ut tärningen värde
    :return: ingen retur
    """
    # Slumpa ett värde
    dice = randint(1, 6)
    # Skriver ut en text
    dice_text.config(text="Tärningens värde: {}".format(dice))
    # Lägger till en "sprite" till Label dice_sprite/dice_img
    dice_sprite.config(file="img//dice{}.png".format(dice))


# Skapa ett fönster som döps till window
window = Tk()

# Sätter storleken på fönstret
window.geometry("400x200")

# Sätter bakgrundsfärg på fönstret
window.configure(background="cornsilk2")

# Sätter titeln på fönstret
window.title("Slå en tärning")

#
# Deklarerar mina komponenter
info = Label(window, text="Tryck på knappen för att slå ett tärningsslag", bg="cornsilk2")
button = Button(window, text="Slå", command=hit, bg="cornsilk2")
dice_text = Label(window, text="Du har inte slagit ännu", bg="cornsilk2")
# Mina bilder ligger i mappen "img", observera // för att ange mapp
dice_sprite = PhotoImage(file="")       # Ingen fil gör att ingen bild ritas ut
dice_img = Label(window, image=dice_sprite, bg="cornsilk2")


# Placera ut widgets i min grid
info.grid(row=0, column=0, columnspan=2, pady=15, padx=20)
button.grid(row=1, column=0, pady=15)
dice_text.grid(row=1, column=1, pady=15)
dice_img.grid(row=2, column=0)

# mainloop för att applikationen skall köras.
window.mainloop()

Fönster vid körning

Vid programstart

Efter att tärning är slagen

Om du vill använda mina tärningar så finns de här, annars kan du skapa egna bilder eller leta upp bilder på nätet som du har rätt att använda.

6.3.2 PySimpleGUI

Som jag nämnde tidigare så är PySimpleGUI relativt nytt och det finns tyvärr inte allt för många bra tutorials eller kodexempel på nätet. Men däremot så finns det väldigt bra dokumentation och genomgångar från skaparna av PySimpleGUI men då bör du redan ha kikat lite snabbt på något kodexempel för att få känslan för att det är just detta paket som du vill använda.

Först ett enkelt exempel för att visa hur koden är uppbyggd.

Kodexempel: Ett litet exempel [klicka för att visa]

# 1. Importerar paketet
import PySimpleGUI as sg

# 2. Bygger upp komponenterna för GUI.
layout = [[sg.Text("What's your name?")],
          [sg.Input(key='-INPUT-')],
          [sg.Text(size=(40, 1), key='-OUTPUT-')],
          [sg.Button('Ok'), sg.Button('Quit')]]

# 3. Skapar fönstret
window = sg.Window('Window Title', layout)

# 4. En event-loop för att fånga upp händelser i GUI
while True:
    event, values = window.read()

    # Stänger fönstret med "kryssknappen" och "Quit-knappen"
    if event == sg.WINDOW_CLOSED or event == 'Quit':
        break

    # Skriver ut
    window['-OUTPUT-'].update('Hello ' + values['-INPUT-'] + "! Thanks for trying PySimpleGUI")

# 5. Stänger fönstret
window.close()

Fönster vid körning

Det som är tydligast med PySimpleGUI är de fem delarna som bygger upp applikationen och framförallt hur delen där alla komponenter skapas görs i form av en lista där tanken är att det skall vara enkelt att se för ett mänskligt öga hur komponenterna skall placeras ut. I filmen nedan så går de igenom de fem delarna på knappt 4 minuter.

PySimpleGUI 2020 - START HERE - Whiteboard video overview (3m51s) [klicka för att visa]

Nästa exempel är inloggningsapplikationen som skapades tidigare under avsnittet om Tkinter. Den gången skrevs koden på 66 rader (inklusive kommentarer) och nu används endast 33 rader för att skapa samma applikation. Jämför gärna de båda applikationerna.

Kodexempel: Inloggningsapplikationen [klicka för att visa]

# 1. Importerar paketet
import PySimpleGUI as sg

# Sätter färgtema på fönstret, gör det "tidigt" för att alla komponenter skall få rätt färg.
sg.theme('Dark Green 5')

# 2. Bygger upp komponenterna för GUI.
layout = [[sg.Text("Username"), sg.Input(key='-USERNAME-')],
          [sg.Text("Password"), sg.Input(key='-PASSWORD-', password_char='*')],
          [sg.Button('Logga in')],
          [sg.Text(size=(40, 1), key='-OUTPUT-')]]

# 3. Skapar fönstret
window = sg.Window('Inloggningsapplikationen', layout)

# 4. En event-loop för att fånga upp händelser i GUI
while True:
    event, values = window.read()

    # Stänger fönstret med "kryssknappen"
    if event == sg.WINDOW_CLOSED:
        break

    # Kollar om användarnamn/lösenord är korrekt
    if values['-USERNAME-'] == "admin" and values['-PASSWORD-'] == "admin":
        # Skriver ut
        window['-OUTPUT-'].update('Inloggningen lyckades')
    else:
        # Skriver ut
        window['-OUTPUT-'].update('Felaktig inloggning, prova med admin/admin istället....')

# 5. Stänger fönstret
window.close()

Fönster vid körning

Där var två väldigt korta exempel på GUI med PySimpleGUI. Vill du utforska detta paketet ytterligare så rekommenderar jag hemsidan eller spellistorna på Youtube där du kan få både överblick och en djupare genomgång av PySimpleGUI. Nu finns det också fler och fler youtubefilmer skapade av andra än bara teamet bakom PySimpleGUI.

m06u04

Bygg en enkel kalkylator, börja med att ha en knapp som adderar heltalen i två inmatningsrutor och summerar dessa innan svaret skrivs ut på något lämpligt sätt.

Denna enkla kalkylator kan du nu bygga ut på olika sätt, här följer några exempel;

  • Hantera decimaltal.
  • Lägga till knappar för subtraktion, multiplikation, division. Kanske också lägga till hantering av heltalsdivision och restdivision.
  • Skapa en lista med alla historiska beräkningar som visas upp på lämpligt sätt.