Python GUI tkinter

Willemers Informatik-Ecke


Bestellen bei Amazon
2015-07-31

Diese Seiten sind Grundlage meines Python-Buchs aus den ersten Recherchen.

Programme werden erst richtig schön, wenn sie in Fenstern laufen. Python bringt eine eigene grafische Bibliothek mit, die auf Tk basiert, das seinerseits eigentlich mit der Interpretersprache Tcl eingeführt wurde. Tcl wird heute kaum noch verwendet, aber dank Python hat Tk eine weitere Verbreitung gefunden. Ein Tkinter-Programm muss darum die Bibliothek tkinter importieren.

import tkinter

Bei Python 2 hieß tkinter noch Tkinter.

Gegebenenfalls muss tkinter nachinstalliert werden. Unter Debian oder Ubuntu lautet der Befehl:

# apt-get install python-tk

Eine kleine Anwendung

Um Tk zu benutzen, muss die Bibliothek tkinter eingebunden werden. Ein Fenster wird durch den Aufruf von Tk() erzeugt. Es können weitere Elemente erzeugt werden, die als Parameter immer das Elternfenster erwarten. In diesem werden sie sich dann nämlich ihren Platz suchen. Das Beispiel zeigt einen Button (Druckknopf) und ein Label (Beschriftungstafel), die im Fenster dargestellt werden.
#!/usr/bin/python
import tkinter
Fenster = tkinter.Tk()
Anzeige = tkinter.Label(Fenster, text="Guten Tag")
Knopf = tkinter.Button(Fenster, text="Ende")
Anzeige.pack()
Knopf.pack()
Fenster.mainloop()
Die Aufrufe pack() werden zur Darstellung der Elemente benötigt und die Funktion mainloop() startet den Ablauf des Fensters.
Button mit Callback aktivieren
Leider passiert nichts, wenn Sie auf den Knopf drücken. Um das zu erreichen, müssen Sie eine Callback-Funktion definieren, die aufgerufen wird, wenn der Button gedrückt wird. Und welche Funktion die Kontrolle übernimmt, muss bei der Erstellung des Buttons angegeben werden.
#!/usr/bin/python
import tkinter, sys
def ende():
    sys.exit(0)

Fenster = tkinter.Tk()
Anzeige = tkinter.Label(Fenster, text="Guten Tag")
Knopf = tkinter.Button(Fenster, text="Ende", command=ende)
Anzeige.pack()
Knopf.pack()
Fenster.mainloop()
In diesem Fall ruft die Callback-Funktion sys.exit() auf, was dazu führt, dass das Programm beendet wird.

Zugriffe auf Ressourcen

Im Listing haben Sie gesehen, wie zwei Ressourcen des Buttons gesetzt wurden.
Knopf = Button(Fenster, text = "Ende", command = ende)
Die Beschriftung (text) wurde ''Ende'' gesetzt und der Callback (command) auf die Funktion ende gelenkt. Wollen Sie die Ressourcen nachträglich verändern, können Sie die Funktion configure() aufrufen.
Knopf.configure(text = "Ende")

Wenn Sie übrigens die Methode configure() ohne Parameter aufrufen, erhalten Sie für die Abkürzungen wie bd ein Zwei-Tupel, dessen zweiter Wert den langen Namen enthält, im Falle von bd ist dies borderwidth. Für alle anderen Optionen erhalten Sie ein Fünf-Tupel. Es enthält den Namen der Option, den Verweisnamen, den Namen der Klasse, der Vorgabewert und schließlich der aktuelle Wert.

'bd': ('bd', 'borderWidth'),
'borderwidth': ('borderwidth', 'borderWidth', 'BorderWidth', '0', '0'),

Sie können Ressourcen über eine Dictionary-Funktionalität des Buttons verändern. Dazu setzen Sie die Ressource als Schlüsselwert in rechteckigen Klammern ein.

Knopf["text"] = "Ende"

Layout

Das Layout ist die Kunst, Kontrollemente in den Fenstern so anzuordnen, dass der Benutzer sich darin zurecht findet. tkinter bietet dazu mehrere Mechanismen an. Mit pack() wird einfach zusammengepackt und mit dem Grid werden die Elemente gitterartig angeordnet. Sie können Ihre Kontrollelemente auch per Pixeladresse komplett selbst platzieren.

Pack

Im ersten Beispiel haben wir die Elemente mit der Funktion pack() einfach in das Fenster hineingepackt. Bei so einfachen Beispielen funktioniert das auch ohne weitere Parameter schon recht gut. Die Funktion erlaubt aber einige Parameter. Da es so viele davon gibt, drängt sich ein Zugriff über den Namen des Parameters geradezu auf. Damit auch komplexere Strukturen möglich sind, wird ein Frame eingesetzt. Dieses ist ein unsichtbarer Rahmen, der andere Kontrollelemente aufnehmen kann. Jeder Frame kann sein eigenes Layout haben.

Grid

In einem Grid können gleichmäßig ausgerichtete Kontrollelemente wie Eingabemasken sehr einfach angelegt werden. Es besteht aus Spalten (column) und Zeilen (row). Diese sind durchnummeriert und wie Sie vermutlich bereits ahnen, beginnt auch hier die Zählung bei 0.

Ein Kontrollelement wird in dem Grid positioniert, indem die Memberfunktion grid aufgerufen wird und die Position als Parameter übergeben wird.

Knopf.grid(row=0, column=3)
Anzeige.grid(row=2, column=3)
Wenn row oder column auf 0 gesetzt sind, können sie auch weggelassen werden.

Place

Dieses Layout platziert ein Widgets in Pixelpositionen auf das Elternfenster.
place(x=10, y=20, width=50, height=24)
Vorher kann die Geometrie gesetzt werden, um die Größe des Fensters auf die Platzierungen abzustimmen.
root = tk.Tk()
# width x height + x_offset + y_offset:
root.geometry("170x200+30+30") 

Kontrollelemente

Zwei Kontrollelemente haben Sie bereits kennengelernt: Den Button und das Label. Während das Label lediglich dazu dient, etwas anzuzeigen, ist der Button ein aktives Element.

Label

Wie jedes Kontrollelement braucht auch das Label als ersten Parameter das Elternfenster. Da einem Label der Inhalt meist bereits bei der Erstellung zugewiesen wird, kann man diesen auch dem Konstruktor übergeben.
Anzeige = Label(Fenster, text = "Guten Tag")
Sie können einem Label aber auch nachträglich jederzeit einen anderen Text zukommen lassen.
Anzeige["text"] = "Guten Abend"

Button

Ein Button bedient ebenfalls die Ressource text wie ein Label. Die Hauptfunktionalität besteht allerdings darin, dass er gedrückt wird. Der Ressource command wird eine parameterlose Funktion zugewiesen, die aufgerufen wird, wenn jemand drückt.
def callbackFunktion():
    tuirgendwas()

Knopf = Button(elternFenster, 
        text = meldung,
        command = callbackFunktion)

Radiobutton

Radiobuttons treten immer im Rudel auf. Dabei kann immer nur einer der Buttons angewählt sein, so wie bei einem Radio immer nur ein Programm eingeschaltet ist. Ein Radiobutton hat darum drei entscheidende Ressourcen. Alle Radiobuttons mit einem gemeinsamen Elternfenster gehören zusammen. Die Ressource variable bestimmt die Variable, in der der Wert des Radiobutton abgelegt wird, der wiederum in der Ressource value festgelegt wird.
Radiobutton(ElternFenster, text="N3",  variable=sender, value=3)
Radiobutton(ElternFenster, text="ZDF", variable=sender, value=2)
Das folgende Programm erstellt ein kleines Fenster, in dem sich drei Radiobuttons befinden, über die der Fernsehsender ausgewählt werden kann.
from tkinter import *
ElternFenster = Tk()
sender = IntVar()  # tkinter Variablenklasse
sender.set(1)
Liste = [ ("ARD", 1), ("ZDF", 2), ("N3", 3), ("arte", 4) ]

def ZeigAuswahl():
    print(sender.get())

for name, nr in Liste:
    Radiobutton(ElternFenster, text=name, variable=sender,
                command=ZeigAuswahl, value=nr).pack()
mainloop()

Checkbox

Der Check in Checkbox deutet schon an, wofür sie gut ist. Man kann den Button mit einem Haken versehen. Alle Ja-Nein-Fragen können mit diesem Kontrollelement abgefragt werden.
info = IntVar()
Checkbutton(ElternFenster, text="beschriftung", variable=info)
print(info.get())

Eingabefeld Entry

Folgen Sie dem Link!

Listbox mit Scrollbar

Eine Listbox ohne Schiebebalken ist auf Dauer wenig sinnvoll, darum wird es hier im Paket erläutert. Im Gegensatz zu einigen Anderen grafischen Oberflächen wird die Listbox nicht als Kind-Widget in ein Scrollbar-Widget gesetzt. Tkinter betrachtet sie als eigenständige Widgets gleicher Ebene. So muss der Programmierer Listbox und Scrollbar nebeneinander anordnen. Im Beispiel wird dies durch zwei Pack-Aufrufe erledigt. Um ein Listbox-Element logisch mit einem Schiebebalken zu verknüpfen, muss dessen Callback-Ressource yscrollcommand auf die Scrollbar-Methode set() gelenkt werden. Anders herum muss die Ressource command des Schiebebalkens auf die Methode yview() der Listbox gelenkt werden.
from tkinter import *

fenster = Tk()
scrollbar = Scrollbar(fenster)
scrollbar.pack(side=RIGHT, fill=Y)
listbox = Listbox(fenster, yscrollcommand=scrollbar.set)
for i in range(1000):
    listbox.insert(END, str(i))
listbox.pack(side=LEFT, fill=BOTH)
scrollbar.config(command=listbox.yview)
mainloop()
Die Listbox wird über ihre Methode insert() mit Elementen gefüllt.

Menü

Ein ganz einfaches Menü. Es besteht aus einer Menüleiste, einem Dateimenü und einem Menüpunkt für Quit, der das Programm beendet und dazu die gleiche Callback-Funktion ende benutzt, die zuvor schon der Button eingesetzt hat.
MenueLeiste = Menu(Fenster)
DateiMenue = Menu(MenueLeiste, tearoff=0)
MenueLeiste.add_cascade(label="Datei", menu=DateiMenue)
DateiMenue.add_command(label="Quit", command=ende)
Fenster["menu"] = MenueLeiste
Soll noch ein Bearbeiten-Menü hinzukommen, müssen Sie Menu() aufrufen und als Elternfenster MenueLeiste verwenden. Soll dagegen im Dateimenü noch weitere Punkte auftreten, rufen Sie Menu() mit dem Elternfenster DateiMenue auf.

Anschließend müssen die Menübestandteile allerdings noch mit den add-Funktionen eingehängt werden. Dabei steht add_cascade für das kaskadierende Einhängen und add_command für das Einhängen eines Buttons. Mit der Funktion add_radiobutton können Sie sogar Radio-Buttons in einem Menü verwenden. % TODO Folgendes Menue noch aendern:

AuswahlMenue = Menu(MenueLeiste, tearoff=0)
Uled = DoubleVar()
AuswahlMenue.add_radiobutton(label="rot", \
                       variable=Uled,value=1.6, \
                       background="red")
AuswahlMenue.add_radiobutton(label="grün", \
                       variable=Uled,value=2.1, \
                       background="green")
AuswahlMenue.add_radiobutton(label="gelb", \
                       variable=Uled,value=2.2,\
                       background="yellow")
AuswahlMenue.add_radiobutton(label="blau", \
                       variable=Uled,value=2.9,\
                       background="blue")
AuswahlMenue.add_radiobutton(label="wei"s", \
                       variable=Uled,value=4.0,\
                       background="white")
MenueLeiste.add_cascade(label="Farbe", menu=AuswahlMenue)
Vielleicht ist Ihnen die Ressouce tearoff bei der Erstellung des Datei-Menüs aufgefallen. Mit tearoff=0 wurde die Möglichkeit abgeschaltet, Menüs einfach von der Menüleiste abreißen zu können. Tk sieht dies standardmäßig vor. Probieren Sie es aus, indem Sie tearoff=0 einfach entfernen.

Das Widget Canvas als Zeichenfläche

Links


Homepage (C) Copyright 2014, 2015 Arnold Willemer