Översikt¶
- Temauppgift 5
- Grafiska gränssnitt, som exempel på tillämpning av OOP
- Egen algoritm som placerar ut kvadrater efter ett visst mönster.
- Seminarium / Rapport Tema 5
- Utbrytning av funktioner
- Mer om funktioner
- default-värden för argument
- funktionsobjekt
- Grafiska gränssnitt och händelsedriven programmering
- Vad är ett GUI?
- Olika former av programflöde
- GUI-programmering i Python med
grid
- Bryta ut funktioner
- Högre ordningens funktioner
Tema 5
Grafiska gränssnitt och abstraktion/dekomposition i praktiken
Temauppgift 5¶
- Del 1: Grafiska gränssnitt med
tkinter
- Del 2: Utplacering av kvadrater i rader/kolumner
- Utökade kodkrav (PEP8 & PEP257 gäller fortfarande):
- Variabelnamn
- Hårdkodade värden
- Kommentarer för alla satser som är två eller fler rader (dvs alla if-satser, loopkonstruktioner m.m.)
- Rekommendation: dela upp kod i funktioner för att öva och att visa att ni vet hur man gör
Seminarium och Rapport Tema 5¶
- Dela upp problem i delproblem.
- Seminarium: öva (förberedelser) och diskutera hur kod kan brytas ut
- Rapport: bryta ut funktioner från befintlig kod
Repetition, f-strängar (Python 3.6+)¶
- Sedan Python 3.6 finns f-strängar (f-strings)
- I f-strängar kan vi skriva pythonuttryck innanför måsvingar med automatisk konvertering till strängar!
- Mycket smidigt för att skriva ut variabler!
In [ ]:
l = ["en", "lista", "med", "värden"]
print(f"första värdet: {l[0]}")
- Sedan Python 3.8 kan man även använda
=
efter ett uttryck för att själva uttrycket också ska skrivas ut.
In [ ]:
s = "batteri"
print(f"{s=}")
Funktionsobjekt
... även funktioner är objekt i Python
Funktionsobjekt¶
- Klasser kan instantieras och producera objekt.
- I Python är även funktioner objekt.
- Vi kan alltså använda funktioner som ett värde.
- Exempel:
def hejsan(): print("Hejsan")
- Anrop av funktionen:
hejsan()
- Funktionsobjektet:
hejsan
Exempel på funktionsobjekt¶
In [ ]:
def print_hello():
print("Hello World!")
eggs = print_hello
eggs()
Högre ordningens funktioner¶
Konsekvenser av att funktioner är objekt¶
- Funktioner är data (minns von Neumann-arkitekturen där både data och program lagras på samma sätt)
- Variabler kan tilldelas funktioner som värden
- Funktioner kan ta andra funktioner som argument
- Funktioner kan returnera funktioner
In [ ]:
my_length_fun = len
another_print = print
another_print(my_length_fun(["a","short","list"]))
Högre ordningens funktioner¶
- Funktioner som opererar på funktioner
- Tar en eller flera funktioner som argument, eller
- Returnerar funktionsobjekt
- Centrala i funktionell programmering, men användbara även i andra sammanhang
Exempel, med funktionsobjekt¶
In [ ]:
# insertionsort som högre ordningens funktion
def insertionsort(alist, order_fn):
for i in range(1, len(alist)):
currentvalue = alist[i]
pos = i
# argumentet order_fn till funktionen är den funktion som
# ska användas för jämförelsen
while (pos >= 1 and order_fn(alist[pos-1], currentvalue)):
alist[pos]=alist[pos-1]
pos -= 1
alist[pos]=currentvalue
def asc_order(value1, value2):
return value1 > value2
def desc_order(value1, value2):
return value1 < value2
alist = [54,26,93,17,77,31,44,55,20]
insertionsort(alist, desc_order)
print("Desc:", alist)
insertionsort(alist, asc_order)
print("Asc:", alist)
Anonyma funktioner?¶
- Alla klasser kan instantieras genom
klassnamn()
och vi behöver inte spara det skapade objektet i en variabel för att kunna använda det.sorted(list())
print(str(1))
- Funktioner som vi skapat med
def
måste vi ge ett namn. - Kan vi skapa funktioner utan namn?
Lambda-uttryck¶
- Lambda-uttryck är uttryck som returnerar ett funktionsobjekt med följande begränsningar:
- Funktionen måste innehålla exakt ett uttryck (eng. expression)
- Värdet av det uttrycket kommer returneras
- Dvs vi kan använda lambda-uttryck för att snabbt skapa små funktioner utan namn
lambda arg1, arg2, ... argn: <uttryck_att_returnera>
- T.ex. funktion som returnerar det första värdet i en lista
lambda x: x[0]
- Användning med inbyggda funktioner
- tillsammans med
sort()
,max()
,min()
- tillsammans med funktionen
filter()
- tillsammans med
- Kanske en funktion som jämför två värden?
lambda x, y: x > y
In [ ]:
# insertionsort som högre ordningens funktion med lambda-argument
def insertionsort(alist, order_fn):
for i in range(1, len(alist)):
currentvalue = alist[i]
pos = i
# argumentet order_fn till funktionen är den funktion som
# ska användas för jämförelsen
while (pos >= 1 and order_fn(alist[pos-1], currentvalue)):
alist[pos]=alist[pos-1]
pos -= 1
alist[pos]=currentvalue
alist = [54,26,93,17,77,31,44,55,20]
insertionsort(alist, lambda x, y: x < y)
print("Desc:", alist)
insertionsort(alist, lambda x, y: x > y)
print("Asc:", alist)
In [ ]:
# pythons inbyggda funktion sorted() kan ta emot nyckelordsargumentet key som är en
# funktion som den ska använda för att plocka fram värdet som något ska sorteras efter.
# säg att vi har en lista av ord-ordfrekvenspar
wordlist = [["ord1", 10], ["ord2", 32], ["ord3", 5]]
# vi skriver en funktion som tar ordfrekvenspar och returnerar dess frekvens
def get_freq(word_and_freq):
return word_and_freq[1]
# vi kan nu skicka med funktionen till sort för att sortera listan efter frekvens
print(f"{sorted(wordlist, key=get_freq)=}")
# istället för att definiera en vanlig namngiven funktion, kan vi använda ett
# lambdauttryck (som evalueras till ett funktionsobjekt)
print(f"{sorted(wordlist, key=lambda word_and_freq: word_and_freq[1])=}")
min()
, max()
¶
- Funktionerna
max()
ochmin()
kan ta in en funktion som används för att ta fram det värde som ska jämföras via nyckelordsargumentetkey
:
max(values, key=func)
min(values, key=func)
In [ ]:
wordlist = [["ord1", 10], ["ord2", 32], ["ord3", 5]]
# mest frekventa ordet, funktionsargumentet ska returnera det värde
# som max() ska använda för att jämföra element med varandra
print(f"max_word = {max(wordlist, key=lambda wf: wf[1])}")
# minst frekventa ordet, samma som max()
print(f"min_word = {min(wordlist, key=lambda wf: wf[1])}")
# kom ihåg, om vi itererar över ett dictionary är det nycklarna vi
# itererar över
contestants = {"ada": 1, "boris": -10, "cissi": 5, "doris": 3}
print(f"winner = {max(contestants, key=lambda name: contestants[name])}")
filter()
¶
- Argument till funktionen filter():
- en funktion
- en iterable (något itererbart, t.ex. en lista, eller nycklar från ett dictionary m.m.)
- Ger oss en iterator över alla element som funktionen returnerar
True
för- En iterator är ett objekt som stegvis genererar en sekvens element för element
In [ ]:
wordlist = [["apa", 10], ["fisk", 32], ["alligator", 5] ]
# ta fram alla ord i wordlist som börjar på "a"
# första argumentet är en funktion som returnerar True för de element som ska behållas
print(f"a_list = {filter(lambda wf: wf[0].startswith('a'), wordlist)}")
# ta fram alla ord i words vars sista tecken är char
words = ["bror", "bok", "bar", "barberare"]
char = "r"
print(f"ends_with_char = {list(filter(lambda word: word[-1] == char, words))}")
# titta bara på rader som inte har "N/A" som värde på index 2
for row in filter(lambda r: r[2] != "N/A", csv_data):
pass # gör grejer
Flera sätt att binda värden till funktionsparametrar
Fem olika metoder
Enskilda parameterbindningar¶
- Positionella argument:
- Argumenten anges i en viss ordning och binds till motsvarande parameter.
- Måste alltid komma först vid funktionsanrop
- Parameternamn behöver inte anges vid anrop.
- (Det vi sett mest av sedan tidigare.)
- Default-argument:
- Argument som inte behöver anges då motsvarande parameter tilldelats ett standardvärde när funktionen definierades.
- Många inbyggda funktioner har dessa, men de används bara i specialfall.
- När man vill ha ett standardbeteende, men ge utrymme för variation. Exempel:
print()
,open()
- (Har vi använt, men oftast inte sett, av naturliga skäl.)
- Nyckelordsargument:
- Alla argument binds till någon parameter, med nyckelordsargument kan vi explicit säga vilket argument som ska bindas till vilken namngiven parameter.
- Alla parametrar (med undantaget
self
i metoddefinitioner) kan bindas mha nyckelordsargument.
Enskilda parameterbindningar¶
In [ ]:
def happy_anniversary(name, occasion="födelsedagen", num=1):
print(f"Grattis på {occasion}, {name}!")
print(f"Tänk att du firat detta {num} gånger nu!")
happy_anniversary("Ragiähl")
In [ ]:
happy_anniversary("Önde", "namnsdagen, eller inte", 0)
In [ ]:
happy_anniversary(num=42, name="Gjohl",)
Sammansatta parameterbindningar¶
*args
:- Ett obegränsat antal positionella argument som inte behöver ha definierats när funktionen definierades.
- Representeras inne i funktionen av en tupel.
- Oftast
*args
men man ser varianter som*objects
- "Övriga positionella argument."
**kwargs
:- Ett obegränsat antal nyckelordsargument som inte behöver ha definierats när funktionen definierades.
- Representeras inne i funktionen av ett dictionary.
- "Övriga nyckelordsargument."
Sammansatta parameterbindningar¶
In [ ]:
def add(term1, term2):
return term1 + term2
def multiply(factor1, factor2):
return factor1 * factor2
def aggregate(*args, function=add):
result = args[0]
for arg in args[1:]:
result = function(result, arg)
return result
aggregate(1,2,3,4,5, function=add)
In [ ]:
aggregate(1,2,3,4,5, function=multiply)
Sammansatta parameterbindningar¶
In [ ]:
def print_pairs(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_pairs(name="Norville Rogers", hometown="Coolsville, OH", company="Mystery, Inc.")
print_pairs(course_name="Graph Theory", exam1="0 points", exam2="3 points", grade="Let's never talk about this again.")
Sammansatta parameterbindningar¶
In [ ]:
def add(term1, term2):
return term1 + term2
def aggregate(*args, fun=add):
result = args[0]
for arg in args[1:]:
result = fun(result, arg)
return result
def multiply(factor1, factor2):
return factor1 * factor2
aggregate(1,2,3,4,5, fun=multiply)
Exempel med print
Exempel med print
¶
In [ ]:
print("We", "can", "actually", "add", "however", "many", "arguments", "we", "want")
In [ ]:
print("We can", "also change", "the separator", sep="\n")
In [ ]:
print("or the", end=' ')
print("end")
In [ ]:
with open("printtest.txt", 'w') as outfile:
print("Or print to a file rather than stdout!", file=outfile)
Fallet open
Fallet open
, forts.
Grafiska användargränssnitt¶
Gränssnitt¶
- Engelska: interface
- Gränssnitt - platsen där två, ofta orelaterade, system möts och kommunicerar med varandra - något som ligger mellan två olika system och genom vilket systemen kommunicerar med varandra. T.ex.
- Hårdvarugränssnitt, fysiska anslutningar mellan apparater, t.ex. USB, SATA, XLR, etc.
- Nätverksgränssnitt, mellan apparat och nätverk, t.ex. Ethernet, WiFi, Fiber, etc.
- Mjukvarugränssnitt, mellan olika program och/eller hårdvaror, t.ex. operativsystem, drivrutiner, API:er.
- Oftast när vi pratar om gränssnitt inom kognitionsvetenskap menar vi gränsnitt som på något sätt har ett mänskligt system, dvs en människa, på ena sidan.
- Ofta pratar vi om användargränssnitt.
Användargränssnitt¶
Mellan en (oftast mänsklig) användare och någon form av system, t.ex.
- Rent elektroniska användargränssnitt
- Som på de tidigaste datorerna. Kommunikation via koppling av kablar och indikatorlampor.
- Fysiska gränssnitt
- Med knappar, vred, analoga displayer, och digitala displayer, t.ex. i fordon eller på köksapparater.
- Rena textgränssnitt
- Kommunikation i båda riktningarna enbart med text, som t.ex. skalet i Linux.
- Grafiska gränssnitt
- Kommunikation med grafiska element som fönster, menyer, ikoner, tabbar, widgets, osv.
- Haptiska gränssnitt
- Kommunikation via sensorer som känner av rörelser och aktuatorer som genererar rörelser.
- De flesta system vi är vana vid är multimodala och använder en blandning av olika gränssnitt för att kommunicera.
Grafiska användargränssnitt¶
- Eng. Graphical User Interface eller GUI
- Flera olika interaktionsmodeller som lägger fokus på olika saker och fungerar olika bra med olika typer av input, oftast beskrivna med metaforer.
- Fönsterbaserade system har visat sig populära på persondatorer med större skärmar, mus och tangentbord.
- Tab- och menybaserade system är mer populärt på enheter med mindre men pekkänsliga skärmar, som smartphones och tablets
Widget
- Ursprungligen synonymt med "gadget", dvs manick, mojäng, grej, pryl, osv. Sedermera mer specifikt "WIndow gaDGET" när fönster-metaforen dominerade inom GUI-fältet.
Bakom kulisserna i ett GUI¶
- Widgets, instanser av olika typer av GUI-element.
- Olika typer av fönster: dialogrutor, öppna dokument, etc.
- Behållare (Containers) som kan innehålla widgets: Frame, Panel, Canvas
- Funktionalitet för att ordna layout av widgets, etc.
- Koppling mellan widgets och funktioner/metoder.
Program- och interaktionsflöde för textbaserade gränssnitt¶
Exempel: Textbaserat gränssnitt
Exempel: Textbaserat gränssnitt
Programflöde: Textbaserat gränssnitt¶
- Linjär/hierarkisk interaktion.
- Visa alternativ för användaren.
- Ta emot kommando från användaren.
- Ta eventuellt emot ytterligare information från användaren.
- Interaktion m.h.a. tangentbord.
- Typisk interaktion
- Kör kommando
- Välj ett alternativ
- Svara på en fråga
Program- och interaktionsflöde för grafiska gränssnitt¶
Exempel: Textbaserat gränssnitt
Exempel: Textbaserat gränssnitt
Programflöde: GUI¶
- Icke-linjär interaktion
- Reaktivt gränssnitt - tät återkoppling (ändringar återspeglas direkt)
- Interaktion via mus och tangentbord
- Interaktion med en begränsad och standardiserad uppsättning widgets (oftast)
Syfte med att skapa grafiska gränssnitt i Python med tkinter
- Introduktion till GUI-programmering.
- Widgets i
tkinter
. - Få saker att hända när man interagerar med widgets.
- Bestämma layout för widgets i ett GUI.
- Inte att ge en introduktion till hur moderna GUI-ramverk fungerar (mer om dessa i kurser senare i programmet).
Om tkinter
¶
- Lastgammalt, men...
- Det enda biblioteket för att bygga GUI-applikationer i Pythons standardbibliotek (dvs det är inbyggt).
- Långt ifrån det enda biblioteket/ramverket för att bygga GUI-applikationer i Python.
- Python-wrapper för Tcl/Tk, ett GUI-bibliotek som sträcker sig tillbaka till början av 90-talet och som kan användas från massor olika programmeringsspråk.
- Tcl, uttalat "tickle", är ett idag utdaterat språk som bara lever kvar genom Tk.
- Widgets är definierade som klasser: faktiska gränssnittskomponenter är instanser av klasserna.
- Olika inställningar kan ges när man skapar en widget.
- Widgets kan placeras i ett GUI på tre sätt - det finns tre "geometry managers" (i andra ramverk också kallade för "layout managers").
Widgets i tkinter
Objektorienterat GUI¶
Struktur på en GUI-applikation med tkinter
¶
In [ ]:
# Importera tkinter
import tkinter as tk
# Ett rot-fönster
root = tk.Tk()
# Widgets läggs in i rot-fönstret
button = tk.Button(root, text="Press me!")
# Widgets layoutas
button.pack()
# Huvudloopen startas
root.mainloop()
Var finns min widget?¶
- En widget ligger alltid i en "container" (behållare).
- Vissa widgets är containers, t.ex. fönster och Frames, och kan innehålla andra widgets.
- Vilka widgets finns?
Widgets i tkinter
¶
Widget | Description |
---|---|
Button | A simple button, used to execute a command or other operation. |
Canvas | Structured graphics. This widget can be used to draw graphs and plots, create graphics editors, and to implement custom widgets. |
Checkbutton | Represents a variable that can have two distinct values. Clicking the button toggles between the values. |
Entry | A text entry field. |
Frame | A container widget. The frame can have a border and a background, and is used to group other widgets when creating an application or dialog layout. |
Label | Displays a text or an image. |
Listbox | Displays a list of alternatives. The listbox can be configured to get radiobutton or checklist behavior. |
Widgets i tkinter
¶
Widget | Description |
---|---|
Menu | A menu pane. Used to implement pulldown and popup menus. |
Menubutton | A menubutton. Used to implement pulldown menus. |
Message | Display a text. Similar to the label widget, but can automatically wrap text to a given width or aspect ratio. |
Radiobutton | Represents one value of a variable that can have one of many values. Clicking the button sets the variable to that value, and clears all other radiobuttons associated with the same variable. |
Scale | Allows you to set a numerical value by dragging a "slider". |
Scrollbar | Standard scrollbars for use with canvas, entry, listbox, and text widgets. |
Text | Formatted text display. Allows you to display and edit text with various styles and attributes. Also supports embedded images and windows. |
Toplevel | A container widget displayed as a separate, top-level window. |
Exempel på widgetanvändning¶
window.py¶
In [ ]:
# importera tkinter-modulen
from tkinter import *
# skapa ett Tk-fönster
window = Tk()
# starta GUI-loopen
mainloop()
- Huvudfönstret i
tkinter är ett objekt av typen (klassen)
Tk
. För att utritning av fönster etc. ska göras, måste man starta GUI:ts huvudloop:mainloop()
(funktionen ligger i modulentkinter
)
Flera fönster¶
In [ ]:
# windows.py
from tkinter import *
# Skapa det första fönstret
root = Tk()
# Skapa det andra fönstret
top = Toplevel()
# Starta Tk-loopen
root.mainloop()
För att skapa fler än ett fönster utöver det första används klassen Toplevel
.
Skapa widgets¶
# utan att spara referens
Widget(parent, attribute1=v1, attribute2=v2, ...)
# med referensen sparad
w = Widget(parent, attribute1=v1, attribute2=v2, ...)
- När man skapar en widget, skapar en instans av en widgetklass, måste man ange dess förälder. Föräldern (
parent
) är ett fönster eller en widget som kan agera "behållare".
Button
¶
- En knapp.
- Knappar kan ha text på
- Knappar kan tryckas på
- Knappar kan vara avstängda (disabled)
- https://dafarry.github.io/tkinterbook/button.htm
button.py¶
In [ ]:
# importera tkinter-modulen
import tkinter as tk
# skapa ett rot-fönster
root = tk.Tk()
button = tk.Button(root, text="Press me!")
button.pack()
# starta GUI-loopen
root.mainloop()
Få något att hända när man trycker på knappen¶
- Koppla beteende till en widget genom att koppla ett funktionsobjekt/metodobjekt till det.
- Ett funktionsobjekt/metodobjekt är "namnet" på en funktion/metod (utan efterföljande parenteser).
- Vi kan koppla ihop ett funktionsobjekt som ett kommando som körs när vi aktiverar en widget.
- Vi kan binda ihop en viss händelse relaterat till widgeten med ett funktionsobjekt.
Ange widget-kommando¶
- Man kan koppla ett kommando till vissa widgets, t.ex. knappar:
button = tk.Button(root, text="OK", command=do_this)
- där
do_this
är ett funktionsobjekt.
button-command.py¶
In [ ]:
# importera tkinter-modulen
import tkinter as tk
def callback():
"""Print a set string when called."""
print("Something happened!")
# skapa ett Tk-fönster
root = tk.Tk()
button = tk.Button(root, text="Press Me!", command=callback)
button.pack()
# starta GUI-loopen
root.mainloop()
Händelser¶
- Förutom att vissa widgets kan ha kommandon kopplade till sig, kan olika former av interaktion med GUI:t producera händelser (en. events).
- Dessa händelser kan kopplas till funktionsobjekt.
- En händelse beskrivs med hjälp av en sträng, t.ex.
"<Button-1>"
som betyder att musknapp 1 har tryckts ner. - Man binder en händelse till ett funktionsobjekt med
bind()
-metoden:
Händelser forts.¶
def key_handler(event):
print("A key was pressed")
- Funktionsobjektet man binder till ett event ska ta in en instans av klassen
Event
som parameter - Från ett
Event
-objekt kan man läsa av diverse information. Se referensmaterialet.
Exempel på händelser¶
- Nedan följer några exempel på händelser man kan binda:
"<Enter>"
när musen förs in i över en widget"<Leave>"
när musen lämnar en widget"<KeyPress>"
när en tangent trycks ner"<KeyRelease>"
när en tangent åker upp igen"<Button-1>"
när musknapp 1 trycks"<Button-2>"
när musknapp 2 trycks
button-event.py¶
In [ ]:
import tkinter as tkx
def callback():
print("Something happened!", end=' ')
def over_me(event):
print("You are over me!", end=' ')
def left_me(event):
print("You left me!", end=' ')
# skapa ett Tk-fönster och knapp
root = tk.Tk()
button = tk.Button(root, text="Press Me!", command=callback)
# bind funktioner till Enter/Leave-händelserna
button.bind("<Enter>", over_me)
button.bind("<Leave>", left_me)
button.pack()
# starta GUI-loopen
root.mainloop()
Label
¶
- En
Label
-instans används som etiketter i ett GUI. - T.ex. för att berätta vad som ska skrivas i ett textfält.
- Man kan välja om texten i etiketten ska var centrerad eller höger- eller vänsterjusterad. Standard är vänsterjusterad.
Entry
¶
- Ett
Entry
är ett textfält som har en rad. - Vi läser från ett
Entry
genom att anropa på dess metodget()
. - Vi ändrar texten som står i ett entry genom att använda metoderna
insert()
ochdelete()
. insert()
behöver ett start-index och en sträng, t.ex.entry1.insert(0, "hej")
.delete()
behöver ett start-index.- Läs mer i referenslitteraturen.
entry.py¶
In [ ]:
import tkinter as tk
# skapa och placera en frame i ett fönster
root = tk.Tk()
def key(event):
"""Skriv ut debugutskrifter för textfält."""
if event.widget == entry1:
print(entry1.get())
if event.widget == entry2:
print("Entry 2 changed")
# Entry 1
entry1 = tk.Entry(root)
entry1.bind("<KeyRelease>", key)
entry1.pack()
# Entry 2
entry2 = tk.Entry(root)
entry2.bind("<KeyRelease>", key)
entry2.pack()
root.mainloop()
Frame
¶
- En behållare (eng. container) för andra widgets.
- Ett användningsområde för
Frame
-objekt är att gruppera widgets. - T.ex. kan flera widgets läggas in i en
Frame
. Sen lägger man till denna till ett fönster.
Layout¶
- Det finns sätt att bestämma hur widgets ska organiseras i en container.
- Följande metoder kan användas för att placera ut
pack
grid
place
- Vi ska koncentrera oss på
grid
.
grid
- Vi lägger ut våra widgets i en matris/tabell/rutnät.
- Matrisen har rader och kolumner av celler.
- En widget placeras på en viss rad, i en viss kolumn.
- En widget kan uppta en eller fler rader eller kolumner.
(I både Android och iOS kan man ha "widgets" på sin startskärm, t.ex. klocka, väder, nyhetsfeed, aktiekurser, mediaspelare, etc. Dessa brukar man oftast kunna placera ut enligt samma grid-princip som tkinter
s grid
-metod använder.)
Exempel på label, textfält och knapp
Exempel på label, textfält och knapp
grid
forts.¶
- En widget kan sträcka sig över fler än en rad/kolumn.
- Vi kan fästa en widget i t.ex. ett hörn eller vid en kant där den stannar om en cell skulle vara större än den widget som finns inuti den.
- Läs mer om
grid
i referensmaterialet
Exempel på label, textfält och knapp
In [ ]:
# grid.py
import tkinter as tk
# Framen läggs automatisk in i en tk.Tk()
frame = tk.Frame()
# placera frame i fönstret med hjälp av pack()
frame.pack()
# titel-label
title_label = tk.Label(frame, text="The best game character ever",
background="red")
title_label.grid(row=0, column=0, columnspan=2, sticky=tk.E+tk.W+tk.N+tk.S)
# namn-label
name_label = tk.Label(frame, text="Name")
name_label.grid(row=1, column=0, sticky=tk.NW)
# name-textfält
name_entry = tk.Entry(frame)
name_entry.grid(row=1, column=1, sticky=tk.NW)
# game-label
game_label = tk.Label(frame, text="Game")
game_label.grid(row=2, column=0, sticky=tk.NW)
# name-textfält
game_entry = tk.Entry(frame)
game_entry.grid(row=2, column=1, sticky=tk.NW)
# knapp
button = tk.Button(frame, text="Ok")
button.grid(row=3, column=1, sticky=tk.SE)
frame.mainloop()
Radiobutton¶
- En grupp av knappar där endast en kan vara aktiv.
- Knappar grupperas ihop genom att de tilldelas samma Tk-variabel att lagra sitt värde i.
- Ett funktionsobjekt kan anges som ett kommando till varje radioknapp.
Tk-variabler¶
- Tk-variabler används av
tkinter för att lagra värden och representerar data i det underliggande Tcl-systemet. Tcl är till skillnad från Python ett statiskt typat språk och vi måste därför deklarera vilken typ av data en viss variabel ska innehålla.
- T.ex. finns
StringVar
som tar hand om strängarIntVar
som tar hand om int:ar
- Exempel:
s = tk.StringVar()
s.set("hej")
print(s.get())
In [ ]:
# radio.py
import tkinter as tk
root = tk.Tk()
def radio():
"""Skriv ut värdet hos radio-knappen."""
print(radio_value.get())
# Tk-variabel att lagra radioknapp-gruppens värde i
radio_value = tk.StringVar()
radio_value.set("svejsan")
# knapp 1
radio_button1 = tk.Radiobutton(root, text="Hejsan", variable=radio_value,
value="hejsan", command=radio)
radio_button1.pack()
# knapp 2
radio_button2 = tk.Radiobutton(root, text="Hoppsan", variable=radio_value,
value="hoppsan", command=radio)
radio_button2.pack()
# knapp 3
radio_button3 = tk.Radiobutton(root, text="Svejsan", variable=radio_value,
value="svejsan", command=radio)
radio_button3.pack()
root.mainloop()
Checkbutton¶
- En knapp som antingen är på eller av.
- Värdet avläses från en Tk-variabel (
IntVar
) som man kopplar till knappen.
Tk-variabler, forts.¶
- En Tk-variabel kan anropa ett funktionsobjekt när förändring sker, antingen när någon läser från den, eller när någon skriver till den.
- Exempel:
def hello(name, index, mode):
print(v.get())
v = tk.StringVar()
v.set("eggs")
v.trace('w', hello)
v.set("bacon")
In [ ]:
# checkbox.py
import tkinter as tk
root = tk.Tk()
def check_changed(name, index, mode):
"""Skriv ut debug-utskrifter för checkboxar."""
# name är namnet på den variabeln som triggade check_changed
# i detta exempel finns två kandidater, check_val1 som har
# namnet "check1" och check_val2 som har namnet "check2".
if name == "check1":
print("check 1: " + str(check_val1.get()), end=' | ')
elif name == "check2":
print("check 2: " + str(check_val2.get()), end=' | ')
# skapa en instans av IntVar som får namnet "check1"
check_val1 = tk.IntVar(name="check1")
# när värdet på check_val1 ändras, kör funktionen check_changed()
check_val1.trace('w', check_changed)
# skapa en instans av IntVar som får namnet "check2"
check_val2 = tk.IntVar(name="check2")
# när värdet på check_val2 ändras, kör funktionen check_changed()
check_val2.trace('w', check_changed)
# skapa två checkbuttons, koppla ihop dem med var sin variabel
checkbutton1 = tk.Checkbutton(root, variable=check_val1,
text="box of check I am")
checkbutton2 = tk.Checkbutton(root, variable=check_val2,
text="box of check I am too")
# lägg till knapparna till layouten
checkbutton1.pack()
checkbutton2.pack()
root.mainloop()
OptionMenu
¶
- Rullgardinsmeny
- har ett värde
- använder en
StringVar
In [ ]:
# optionmenu.py
import tkinter as tk
root = tk.Tk()
var = tk.StringVar(root)
var.set("one") # initial value
option = tk.OptionMenu(root, var, "one", "two", "three", "four")
option.pack()
def ok():
print("value is", var.get(), end=' | ')
button = tk.Button(root, text="OK", command=ok)
button.pack()
tk.mainloop()
Undersöka/testa kod¶
Kod-disposition¶
- importer
- globala variabler och konstanter
- definitioner (funktioner, klasser och metoder)
- delen av koden som faktiskt gör något, helst innanför en
if __name__ == "__main__":
# kod här
Olika sätt att undersöka sin kod¶
- Skriv ut variabler, delresultat, resultat, etc. (spårutskrifter)
- Strössla gärna med spårutskrifter, men gör dem tydliga
- Skriver ni ut variabler med samma namn eller liknande värden, var gärna övertydliga
def my_clever_function(an_argument, another_argument):
print("\n--------------------\n")
print("In my_clever_function:")
print(f"{an_argument=}")
print(f"{another_argument=}")
- Testa med assertions
- syntax:
assert sanningsuttryck
- syntax:
assert my_function(5) > 100
assert my_sorted([5, 2, 6]) == [2, 5, 6]
Exempel, assert¶
- Skriv funktionen
count_integers(value_list)
som returnerar antalet heltal i en lista (argumentetvalue_list
)
In [ ]:
def count_integers(value_list):
num_ints = 0
for value in value_list:
if type(value) == int:
num_ints += 1
return num_ints
print(f"{count_integers([1, 2, 3, 4])=}")
assert count_integers([1, 2, 3, 4, "5"]) == 5
#assert count_integers([1, 2, 3, 4]) == 3
Bryta ut funktioner¶
Bryta ut funktioner, saker att tänka på¶
- Scope
- globala variabler
- lokala variabler
- Lokala variabler
- alla variabler som definieras inne i en funktion (inkl. argumenten) är bara tillgängliga inuti funktionen
- Globala variabler
- variabler som definieras utanför funktioner är globala
- om de ska användas inuti en funktion bör nyckelordet global användas
- Undvik att använda globala variabler. Konstanter är ok, använd i så fall variabelnamn med endast versaler.
Exempel, scope¶
- Globala och lokala variabler: localglobal.py
- Varför blir det fel: localvars.py
- Varför blir det fel: globalvars.py
In [ ]:
# localglobal.py
def print_global_name():
"""Skriv ut globala variabeln name."""
global name
print(f"global name: {name}")
def change_and_print_local_name(name):
"""Ändra och skriv utlokala variabeln name."""
# alla variabler i en funktion är lokala om inte annat anges
secret = "When this function returns, I am gone."
name = name * 2
print(f"changed local name: {name}")
def change_and_print_global_name():
"""Ändra och skriv ut globala variabeln name."""
global name
name = name * 2
print(f"changed global name: {name}")
name = "Ada"
print_global_name()
change_and_print_local_name(name)
print_global_name()
change_and_print_global_name()
print_global_name()
In [ ]:
# localvars.py
def find_min_and_max(values):
min_val = None
max_val = None
for value in values:
if type(value) != int:
continue
if min_val is None and max_val is None:
min_val = value
max_val = value
continue
if value > max_val:
max_val = value
if value < min_val:
min_val = value
print(f"min: {min_val}, max: {max_val}")
my_values1 = ["a", 10, 90, -100, 80, 900, "nehepp"]
my_values2 = ["a", 1, 9, -10, 8, 90, "jahapp"]
find_min_and_max(my_values1)
find_min_and_max(my_values2)
# Här går koden sönder, varför?
print(max_val)
print(min_val)
In [ ]:
# globalvars.py
min_val = None
max_val = None
def find_min_and_max(values):
global min_val, max_val
for value in values:
if type(value) != int:
continue
# om vi inte har något min/max än
if min_val is None and max_val is None:
min_val = value
max_val = value
continue
# uppdatera när vi hittar nytt min/max
if value > max_val:
max_val = value
if value < min_val:
min_val = value
print(f"min: {min_val}, max: {max_val}")
my_values1 = ["a", 10, 90, -100, 80, 900, "nehepp"]
my_values2 = ["a", 1, 9, -10, 8, 90, "jahapp"]
find_min_and_max(my_values1)
print(max_val)
print(min_val)
# varför blir det fel här? Minsta heltalet är ju -10 i my_values2
find_min_and_max(my_values2)
print(max_val)
print(min_val)
Varför bryta ut funktion?¶
- Minska upprepad kod
- lättare att underhålla kod och uppdatera kod
- lättare att läsa kod, inte lika lång
- Minska väldigt snarlik kod
- ersätt snarlika kodavsnitt med funktion som anpassar beteende givet argumenten
- Underlätta läsning av kod – givet att bra funktionsnamn används
Bryta ut funktioner, exempel på tillvägagångssätt¶
- Ta kod som upprepas och stoppa in den i en funktion.
- Lägg till argument till funktionen efter behov.
- Byt ut upprepande kod mot anrop till funktionen du skapat.
In [ ]:
# insertionsort original
def insertionsort(list_to_sort,order):
for i in range(1,len(list_to_sort)):
currentvalue = list_to_sort[i]
pos = i
if order == "ascending":
while (pos >= 1 and list_to_sort[pos - 1] > currentvalue):
list_to_sort[pos]=list_to_sort[pos - 1]
pos = pos - 1
elif order == "descending":
while (pos >= 1 and list_to_sort[pos - 1] < currentvalue):
list_to_sort[pos]=list_to_sort[pos - 1]
pos = pos - 1
list_to_sort[pos]=currentvalue
alist = [54,26,93,17,77,31,44,55,20]
insertionsort(alist, "ascending")
print("Ascending", alist)
alist = [54,26,93,17,77,31,44,55,20]
insertionsort(alist, "descending")
print("Descending", alist)
Exempel: Hur bryter vi ut en jämförelse?¶
- Vad kommer jämförelsen att ersättas med för värde när Python beräknar värdet?
- Vad har jämförelsen för konsekvens?
- Vad kan vi "kalla" jämförelsen för?
if order == "ascending":
while (pos >= 1 and list_to_sort[pos - 1] > currentvalue):
list_to_sort[pos]=list_to_sort[pos - 1]
pos = pos - 1
elif order == "descending":
while (pos >= 1 and list_to_sort[pos - 1] < currentvalue):
list_to_sort[pos]=list_to_sort[pos - 1]
pos = pos - 1
In [ ]:
# insertionsort utbruten
def insertionsort(alist, order):
for i in range(1, len(alist)):
currentvalue = alist[i]
pos = i
while (pos >= 1 and prev_val_should_move(alist[pos-1],
currentvalue, order)):
alist[pos] = alist[pos-1]
pos -= 1
alist[pos] = currentvalue
def prev_val_should_move(value1, value2, order):
if order == "ascending":
return value1 > value2
elif order == "descending":
return value1 < value2
alist = [54,26,93,17,77,31,44,55,20]
insertionsort(alist, "descending")
print("Desc:", alist)
insertionsort(alist, "ascending")
print("Asc:", alist)