729G06 – Föreläsning 4 Händelsedriven GUI-programmering

2015-05-03
Översikt
• Olika former av programflöden
729G06 – Föreläsning 4
Händelsedriven
GUI-programmering
• Vad är ett GUI?
• GUI-programmering i Python med Tkinter
• ”Widgetar”
Annika Silvervarg
Ciltab, IDA, Linköpings universitet
1
Textbaserat gränssnitt – Exempel
Textbaserat gränssnitt – Programflöde
Enter name: Amanda
• Linjär/hierarkisk interaktion
Enter title: Giant Slayer
• Visa alternativ för användaren
Enter sex (m/f): f
• Ta emot kommando från användaren
Choose appearance
1. unwashed
2. sparkling
3. retro
• Ta eventuellt emot ytterligare information från användaren
• Interaktion m h a tangentbord
• Typisk interaktion
>1
• Kör kommando
Do you have a magic bag (y/n)? y
• Välj ett alternativ
Are you happy (y/n)? n
• Svara på en fråga
Do you have a secret (y/n)? n
Generate story (y=yes, n=no, start over)? n
GUI = Graphical User Interface
GUI – Exempel
• Gränssnitt - något som ligger mellan två andra saker
• För GUI är det gränssnittet mellan användaren och programmet
Name
Amanda
• Alternativ till GUI: textbaserat gränssnitt, fysiskt gränssnitt
Title
the Giant Slayer
Sex
Female
Male
Appearance
Other
unwashed
has magic bag
is happy
has secret
Reset
Quit
1
2015-05-03
GUI - Programflöde
GUI - Exempel
• Icke-linjär interaktion
• Reaktivt - Händelsestyrt
• Interaktion via mus och tangentbord
• Interaktion med en begränsad och standardiserad uppsättning
widgets (oftast)
GUI – beståndsdelar
Widgets (window gadgets)
• Widgets: instanser av olika typer av GUI-element
• Fönster: dialogrutor, öppna dokument etc
Label
Text input field
Label
Text input field
Drop Down
• Behållare (Containers) : Frame, Panel, Canvas
Radio Button 1
• Funktionalitet för att ordna layout av widgets etc
• Koppling mellan widgets och faktisk funktionalitet
Radio Button 2
Radio Button 3
Selected Checkbox
Unselected Checkbox
Selected Checkbox
Unselected Checkbox
Button 1
Widgets i Tkinter
Button 2
Widgets
• Widgets är objekt (dvs instanser av olika widgetklasser)
• 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 språk också kallade för "layout managers")
http://pythonwiki.tiddlyspot.com/#%5B%5BVisual%20Guide%20to%20Tkinter%20widgets%5D%5D
2
2015-05-03
Ett fönster
Flera fönster
import Tkinter as tk
import Tkinter as tk
# skapa ett Tk-fönster
root= tk.Tk()
# skapa ett Tk-fönster
root = tk.Tk()
# starta GUI-loopen
root.mainloop()
# skapa ett till fönster
top = tk.Toplevel()
Ett fönster i Tkinter är ett objekt av typen Tk. För att utritning av fönster
etc ska göras, måste man starta GUI:ts huvudloop: mainloop()
# starta GUI-loopen
root.mainloop()
För att skapa fler än ett fönster används klassen Toplevel fönster utöver
det första
Skapa widgets
• När man skapar en widget, dvs skapar en instans av en widgetklass,
måste man ange dess förälder. Föräldern är ett fönster eller en widget
som kan agera "container"
Button
• En knapp
• Knappar kan ha text på
• Knappar kan tryckas på
• Knappar kan vara avstängda (disabled) mm
• Widget(parent, attribute1=v1, attribute2=v2,…)
• w = Widget(parent, attribute1=v1, attribute2=v2,…)
Button
import Tkinter as tk
# skapa ett Tk-fönster
root = tk.Tk()
# skapa en knapp
button = tk.Button(root, text="Press Me!")
button.pack()
Widget - Händelse
• Koppla beteende till en widget genom att koppla en funktion/metod
till det
• Vi kan koppla till en funktion som ett kommando som körs när vi
aktiverar en widget
• Vi kan binda ihop en viss händelse relaterat till widgeten med en viss
funktion
# starta GUI-loopen
root.mainloop()
3
2015-05-03
Widget – kommando
Button med kommando
• Man kan koppla ett kommando till vissa widgets
import Tkinter as tk
• T ex knappar:
def callback():
print("Something happened!")
• button = tk.Button(text="OK", command=do_this)
• do_this är namnet på en funktion/metod
# 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
Händelser forts.
• Förutom att vissa widgets kan ha kommandon kopplade till sig, kan
olika former av interaktion med GUI:t producera händelser – events:
• Funktionen man binder till ett event ska ta in en instans av klassen
Event som parameter:
def key_handler(event):
print("A key was pressed”)
• <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
• Från ett event-objekt kan man läsa av diverse information med
event.attribut, där attribut kan vara t ex
• <KeyRelease> - när en tangent åker upp igen
• <Button-1> - när musknapp 1 trycks
• widget – vilken widget instans som genererade händelsen
• <Button-2> - när musknapp 2 trycks
• x, y – musens position i pixlar
• char – teckenkoden för keyboard event som en sträng
• En händelse beskrivs med hjälp av en sträng, t ex "<Button-1>"
• keysym – teknet för ett keyboard event
• Dessa händelser kan kopplas till funktioner/metoder med bind() :
• type – typen av event
• widget.bind("<Button-1>", function)
Button – Händelser
Label
def callback():
print("Something happened!")
• Labels används som etiketter i ett GUI,
t ex för att berätta vad som ska skrivas i ett textfält.
def over_me(event):
print("You are over me!")
print("event type: " + event.type)
print("mouse pos: (" + str(event.x) + ", " + str(event.y) + ")")
• Man kan välja om texten i etiketten ska var centrerad, höger- eller
vänsterjusterad
#label
label1 = tk.Label(root, text="Write something")
label1.pack()
def left_me(event):
print("You left me!")
# skapa ett Tk-fönster
root = tk.Tk()
button = tk.Button(root, text="Press Me!", command=callback)
button.bind("<Enter>", over_me)
button.bind("<Leave>", left_me)
button.pack()
27
4
2015-05-03
Entry
Entry
• Ett Entry är ett textfält som har en rad
def key(event):
if event.widget == entry1:
print(entry1.get())
if event.widget == entry2:
print("Entry 2 changed")
• Vi läser från ett entry genom att anropa dess metod get()
• Vi ändrar texten som står i ett entry genom att använda metoderna
insert() och delete()
• insert() behöver ett start-index och en sträng,
t ex entry1.insert(0, "hej")
entry1 = tk.Entry(root)
entry1.bind("<KeyRelease>", key)
entry1.pack()
• delete() behöver ett index för att ta bort ett tecken, eller
ett start-index och slut-index. Man kan använda Tkinter.END för att
ange slut-index
entry2 = tk.Entry(root)
entry2.bind("<KeyRelease>", key)
entry2.pack()
30
Entry – Button sammankoppling via händelse
Frame
• En behållare (eng container) för andra widgets
def clear_entry2():
entry2.delete(0, tk.END)
• Ett användningsområde för Frames är att gruppera widgets,
t ex kan flera widgets läggas in i en frame, sen lägger man till den
frame:n till ett fönster
button2 = tk.Button(root, text="Clear", command=clear_entry2)
button2.pack()
31
Layout
• Det finns sätt att bestämma hur widgets ska organiseras i en
container:
• 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 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
5
2015-05-03
Layout med grid()
Exempel på label, textfält och knapp
0
0
title_label = tk.Label(frame, text="The best game character ever",
background="red", font = myFont)
title_label.grid(row=0, column=0, columnspan=2, sticky=tk.E+tk.W+tk.N+tk.S)
1
The best game character ever
1
Name
Bob
2
Game
Bob's Great Adventure
3
name_label = tk.Label(frame, text="Name", font = myFont)
name_label.grid(row=1, column=0, sticky=tk.NW)
name_entry = tk.Entry(frame, font = myFont)
name_entry.grid(row=1, column=1, sticky=tk.NW)
Ok
game_label = tk.Label(frame, text="Game", font = myFont)
game_label.grid(row=2, column=0, sticky=tk.NW)
game_entry = tk.Entry(frame, font = myFont)
game_entry.grid(row=2, column=1, sticky=tk.NW)
button = tk.Button(frame, text="Ok", font = myFont)
button.grid(row=3, column=1, sticky=tk.SE)
Radiobutton
Tk-variabler
• En grupp av knappar där endast en kan vara aktiv
• Tk-variabler är objekt som Tkinter använder för att lagra värden i
• Knappar grupperas ihop genom att de tilldelas samma Tk-variabel att
lagra sitt värde i
• T ex finns:
• En funktion kan anges som ett kommando till varje radioknapp
• StringVar som tar hand om strängar
• IntVar som tar hand om int:ar
• En Tk-variabel kan ropa på en funktion när förändring sker, antingen
när någon läser från den, eller när någon skriver till den
Radiobuttons
def radio():
print(radio_value.get())
# Tk-variabel att lagra radioknapp-gruppens värde i
radio_value = tk.StringVar()
radio_value.set("inget valt")
Checkbutton
• En knapp som antingen är på eller av
• Värdet avläses från en Tkinter-variabel (IntVar) som man kopplar till
knappen
radio_button1 = tk.Radiobutton(root, text="Hejsan",
variable=radio_value, value="hejsan", command=radio)
radio_button1.pack()
radio_button2 = tk.Radiobutton(root, text="Hoppsan",
variable=radio_value, value="hoppsan", command=radio)
radio_button2.pack()
radio_button3 = tk.Radiobutton(root, text="Svejsan",
variable=radio_value, value="svejsan", command=radio)
radio_button3.pack()
6
2015-05-03
Checkboxes
def check_changed(name, index, mode):
if name == "check1":
print("check 1: " + str(check_val1.get()))
elif name == "check2":
print("check 2: " + str(check_val2.get()))
Text
• En Text-widget innehåller flera rader text
• Diverse metoder finns för att manipulera innehållet
• För att ändra hela texten används
text.insert(tk.END, ”text som ska visas”)
check_val1 = tk.IntVar(name="check1")
check_val1.trace('w', check_changed)
check_val2 = tk.IntVar(name="check2")
check_val2.trace('w', check_changed)
checkbutton1 = tk.Checkbutton(root, variable=check_val1, text="box of check I am")
checkbutton1.pack()
checkbutton2 = tk.Checkbutton(root, variable=check_val2, text="box of check I am too")
checkbutton2.pack()
Text
text = tk.Text(root, width=25, height=10)
text.insert(tk.END, "This is the text widget.\nWith many lines.")
text.pack()
Combobox
• En combobox visar det alternativ som är valt. Klickar man på dess
expandera-knapp, rullas alternativen ut i en vertikal lista.
• Valt alternativ lagras i en Tk-variabel som är kopplad till comboboxen
• Combobox finns bara i ttk
ttk - widgets med anpassat utseende
• Sedan Tk 8.5 finns modulen ttk som har widgets som efterliknar det
körande operativsystemets stil
• Widgets som finns som ttk-varianter:
• Button
• Checkbutton
• Entry
• Label
• LabelFrame
ttk - widgets med anpassat utseende
• ttk lägger även till fyra nya widgets:
• Combobox
• Notebook
• Progressbar
• Separator
• ttk importeras med:
from Tkinter import ttk
• Menubutton
• PanedWindow
• Radiobutton
• Scale
• Scrollbar
7
2015-05-03
Combobox
def combo_value_changed(name, index, mode):
print(combo_value.get())
combo_value = tk.StringVar()
combo_value.set("Choose")
combobox = ttk.Combobox(root, textvariable=combo_value,
values=["eggs", "bacon", "spam"])
combo_value.trace('w', combo_value_changed)
combobox.pack()
Bra Tkinter-referenser
• Tkinter 8.5 reference: a GUI for Python.
http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/index.html
• An Introduction to Tkinter. 1999. Fredrik Lund
http://www.pythonware.com/library/tkinter/introduction/index.htm
• An introduction to Tkinter. 2005.
http://effbot.org/tkinterbook/
• TkDocs Tutorial.
http://www.tkdocs.com/tutorial/index.html
• OBS! Python 3 ändringar (som ni kan bortse ifrån just nu, Lab4 kör 2.7):
• Tkinter -> tkinter
• import ttk -> from tkinter import ttk
8