Rheinwerk Computing < openbook >

 
Inhaltsverzeichnis
1 Einleitung
2 Die Programmiersprache Python
Teil I Einstieg in Python
3 Erste Schritte im interaktiven Modus
4 Der Weg zum ersten Programm
5 Kontrollstrukturen
6 Dateien
7 Das Laufzeitmodell
8 Funktionen, Methoden und Attribute
9 Informationsquellen zu Python
Teil II Datentypen
10 Das Nichts – NoneType
11 Operatoren
12 Numerische Datentypen
13 Sequenzielle Datentypen
14 Zuordnungen
15 Mengen
16 Collections
17 Datum und Zeit
18 Aufzählungstypen – Enum
Teil III Fortgeschrittene Programmiertechniken
19 Funktionen
20 Modularisierung
21 Objektorientierung
22 Ausnahmebehandlung
23 Iteratoren
24 Kontextobjekte
25 Manipulation von Funktionen und Methoden
Teil IV Die Standardbibliothek
26 Mathematik
27 Kryptografie
28 Reguläre Ausdrücke
29 Schnittstelle zu Betriebssystem und Laufzeitumgebung
30 Kommandozeilenparameter
31 Dateisystem
32 Parallele Programmierung
33 Datenspeicherung
34 Netzwerkkommunikation
35 Debugging und Qualitätssicherung
36 Dokumentation
Teil V Weiterführende Themen
37 Anbindung an andere Programmiersprachen
38 Distribution von Python-Projekten
39 Grafische Benutzeroberflächen
40 Python als serverseitige Programmiersprache im WWW – ein Einstieg in Django
41 Wissenschaftliches Rechnen
42 Insiderwissen
43 Von Python 2 nach Python 3
A Anhang
Stichwortverzeichnis

Download:
- Beispielprogramme, ca. 464 KB

Jetzt Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Python 3 von Johannes Ernesti, Peter Kaiser
Das umfassende Handbuch
Buch: Python 3

Python 3
Pfeil 39 Grafische Benutzeroberflächen
Pfeil 39.1 Toolkits
Pfeil 39.2 Einführung in tkinter
Pfeil 39.2.1 Ein einfaches Beispiel
Pfeil 39.2.2 Steuerelementvariablen
Pfeil 39.2.3 Der Packer
Pfeil 39.2.4 Events
Pfeil 39.2.5 Steuerelemente
Pfeil 39.2.6 Zeichnungen – das Canvas-Widget
Pfeil 39.2.7 Weitere Module
Pfeil 39.3 Einführung in PyQt
Pfeil 39.3.1 Installation
Pfeil 39.3.2 Grundlegende Konzepte von Qt
Pfeil 39.3.3 Entwicklungsprozess
Pfeil 39.4 Signale und Slots
Pfeil 39.5 Wichtige Widgets
Pfeil 39.5.1 QCheckBox
Pfeil 39.5.2 QComboBox
Pfeil 39.5.3 QDateEdit, QTimeEdit, QDateTimeEdit
Pfeil 39.5.4 QDialog
Pfeil 39.5.5 QLineEdit
Pfeil 39.5.6 QListWidget, QListView
Pfeil 39.5.7 QProgressBar
Pfeil 39.5.8 QPushButton
Pfeil 39.5.9 QRadioButton
Pfeil 39.5.10 QSlider, QDial
Pfeil 39.5.11 QTextEdit
Pfeil 39.5.12 QWidget
Pfeil 39.6 Zeichenfunktionalität
Pfeil 39.6.1 Werkzeuge
Pfeil 39.6.2 Koordinatensystem
Pfeil 39.6.3 Einfache Formen
Pfeil 39.6.4 Grafiken
Pfeil 39.6.5 Text
Pfeil 39.6.6 Eye Candy
Pfeil 39.7 Model-View-Architektur
Pfeil 39.7.1 Beispielprojekt: ein Adressbuch
Pfeil 39.7.2 Auswählen von Einträgen
Pfeil 39.7.3 Bearbeiten von Einträgen
 
Zum Seitenanfang

39.2    Einführung in tkinter Zur vorigen ÜberschriftZur nächsten Überschrift

Nachdem wir Ihnen die verschiedenen GUI-Toolkits vorgestellt haben, für die Python-Bindings existieren, möchten wir uns der Programmierung grafischer Benutzeroberflächen widmen. Dazu ist in der Standardbibliothek das Modul tkinter enthalten, über das sich grafische Oberflächen mit dem Tk-Toolkit programmieren lassen. Das Modul tkinter ist die einzige Möglichkeit, ohne die Installation von Drittanbieterbibliotheken eine grafische Benutzeroberfläche in Python zu schreiben.

Deshalb lohnt es sich, einen Blick auf tkinter und seine Möglichkeiten zu werfen.

 
Zum Seitenanfang

39.2.1    Ein einfaches Beispiel Zur vorigen ÜberschriftZur nächsten Überschrift

Zum Einstieg in die Verwendung von tkinter möchten wir ein einfaches Beispielprogramm präsentieren und anschließend besprechen. Das Programm bringt einen Dialog auf den Bildschirm, der den Benutzer dazu auffordert, seinen Namen einzugeben. Durch einen Klick auf einen Button wird der Name umgedreht, die Buchstaben erscheinen also in umgekehrter Reihenfolge. Ein weiterer Button beendet den Dialog. Die folgende Grafik zeigt, wie die Oberfläche später aussehen wird. Links sehen Sie die GUI vor dem Anklicken des Umdrehen-Buttons und rechts danach.

Die erste grafische Oberfläche

Abbildung 39.1    Die erste grafische Oberfläche

Diesem Beispielprogramm liegt der folgende Quelltext zugrunde:

import tkinter
class MyApp(tkinter.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.createWidgets()
def createWidgets(self):
self.nameEntry = tkinter.Entry(self)
self.nameEntry.pack()
self.name = tkinter.StringVar()
self.name.set("Ihr Name...")
self.nameEntry["textvariable"] = self.name
self.ok = tkinter.Button(self)
self.ok["text"] = "Ok"
self.ok["command"] = self.quit
self.ok.pack(side="right")
self.rev = tkinter.Button(self)
self.rev["text"] = "Umdrehen"
self.rev["command"] = self.onReverse
self.rev.pack(side="right")
def onReverse(self):
self.name.set(self.name.get()[::-1])

Zunächst wird das Modul tkinter eingebunden und eine Klasse erstellt, die von tkinter.Frame erbt. Die Basisklasse Frame repräsentiert ein Rahmen-Widget. Das ist ein Steuerelement, das standardmäßig unsichtbar und ohne nennenswerte Funktionalität ist; es stellt den Ausgangspunkt für ein eigenes Widget bzw. einen eigenen Dialog dar. Wir benutzen das Frame-Widget als Container für die Steuerelemente unseres Dialogs.

Im Konstruktor von MyApp werden der Konstruktor der Basisklasse und die Methode pack gerufen. Diese Methode meldet das Widget, für das die Methode aufgerufen wurde, beim sogenannten Packer an. Der Packer ordnet die angemeldeten Steuerelemente dann gemäß den Wünschen des Programmierers auf der Oberfläche an. Insbesondere werden die Positionen der Steuerelemente in der Regel nicht durch feste Koordinaten vorgegeben, sondern durch den Packer dynamisch ermittelt.

Die Methode createWidget wird als letzte im Konstruktor aufgerufen und hat die Aufgabe, die für unseren Dialog benötigten Steuerelemente zu initialisieren. Dazu wird zunächst eine Instanz der Klasse tkinter.Entry erzeugt, die ein Eingabefeld repräsentiert. Dieses wird dann mittels pack auf die Oberfläche gebracht. Danach wird eine Instanz der Klasse tkinter.StringVar unter dem Namen self.name erzeugt, über die wir später den im Eingabefeld self.nameEntry eingegebenen Text auslesen oder verändern können. Initial setzen wir den Text auf »Ihr Name...« und melden die Steuerelementvariable beim Entry-Widget an. Dies geschieht, indem Sie wie bei einem Dictionary den Schlüssel »textvariable« der Entry-Instanz beschreiben.

Auf analoge Art und Weise erzeugen wir im weiteren Verlauf der Funktion noch die beiden Buttons, die im Dialog zur Verfügung stehen sollen. Bei einem Button können auf ähnliche Weise wie bei einem Entry-Widget die Optionen text für die Beschriftung des Buttons und command beschrieben werden. Über die Option command wird eine Funktion festgelegt, die gerufen wird, wenn der Benutzer auf den Button geklickt hat. Im Falle des OK-Buttons setzen wir das command-Attribut auf die quit-Methode. Diese Methode stammt von der Basisklasse Frame und beendet den Dialog.[ 184 ](Wenn Sie das Beispiel in der Entwicklungsumgebung IDLE ausführen, lässt sich der Dialog möglicherweise nicht beenden. Versuchen Sie in diesem Fall, das Beispiel direkt auszuführen. )

Beim Aufruf der pack-Methoden der beiden Buttons sehen Sie einen Weg, dem Packer einen Positionswunsch für ein Steuerelement mitzuteilen. Über den Schlüsselwortparameter side können Sie also beispielsweise festlegen, ob das Steuerelement links- oder rechtsbündig angeordnet werden soll. Dies ist natürlich nicht die einzige mögliche Angabe. Weitere Möglichkeiten lernen Sie im Laufe dieses Kapitels kennen.

Zum Schluss folgt die Implementation der Funktion onReverse, die gerufen wird, wenn der Benutzer auf den Button self.rev geklickt hat. Hier wird über die Steuerelementvariable self.name der im Eingabefeld eingegebene Text ausgelesen, umgedreht und wieder in das Eingabefeld geschrieben.

Wir haben jetzt eine Klasse erstellt, die unseren Dialog repräsentiert. Was noch fehlt, ist der Code, der die Klasse instanziiert und den Dialog anzeigt. Das geht folgendermaßen:

root = tkinter.Tk()
app = MyApp(root)
app.mainloop()

Zunächst wird eine Instanz der Klasse tkinter.Tk erzeugt, die den Dialograhmen repräsentiert. Danach instanziieren wir unsere Applikationsklasse MyApp und übergeben dabei die Tk-Instanz als Vaterinstanz. Durch den Aufruf von mainloop in der dritten Zeile wird der Dialog angezeigt. Die Methode blockiert so lange, bis der Dialog beendet wird.

So viel zum ersten einführenden Tk-Programm. Im Folgenden werden wir uns mit wichtigen Aspekten von Tk befassen, beispielsweise dem Konzept der Steuerelementvariablen und dem Packer. Anschließend besprechen wir die wichtigsten Steuerelemente.

 
Zum Seitenanfang

39.2.2    Steuerelementvariablen Zur vorigen ÜberschriftZur nächsten Überschrift

Im einführenden Beispiel wurden Steuerelementvariablen verwendet, um den Datenaustausch zwischen Programm und Oberfläche zu realisieren. Eine Steuerelementvariable ist an eine bestimmte Information eines Steuerelements gebunden, beispielsweise an den Text eines Eingabefeldes, und enthält stets den Wert, den das Steuerelement momentan anzeigt. Umgekehrt lässt sich über die Steuerelementvariable die entsprechende Information des Steuerelements aus dem Programm heraus verändern.

Es ist klar, dass Steuerelementvariablen nicht zwangsläufig Strings sein müssen, denken Sie an ein Steuerelement zur Eingabe einer ganzen Zahl. Die Basisdatentypen von Python können nicht als Datentypen für Steuerelementvariablen verwendet werden,[ 185 ](Das liegt daran, dass das mit einer Steuerelementvariablen verbundene Widget benachrichtigt werden muss, wenn sich der Wert der Variablen ändert. Dies ist mit den Basisdatentypen nicht möglich. ) hier muss auf spezielle Datentypen des tkinter-Moduls zurückgegriffen werden. Tabelle 39.1 listet alle verfügbaren Typen auf.

Datentyp Entsprechender Python-Typ
tkinter.BooleanVar bool
tkinter.DoubleVar float
tkinter.IntVar int
tkinter.StringVar str

Tabelle 39.1    Datentypen für Steuerelementvariablen

Alle diese Datentypen erben von der Basisklasse tkinter.Variable, deren Konstruktor die im Folgenden beschriebene Schnittstelle hat:

Variable([master, value])

Über das Argument master kann ein Master-Widget angegeben werden. Dies ist insbesondere dann sinnvoll, wenn Sie mehrere Tk-Instanzen gleichzeitig verwenden. Sollte nur eine Tk-Instanz existieren, wird diese automatisch als Master-Widget verwendet, und der Parameter master braucht nicht angegeben zu werden.

Für den Parameter value kann ein Wert angegeben werden, den die erzeugte Instanz speichern soll. Der hier übergebene Wert wird in den Datentyp der Steuerelementvariablen konvertiert. Beachten Sie, dass eine gescheiterte Konvertierung erst beim Aufruf der Methode get auffällt.

Nach der Instanziierung bietet eine Steuerelementvariable die Methoden get und set an, um auf den in ihr gespeicherten Wert zuzugreifen.

>>> t = tkinter.Tk()
>>> v = tkinter.StringVar(value="Hallo Welt")
>>> v.get()
'Hallo Welt'
>>> v.set("Bla Blubb")
>>> v.get()
'Bla Blubb'

Beachten Sie, dass erst beim Aufruf von get eine Exception geworfen wird, wenn die Variable einen ungültigen Wert speichert, während ein Aufruf der Methode set mit einem ungültigen Wert stillschweigend ignoriert wird.

[»]  Hinweis

Eine interessante Eigenschaft von Steuerelementvariablen ist, dass sie von mehreren Steuerelementen gleichzeitig verwendet werden können. Auf diese Weise können einfache Zusammenhänge zwischen den Inhalten der Steuerelemente hergestellt werden.

So lässt sich im einführenden Beispielprogramm die Steuerelementvariable self.name neben dem Eingabefeld self.nameEntry auch der Schaltfläche self.rev zuordnen:

self.rev["textvariable"] = self.name

Auf diese Weise wird der im Eingabefeld eingegebene Name unmittelbar als Beschriftung der Schaltfläche übernommen.

 
Zum Seitenanfang

39.2.3    Der Packer Zur vorigen ÜberschriftZur nächsten Überschrift

Eine grafische Benutzeroberfläche besteht aus einer Reihe von Steuerelementen, die in einer sinnvollen Struktur im Dialog angeordnet sind. Theoretisch ist es möglich, die Position eines jeden Steuerelements »per Hand« festzulegen. Dieser Ansatz ist problematisch, denn der Programmierer interessiert sich in der Regel nicht für die genauen Koordinaten der Steuerelemente, sondern möchte vielmehr die Struktur vorgeben, in der sie angeordnet werden sollen. Zudem müsste der Programmierer beispielsweise das Vergrößern und Verkleinern des Dialogs selbst behandeln und die Steuerelemente gegebenenfalls verschieben oder in ihrer Größe anpassen.

Bei Tk gibt es den sogenannten Packer, der die Aufgabe übernimmt, die Steuerelemente im Dialog anzuordnen. Der Programmierer braucht nur in Form von Anweisungen zur Ausrichtung eines Steuerelements eine Struktur für den Dialog vorzugeben.

Die Steuerelemente eines Dialogs sind in Tk, ähnlich wie bei anderen Toolkits, hierarchisch angeordnet. Das bedeutet, dass jedes Steuerelement über ein übergeordnetes Vaterelement verfügt. Außerdem darf jedes Steuerelement beliebig viele Kindelemente enthalten. In der Tk-Terminologie wird ein Vaterelement Master und ein Kindelement Slave genannt. Die Hierarchie der Steuerelemente in unserem Beispielprogramm zu Beginn dieses Kapitels soll durch die folgende Grafik veranschaulicht werden:

Die Hierarchie der Steuerelemente ist wichtig, um die Arbeitsweise des Packers zu verstehen. Dieser ordnet nämlich die Kindelemente innerhalb ihres Vaterelements an und dann das Vaterelement mitsamt den Kindelementen in dessen Vaterelement. Es ist also sinnvoll, Steuerelemente zu kapseln, damit sie gemeinsam vom Packer angeordnet werden können. Zu diesem Zweck verwendet man häufig das Frame-Widget.

Steuerelementhierarchie im Beispielprogramm

Abbildung 39.2    Steuerelementhierarchie im Beispielprogramm

Wie bereits gesagt, kann dem Packer ein Layout vorgegeben werden, nach dem er die Widgets anzuordnen hat. Im Beispielprogramm haben wir das erreicht, indem wir beim Aufruf der Methode pack der beiden Buttons den Schlüsselwortparameter side mit dem Wert "right" übergeben haben. Der Packer platziert Widgets mit dieser Layoutanweisung rechtsbündig an der Oberfläche. Im Folgenden möchten wir exemplarisch anhand des inzwischen bekannten Programms demonstrieren, wie der Packer funktioniert.

Der Packer arbeitet stets in einem rechteckigen Teilbereich des Fensters (in den folgenden Bildern schwarz umrandet). Zu Beginn, wenn noch kein Widget »gepackt« wurde, ist dies das gesamte Fenster (siehe Abbildung 39.3).

Arbeitsweise des Packers, Schritt 1

Abbildung 39.3    Arbeitsweise des Packers, Schritt 1

Als Erstes wird das Entry-Widget ohne nähere Layoutangabe gepackt. In einem solchen Fall ordnet der Packer die Widgets vertikal an. Das Entry-Widget wird also ganz nach oben und über die volle Breite des Dialogs gelegt (siehe Abbildung 39.4).

Arbeitsweise des Packers, Schritt 2

Abbildung 39.4    Arbeitsweise des Packers, Schritt 2

Interessant ist, was mit dem Arbeitsbereich passiert. Dieser wird verkleinert und umfasst das soeben gepackte Widget nicht mehr. Ein einmal platziertes Widget wird also nicht mehr aufgrund von Layoutangaben späterer Widgets hin- und hergeschoben.

Als Nächstes werden die Buttons in den verbleibenden Arbeitsbereich eingefügt. Diese haben die Layoutanweisung »rechtsbündig« und werden deshalb horizontal vom rechten Dialogrand aus platziert. Sie beanspruchen die vollständige Höhe des Arbeitsbereichs, da sie horizontal platziert werden, und lassen links einen Restarbeitsbereich übrig, in dem der Packer eventuelle weitere Widgets platziert (siehe Abbildung 39.5).

Arbeitsweise des Packers, Schritt 3

Abbildung 39.5    Arbeitsweise des Packers, Schritt 3

So viel zur allgemeinen Arbeitsweise des Packers. Im Folgenden möchten wir die Layoutanweisungen, die dem Packer in Form von Schlüsselwortparametern beim Aufruf der pack-Methode erteilt werden können, genauer unter die Lupe nehmen.

Parameter Mögliche Werte Bedeutung
after Widget Das Steuerelement soll nach dem angegebenen Widget gepackt werden.
anchor "n", "ne", "e", "se", "s", "sw", "w", "nw", "center" Wenn der dem Widget zugeteilte Bereich größer ist als das Widget, kann über anchor die Ausrichtung des Widgets innerhalb dieses Bereichs festgelegt werden. Bei den möglichen Werten handelt es sich um die Himmelsrichtungen sowie »zentriert«.
before Widget Das Steuerelement soll vor dem angegebenen Widget gepackt werden.
expand bool Legt fest, ob die Position des Widgets bei Vergrößerung des Master-Widgets angepasst werden soll (siehe Abbildung 39.7).
fill "x", "y", "both", "none" Die Größe des Widgets wird bei Vergrößerung des Master-Widgets angepasst. Die Größe kann dabei horizontal, vertikal, vollständig oder gar nicht angepasst werden (siehe Abbildung 39.7).
in Widget Fügt das Steuerelement in das angegebene Master-Widget ein.
ipadx
ipady
int Pixelgröße für das horizontale bzw. vertikale innere Padding
padx
pady
int Pixelgröße für das horizontale bzw. vertikale äußere Padding
side "left", "right", "top", "bottom" Die Seite des Arbeitsbereichs, an der das Widget eingefügt wird.
Ein auf der linken bzw. rechten Seite platziertes Widget beansprucht die gesamte Höhe und ein oben bzw. unten platziertes Widget die gesamte Breite des Arbeitsbereichs. Differenziertere Layouts erreichen Sie mithilfe eines Frame-Widgets.

Tabelle 39.2    Layoutanweisungen

Padding

Sicherlich ist Ihnen schon aufgefallen, dass der Dialog, den wir in unserer Beispielanwendung erzeugt haben, recht seltsam aussieht. Das liegt daran, dass die Widgets ohne Zwischenraum direkt aneinandergelegt werden. Mithilfe des sogenannten Paddings (dt. »Füllmaterial«) kann man die Größe dieses Zwischenraums festlegen und so ein weniger gedrungenes Dialogbild erreichen.

Grundsätzlich unterscheidet man zwei Arten von Padding: äußeres und inneres. Das äußere Padding beschreibt den Abstand, mit dem ein Widget neben einem anderen platziert wird. Dieser Wert wird mithilfe der Schlüsselwortparameter padx und pady der pack-Methode übergeben. Als Wert muss hier eine ganze Zahl angegeben werden, die dem gewünschten Abstand in Pixel entspricht. Die folgende Abbildung zeigt unseren Dialog, bei dem jedes Steuerelement ein äußeres Padding von fünf Pixeln verpasst bekommen hat:

Padding im Einsatz

Abbildung 39.6    Padding im Einsatz

Beim inneren Padding handelt es sich um eine Abstandsangabe innerhalb eines Widgets. In diesem Widget enthaltene Slave-Widgets[ 186 ](Slave-Widget ist die TkInter-Terminologie für ein untergeordnetes Steuerelement. Bei der abgebildeten Oberfläche ist das Eingabefeld beispielsweise ein Slave des übergeordneten Frame-Widgets. ) respektieren dieses Padding und werden entsprechend weiter innen angeordnet. Diese Form des Paddings ist besonders für Frame-Widgets interessant. Das innere Padding wird analog zum äußeren Padding über die Schlüsselwortparameter ipadx und ipady festgelegt.

Verhalten bei Vergrößerung des Master-Widgets

Die folgende Grafik demonstriert die Auswirkungen der Layoutanweisungen fill und expand konkret an einem Beispiel:

Die Layoutanweisungen fill und expand

Abbildung 39.7    Die Layoutanweisungen fill und expand

Das obere linke Bild zeigt das Verhalten des Dialogs ohne weitere Layoutanweisungen beim Vergrößern des Fensters. Rechts daneben wurde die Layoutanweisung fill="both" verwendet, unten links expand=True und unten rechts schließlich die Kombination aus expand und fill. Die Layoutanweisungen wurden stets bei allen pack-Aufrufen gleichermaßen übergeben, auch beim pack-Aufruf für das übergeordnete Frame-Widget.

 
Zum Seitenanfang

39.2.4    Events Zur vorigen ÜberschriftZur nächsten Überschrift

Beim Schreiben einer Tk-Anwendung wird nach dem Erstellen und Instanziieren der Applikationsklasse der Kontrollfluss durch Aufruf der Methode mainloop an das Tk-Framework abgegeben. Es stellt sich die Frage, auf welchem Wege wir beispielsweise auf Eingaben des Benutzers reagieren können, wenn wir gar keine wirkliche Kontrolle über das Programm und die grafische Oberfläche haben. Aus diesem Grund ist in Tk eine Reihe von Events definiert. Ein Event ist beispielsweise ein Tastendruck oder Mausklick des Benutzers. Mithilfe der Methode bind eines Widgets können wir eine selbst definierte Methode an ein Event binden. Eine an ein Event gebundene Methode wird vom Tk-Framework immer dann gerufen, wenn das entsprechende Event eintritt, der Benutzer also beispielsweise eine spezielle Taste gedrückt hat.

Events binden

Die Methode bind(event, func, [add]) eines Steuerelements bindet die Funktion func an das Event event. Dabei muss für func das Funktionsobjekt einer Funktion übergeben werden, die genau einen Parameter, das sogenannte Event-Objekt, erwartet. Diese Funktion wird Eventhandler genannt. Wenn für den optionalen Parameter add der Wert True übergeben wird und es bereits andere Funktionen gibt, die an das Event event gebunden sind, werden diese nicht gelöscht, sondern func wird nur in die Liste dieser Funktionen eingereiht. Standardmäßig werden vorherige Bindungen überschrieben.

Für den wichtigsten Parameter event muss ein String übergeben werden, der das Event spezifiziert, an das die Funktion func gebunden werden soll. Eine solche Event-Spezifikation hat die folgende Form:

"<Modifier-Modifier-Type-Detail>"

Die beiden Modifier-Einträge in der Event-Spezifikation sind optional und erlauben es beispielsweise, einen Mausklick bei gedrückter (ª)-Taste und einen normalen Mausklick gesondert zu betrachten. Der Type-Eintrag kennzeichnet den Event-Typ, und über den Detail-Eintrag kann eine nähere Spezifikation erfolgen, beispielsweise kann hier angegeben werden, welche Maustaste gedrückt werden muss, um das Event auszulösen.

Der folgende Aufruf von bind lässt beispielsweise immer dann einen Aufruf der Funktion f auslösen, wenn der Benutzer bei gedrückter (ª)-Taste mit der linken Maustaste in das Widget klickt.

widget.bind("<Shift-ButtonPress-1>", f)

Alternativ lässt sich ein Event für alle Widgets der Applikation auf einmal binden. Dazu wird die Methode bind_all verwendet, die über die gleiche Schnittstelle verfügt wie bind:

widget.bind_all("<Shift-ButtonPress-1>", f)

Die Methoden bind und bind_all geben eine Funktions-ID zurück, über die sich die hergestellte Verbindung referenzieren lässt. Das ist insbesondere dann wichtig, wenn Event-Bindings wieder aufgehoben werden sollen. Das geschieht über die Methoden unbind und unbind_all. Beide Funktionen bekommen einen Event-String übergeben und heben alle mit diesem Event bestehenden Verbindungen auf:

widget.unbind("<Shift-ButtonPress-1>")
widget.unbind_all("<Shift-ButtonPress-1>")

Der Methode unbind kann zusätzlich eine Funktions-ID übergeben werden, um eine konkrete Event-Bindung aufzuheben.

Im Folgenden möchten wir näher auf die Elemente des Event-Strings eingehen.

Event-Modifier

Beginnen wir mit den Werten, die für die Modifier-Einträge in der Event-Spezifikation verwendet werden können. Dabei handelt es sich, wie bereits anhand eines Beispiels demonstriert, um Zusatzbedingungen, unter denen das Type-Event auftreten muss, beispielsweise also die Eingabe eines »a« bei gedrückter (Strg)-Taste. Die wichtigsten Modifier sind in Tabelle 39.3 aufgelistet und erläutert.

Attribut Bedeutung
Alt, Control, Shift, Lock Beim Auftreten des Events muss die spezifizierte Taste ((Alt), (Strg), (ª) bzw. die (º)) gedrückt sein.
Buttonx
Bx
Die Maustaste x muss beim Auftreten des Events gedrückt sein. Die Zahl x spezifiziert dabei, welche Taste gemeint ist: 1 steht für die linke, 2 für die mittlere* und 3 für die rechte Maustaste.
Weitere Maustasten werden entsprechend fortlaufend nummeriert.
Double
Triple
Das Event muss zweimal bzw. dreimal kurz hintereinander auftreten, wie beispielsweise bei einem Doppelklick.
* Das Scrollrad zählt als mittlere Maustaste. Ist weder ein Scrollrad noch eine echte mittlere Maustaste vorhanden, zählt ein gleichzeitiger Klick mit der linken und der rechten Maustaste als Klicken der mittleren Maustaste.

Tabelle 39.3    Modifier

Event-Typen

Nachdem wir uns um die Modifier gekümmert haben, werden wir nun den Type-Eintrag der Event-Spezifikation besprechen. Diese werden in Tabelle 39.4 aufgelistet und erläutert. Sollte ein Detail-Argument erforderlich sein, wird dies ebenfalls erwähnt.

Beachten Sie, dass ein Widget unter Umständen den Eingabefokus haben muss, um ein bestimmtes Event zu empfangen.

Event Bedeutung
KeyPress
Key
Der Benutzer hat eine Taste gedrückt. Über den Detail-Eintrag in der Event-Spezifikation kann das Event auf eine bestimmte Taste beschränkt werden.*
KeyRelease Der Benutzer hat eine Taste losgelassen. Hier können die gleichen Detail-Angaben verwendet werden wie bei KeyPress.
ButtonPress Der Benutzer hat eine Maustaste über dem Widget gedrückt.** Im Detail-Eintrag der Event-Spezifikation kann die Nummer einer Maustaste angegeben werden, um sich auf diese zu beschränken. Dabei entspricht 1 der linken, 2 der mittleren und 3 der rechten Maustaste.
ButtonRelease Der Benutzer hat eine Maustaste losgelassen. Auch hier kann über den Detail-Eintrag in der Event-Spezifikation angegeben werden, welche Maustaste gemeint ist.
Motion Der Benutzer hat die Maus über dem Widget bewegt.
Mithilfe des Buttonx-Modifiers kann dabei festgelegt werden, dass eine bestimmte Maustaste gedrückt sein soll, während der Benutzer die Maus bewegt.
Enter, Leave Der Benutzer hat mit dem Mauszeiger den Bereich des Widgets betreten bzw. verlassen.
FocusIn, FocusOut Das Widget hat den Eingabefokus bekommen bzw. verloren.
Expose Das Widget war vollständig oder zum Teil von anderen Fenstern verdeckt und wurde vom Benutzer wieder hervorgeholt. In diesem Fall müssen möglicherweise Teile des Widgets neu gezeichnet werden.
Destroy Das Widget wurde zerstört.
Configure Das Widget hat seine Größe oder Position verändert.
MouseWheel Der Benutzer hat das Mausrad bewegt.
* Hier sind die folgenden, meist selbsterklärenden Werte möglich: Alt_L, Alt_R, BackSpace, Cancel, Caps_Lock, Control_L, Control_R, Delete, End, Escape, F1F12, Home (»Pos1«), Insert, Left, Up, Right, Down, Next (»Page Down«), Num_Lock, Pause, Print, Prior (»Page Up«), Return, Scroll_Lock, Shift_L, Shift_R, Tab. Außerdem kann ein einzelnes Zeichen angegeben werden.
** Das Widget, über dem die Maustaste gedrückt wurde, bekommt sozusagen ein »Abonnement« auf Maus-Events. Konkret bedeutet das, dass die Release- und Motion-Events auch an dieses Widget gesendet werden, wenn sich die Maus inzwischen über einem anderen Widget befindet. Erst das Loslassen der Taste hebt dieses »Abonnement« auf.

Tabelle 39.4    Events

Eventhandler

Nachdem ein Event ausgelöst wird, wird der damit verbundene Eventhandler aufgerufen. Dieser Handler bekommt dann ein Event-Objekt – genauer: eine tkinter. Event-Instanz – übergeben, das nähere Informationen über das aufgetretene Event enthält. Tabelle 39.5 listet die wichtigsten Attribute des Event-Objekts auf und erklärt sie. Beachten Sie dabei, dass abhängig vom ausgelösten Event nur ein Teil der Attribute sinnvoll ist.

Attribut Bedeutung
char Enthält das eingegebene Zeichen als String.
gültig für: KeyPress, KeyRelease
delta Gibt an, wie weit der Benutzer das Mausrad gedreht hat. Dabei gibt das Vorzeichen die Drehrichtung an.
gültig für: MouseWheel
focus Gibt an, ob das Widget, über dem sich die Maus befindet, den Eingabefokus hat oder nicht.
gültig für: Enter, Leave
height, width Enthält die neue Höhe bzw. Breite des Widgets in Pixel.
gültig für: Configure, Expose
keycode, keysym Enthält den Keycode bzw. den symbolischen Namen der gedrückten Taste.
gültig für: KeyPress, KeyRelease
time Enthält einen ganzzahligen Timestamp, der den Zeitpunkt des Events beschreibt. Es handelt sich um die seit dem Systemstart vergangene Zeit in Millisekunden.
type Enthält den ganzzahligen Identifier des Event-Typs.
widget Referenziert das Widget, in dem das Event aufgetreten ist.
x, y Enthält die X- bzw. Y-Koordinate des Mauszeigers in Pixel. Diese Angabe versteht sich relativ zur linken oberen Ecke des Widgets.
x_root, y_root Enthält die X- bzw. Y-Koordinate des Mauszeigers, diesmal allerdings relativ zur linken oberen Ecke des Bildschirms.
gültig für: ButtonPress, ButtonRelease, KeyPress, KeyRelease, Motion

Tabelle 39.5    Attribute des Event-Objekts

Beispiel

Damit ist die Einführung in die wichtigsten Event-Typen abgeschlossen. An dieser Stelle möchten wir Ihnen ein Beispielprogramm vorstellen, das die Verwendung von Events veranschaulicht. Die grafische Oberfläche des Programms beinhaltet ein Label, ein Widget, das nur einen Text anzeigt, ein Eingabefeld sowie einen Button, über den sich der Dialog beenden lässt. Das Programm soll bestimmte Events, die an das Eingabefeld gesendet werden, empfangen und den Eingang des Events durch eine entsprechende Nachricht im Label bestätigen:

import tkinter
class MyApp(tkinter.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.createWidgets()
self.createBindings()
def createWidgets(self):
self.label = tkinter.Label(self)
self.label.pack()
self.label["text"] = "Bitte sende ein Event"
self.entry = tkinter.Entry(self)
self.entry.pack()
self.ok = tkinter.Button(self)
self.ok.pack()
self.ok["text"] = "Beenden"
self.ok["command"] = self.quit
def createBindings(self):
self.entry.bind("Entenhausen", self.eventEntenhausen)
self.entry.bind("<ButtonPress-1>", self.eventMouseClick)
self.entry.bind("<MouseWheel>", self.eventMouseWheel)
def eventEntenhausen(self, event):
self.label["text"] = "Sie kennen das geheime Passwort!"
def eventMouseClick(self, event):
self.label["text"] = "Mausklick an Position " \
"({},{})".format(event.x, event.y)
def eventMouseWheel(self, event):
if event.delta < 0:
self.label["text"] = "Bitte bewegen Sie das Mausrad"\
" in die richtige Richtung."
else:
self.label["text"] = "Vielen Dank."

Zunächst werden wie gehabt die drei Widgets der Anwendung in der Methode createWidgets initialisiert, die vom Konstruktor aufgerufen wird. Danach wird die Methode createBindings aufgerufen, die dafür zuständig ist, die Eventhandler zu registrieren. In dieser Methode werden drei Events berücksichtigt: das Eingeben der Zeichenfolge »Entenhausen«, das Klicken mit der linken Maustaste und das Bewegen des Mausrads. Beachten Sie, dass sich alle drei Events auf das Eingabefeld, das Entry-Widget, beziehen. Es werden also beispielsweise nur Mausklicks über diesem Eingabefeld als Event registriert.

Danach folgen die Eventhandler-Methoden eventEntenhausen, eventMouseClick und eventMouseWheel. In der Methode eventMouseWheel wird ausgenutzt, dass das Vorzeichen von event.delta angibt, in welche Richtung das Mausrad bewegt wurde.

Wir haben die Verwendung des Label-Widgets noch nicht im Detail besprochen, doch es ist ersichtlich, dass man über den Schlüssel text den Text verändern kann, den es anzeigt. Eine ausführlichere Besprechung dieses Widgets finden Sie im nächsten Abschnitt.

[»]  Hinweis

Eine Bewegung des Mausrads löst unter Windows das in diesem Beispielprogramm verbundene Event MouseWheel aus. Unter Linux werden die beiden Richtungen des Mausrades als alternative Buttons 4 und 5 der Maus interpretiert. Eine Bewegung des Mausrades löst hier eines der Events ButtonPress-4 und ButtonPress-5 aus.

Im Onlineangebot zu diesem Buch (www.rheinwerk-verlag.de/4467) finden Sie eine Variante des Beispielprogramms, die sowohl unter Windows als auch unter Linux funktioniert.

Schließlich fehlt noch der Code, der den Dialog an die Oberfläche bringt. Dieser hat sich in Bezug auf das vorangegangene Beispielprogramm nicht verändert:

root = tkinter.Tk()
app = MyApp(root)
app.mainloop()

Das Beispielprogramm sieht folgendermaßen aus:

Beispielprogramm mit Events

Abbildung 39.8    Beispielprogramm mit Events

Damit ist die Einführung in die Welt der Events beendet. Im Folgenden möchten wir die verschiedenen Steuerelementtypen besprechen, die Sie in tkinter-Anwendungen verwenden können.

 
Zum Seitenanfang

39.2.5    Steuerelemente Zur vorigen ÜberschriftZur nächsten Überschrift

Nachdem wir uns mit den Grundlagen von Tk beschäftigt haben, werden Sie jetzt erfahren, welche Widgets uns zur Verfügung stehen und wie Sie sie benutzen können. Tabelle 39.6 listet alle Tk-Widgets auf, die in diesem Kapitel besprochen werden.

Widget Bedeutung
Widget die Basisklasse aller Steuerelemente
Button eine Schaltfläche
Canvas ein Steuerelement für Zeichnungen und Grafiken
Checkbutton ein Steuerelement, das entweder aktiviert oder deaktiviert sein kann
Entry ein einzeiliges Eingabefeld
Label ein Steuerelement für Beschriftungen
LabelFrame ein Steuerelement für beschriftete Rahmen
Listbox eine Liste von Einträgen
Menu ein Kontextmenü
Menubutton eine Schaltfläche, die ein Kontextmenü anzeigt, wenn sie angeklickt wird
OptionMenu eine Schaltfläche, die eine Auswahlliste anzeigt, wenn sie angeklickt wird
Radiobutton Ein Steuerelement, das entweder aktiviert oder deaktiviert sein kann. Innerhalb einer Gruppe darf nur genau ein Radiobutton aktiviert sein.
Scrollbar eine Leiste, die das Scrollen übergroßer Widgets ermöglicht
Spinbox ein Steuerelement zum Einstellen eines Zahlenwertes
Text ein mehrzeiliges Eingabefeld

Tabelle 39.6    Erklärte Widgets

[»]  Hinweis

Die zu den jeweiligen Widgets vorgestellten Beispielprogramme sind verkürzt dargestellt, um das grundlegende Prinzip zu veranschaulichen. Im Onlineangebot zu diesem Buch (www.rheinwerk-verlag.de/4467) finden Sie etwas ausführlichere und lauffähige Versionen der Beispielprogramme.

Die Basisklasse Widget

Die Klasse tkinter.Widget ist die Basisklasse aller Widgets, sie stellt eine Grundfunktionalität bereit, die für jedes Widget verfügbar ist. Dazu zählen zunächst einmal die sogenannten winfo-Methoden (für Widget Info), mit deren Hilfe Sie Informationen über ein Widget, beispielsweise seine Position, herausfinden können. In Tabelle 39.7 werden die wichtigsten winfo-Methoden erläutert. Dabei sei w ein Widget.

Schnittstelle Bedeutung
w.winfo_children() Gibt eine Liste der Unter-Widgets von w zurück.
w.winfo_class() Gibt den Namen der Widget-Klasse von w als String zurück.
w.winfo_geometry() Gibt die Position und die Dimension des Widgets w relativ zum übergeordneten Widget in Form eines Strings des Formats "BxH+X+Y" zurück.
w.winfo_height()
w.winfo_width()
Gibt die Höhe bzw. Breite des Widgets w in Pixel zurück.
w.winfo_pointerx()
w.winfo_pointery()
w.winfo_pointerxy()
Gibt die X-, Y-Koordinate des Mauszeigers bzw. ein Tupel aus beiden Koordinaten relativ zur oberen linken Ecke des Bildschirms zurück.
w.winfo_rootx()
w.winfo_rooty()
Gibt die X- bzw. Y-Position des Widgets w relativ zur oberen linken Ecke des Bildschirms zurück.
w.winfo_screenheight()
w.winfo_screenwidth()
Gibt die Höhe bzw. Breite des Bildschirms in Pixel zurück.
w.winfo_x()
w.winfo_y()
Gibt die X- bzw. Y-Koordinate des Widgets w relativ zum übergeordneten Widget zurück.

Tabelle 39.7    winfo-Methoden

Wie Sie es bereits aus vorangegangenen Beispielen kennen, kann ein Widget konfiguriert werden. Dazu wird wie bei einem Dictionary auf Konfigurationsschlüssel zugegriffen.

w["width"] = 200 
w["height"] = 100

Alternativ können Widgets beim Instanziieren konfiguriert werden. Dazu können dem jeweiligen Konstruktor die gewünschten Einstellungen in Form von Schlüsselwortparametern übergeben werden:

frame = tkinter.Frame(width=200, height=200)

Viele Widgets verfügen über die Schlüssel width und height für die Breite bzw. Höhe des Widgets,[ 187 ](Breite und Höhe werden abhängig vom Steuerelement in Pixel bzw. Zeichenbreiten und Zeilenhöhen angegeben. ) padx und pady für das horizontale bzw. vertikale Padding, state für den Zustand des Widgets[ 188 ](Mögliche Werte sind hier zum Beispiel "enabled", "disabled" und "active". ) sowie foreground und background für Vordergrund- bzw. Hintergrundfarbe.

Welche weiteren Schlüssel ein Widget anbietet, hängt von seinem Typ ab und kann in diesem Kapitel nicht erschöpfend behandelt werden. In den folgenden Abschnitten lernen Sie die wichtigsten in tkinter enthaltenen Widget-Typen kennen.

Button

Eine Instanz der Klasse tkinter.Button stellt ein Button-Widget dar und entspricht einer Schaltfläche in der grafischen Oberfläche. Sie haben bereits im Beispielprogramm gesehen, wie man einen Button verwendet. Wichtig ist dabei, nach dem Instanziieren der Button-Klasse die Optionen text und command zu setzen, über die die Beschriftung des Buttons und die Handler-Funktion festgelegt werden. Die Handler-Funktion wird vom Tk-Framework gerufen, wenn der Benutzer auf den Button geklickt hat:

class MyApp(tkinter.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.ok = tkinter.Button(self)
self.ok["text"] = "Beschriftung"
self.ok["command"] = self.handler
self.ok.pack()
def handler(self):
print("Button gedrückt")

Nach der Einrichtung des Buttons ok wird stets die Methode handler aufgerufen, wenn der Benutzer den Button anklickt.

Checkbutton

Der Checkbutton, auch Checkbox genannt, ist ein mit dem Button verwandtes Steuerelement. Ein Checkbutton besteht aus einer kleinen Box und einer Beschriftung und kann zwei Status annehmen: aktiviert und deaktiviert. Der Benutzer kann durch einen Klick auf die kleine Box den Status ändern. Der aktivierte Zustand eines Checkbuttons wird durch ein Häkchen oder ein Kreuz in der Box angezeigt.

Ein Checkbutton im Einsatz

Abbildung 39.9    Ein Checkbutton im Einsatz

Erstellung und Verwendung eines Checkbuttons sind denkbar einfach und funktionieren ähnlich wie bei einem Button. Zusätzlich muss aber eine Steuerelementvariable für den aktuellen Status des Checkbuttons angelegt werden:

check = tkinter.Checkbutton(master)
check["text"] = "Hallo Welt"
checked = tkinter.BooleanVar()
checked.set(True)
check["variable"] = checked
check.pack()

Nach der Instanziierung des Checkbuttons kann über die Option text eine Beschriftung festgelegt werden. Dann wird mithilfe der Option variable eine Steuerelementvariable für den Status des Checkbuttons zugewiesen. Schließlich wird der Checkbutton durch Aufruf der Methode pack an die grafische Oberfläche gebracht.

Analog zu einem Button kann über die Option command eine Handler-Funktion angegeben werden, die vom Tk-Framework gerufen wird, wenn der Benutzer den Status des Checkbuttons wechselt. Innerhalb dieser Handler-Funktion kann dann über die Steuerelementvariable checked der aktuelle Status des Checkbuttons erfragt werden.

Checkbuttons treten häufig nicht alleine auf, sondern repräsentieren Einstellungsgruppen, deren einzelne Elemente unabhängig voneinander aktiviert bzw. deaktiviert werden können. Das folgende komplexere Beispielprogramm zeigt, wie mehrere Checkbuttons erzeugt und ihre Events von einer gemeinsamen Handler-Funktion verarbeitet werden können:

class MyApp(tkinter.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.names = ("Donald Duck", "Dagobert Duck", "Gustav Gans")
self.checks = []
self.vars = []
for name in self.names:
var = tkinter.BooleanVar()
var.set(False)
check = tkinter.Checkbutton(self)
check["text"] = name
check["command"] = self.handler
check["variable"] = var
check.pack(anchor="w")
self.checks.append(check)
self.vars.append(var)
def handler(self):
x = " und ".join(
[name for name, var in zip(self.names, self.vars) if var.get()]
)

Für jeden Eintrag des Tupels self.names werden ein Checkbutton und eine Steuerelementvariable erzeugt und jeweils in den Listen self.checks und self.vars abgelegt. Jedem erzeugten Checkbutton wird die gemeinsame Handler-Funktion handler zugewiesen. In diesem Handler wird mithilfe der Built-in Function zip über Tupel aus Namen und korrespondierender Steuerelementvariablen iteriert, um diejenigen Namen auszugeben, deren Checkboxen vom Benutzer ausgewählt wurden.

Radiobutton

Ein Radiobutton ist wie ein Checkbutton ein Steuerelement, das durch einen Klick des Benutzers aktiviert oder deaktiviert werden kann. Das Besondere am Radiobutton ist, dass man eine Gruppe von Radiobuttons definieren kann, innerhalb derer immer genau einer der Radiobuttons aktiviert ist. Dabei bilden die Radiobuttons eine Gruppe, die sich dieselbe Steuerelementvariable teilt. Im folgenden Beispiel-Code darf sich der Benutzer die Hauptstadt eines G8-Staates aussuchen:

class MyApp(tkinter.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.auswahl = ["Berlin", "London", "Moskau", "Ottawa",
"Paris", "Rom", "Tokio", "Washington DC"]
self.stadt = tkinter.StringVar()
self.stadt.set("Moskau")
for a in self.auswahl:
b = tkinter.Radiobutton(self)
b["text"] = a
b["value"] = a
b["variable"] = self.stadt
b["command"] = self.handler
b.pack(anchor="w")
def handler(self):
print(self.stadt.get())

Zunächst werden eine Liste aller Städte, die zur Auswahl stehen sollen, und eine Steuerelementvariable angelegt. Diese Variable enthält später stets den Namen der Stadt, die aktuell vom Benutzer ausgewählt ist. Zu Beginn wird Moskau ausgewählt.

Dann wird in einer Schleife über alle Städte iteriert und für jede Stadt ein Radiobutton angelegt. In diesem Fall werden die Optionen text für die Beschriftung, value für den Wert, den die Steuerelementvariable annimmt, wenn der Radiobutton ausgewählt wird, variable für die Steuerelementvariable und command für die Handler-Funktion gesetzt. Analog zum Checkbutton wird diese Handler-Funktion immer dann gerufen, wenn der Benutzer seine Auswahl verändert.

Die grafische Oberfläche des oben dargestellten Beispielprogramms sehen Sie in Abbildung 39.10.

Eine Menge Radiobuttons

Abbildung 39.10    Eine Menge Radiobuttons

Entry

Bei einem Entry-Widget handelt es sich um ein einzeiliges Eingabefeld, in das der Benutzer beliebigen Text schreiben kann. Der folgende Beispiel-Code erzeugt ein Entry-Widget und schreibt einen Text hinein:

class MyApp(tkinter.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.entryVar = tkinter.StringVar()
self.entryVar.set("Hallo Welt")
self.entry = tkinter.Entry(self)
self.entry["textvariable"] = self.entryVar
self.entry.pack()
self.entry.bind("<Return>", self.handler)
def handler(self, event):
print(self.entryVar.get())

Es werden ein Entry-Widget und eine Steuerelementvariable instanziiert. Dann wird die Steuerelementvariable auf einen Wert gesetzt und mit dem Eingabefeld verbunden. Nachdem das Eingabefeld dem Packer übergeben wurde, verbinden wir noch das Event Return, das beim Drücken der (¢)-Taste im Eingabefeld ausgelöst wird, mit einer Handler-Funktion, die den aktuellen Inhalt des Eingabefeldes ausgibt. Die grafische Oberfläche dieses Beispielprogramms sieht so aus:

Ein Entry-Widget

Abbildung 39.11    Ein Entry-Widget

Label

Ein Label-Widget ist ein sehr einfaches Widget, dessen einzige Aufgabe es ist, einen Text auf der grafischen Oberfläche anzuzeigen. Das folgende Beispielprogramm zeigt exemplarisch die Verwendung des Label-Widgets:

class MyApp(tkinter.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.label = tkinter.Label(self)
self.label["text"] = "Hallo Welt"
self.label.pack()

Nach dem Instanziieren der Klasse tkinter.Label wird über die Option text die Beschriftung des Labels verändert und das Label schließlich dem Packer übergeben. Anstatt die Option text zu verwenden, wäre es an dieser Stelle auch möglich gewesen, über die Option textvariable eine Steuerelementvariable zu definieren und diese mit dem gewünschten Text zu beschreiben.

Die grafische Oberfläche dieses Beispielprogramms ist wenig spektakulär und in folgender Abbildung zu sehen:

Ein Label im Einsatz

Abbildung 39.12    Ein Label im Einsatz

LabelFrame

Ein LabelFrame-Widget ist eine spezielle Form des Frame-Widgets und dient zur Gruppierung von Steuerelementen. Das LabelFrame-Widget zeichnet einen beschrifteten Rahmen um die ihm untergeordneten Widgets. Im folgenden Beispielprogramm wird das Checkbutton-Beispiel um einen LabelFrame erweitert:

class MyApp(tkinter.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.names = ("Donald Duck", "Dagobert Duck", "Gustav Gans")
self.group = tkinter.LabelFrame(self)
self.group["text"] = "Entenhausen"
self.group.pack()
self.checks = []
self.vars = []
for name in self.names:
var = tkinter.BooleanVar()
var.set(False)
check = tkinter.Checkbutton(self.group)
check["text"] = name
check["command"] = self.handler
check["variable"] = var
check.pack(anchor="w")
self.checks.append(check)
self.vars.append(var)
def handler(self):
print(" und ".join(
[name for name, var in zip(self.names, self.vars) if var.get()])
)

Die Verwendung des LabelFrame-Widgets beschränkt sich auf die Instanziierung der tkinter.LabelFrame-Klasse, die Angabe der text-Option für die Beschriftung des Rahmens und die Erzeugung der untergeordneten Widgets. Die in diesem Beispiel erzeugte grafische Oberfläche sieht folgendermaßen aus:

Ein LabelFrame-Widget

Abbildung 39.13    Ein LabelFrame-Widget

Listbox

Bei einer Listbox handelt es sich um ein Steuerelement, das eine Liste von Einträgen darstellt. Je nach Anwendung darf der Benutzer einen oder mehrere Einträge auswählen oder modifizieren. Im einfachsten Fall kann eine Listbox folgendermaßen erzeugt werden:

class MyApp(tkinter.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.eintraege = ["Berlin", "London", "Moskau", "Ottawa",
"Paris", "Rom", "Tokio", "Washington DC"]
self.lb = tkinter.Listbox(master)
self.lb.insert("end", *self.eintraege)
self.lb.pack()

Zunächst legen wir die Liste self.eintraege an, die die Einträge enthält, die wir später in die Listbox schreiben möchten. Dann wird eine Instanz der Klasse tkinter.Listbox erzeugt und gepackt. Schließlich rufen wir für jeden gewünschten Eintrag die Methode insert der Listbox auf, die den jeweiligen Eintrag ans Ende der Listbox anhängt.

Die von diesem Quellcode erzeugte Listbox sieht folgendermaßen aus:

Die fertige Listbox

Abbildung 39.14    Die fertige Listbox

Die Einträge einer Listbox werden von 0 beginnend durchnummeriert. Über diesen Index kann auf die Einträge zugegriffen werden. Dazu definiert die Klasse Listbox eine Reihe von Methoden, die in Tabelle 39.8 zusammengefasst werden. Einige der Methoden bekommen dabei einen Index first und einen optionalen Index last übergeben. Wenn last angegeben wird, bezieht sich die Methode sinngemäß auf alle Einträge mit einem Index zwischen first und last. Wird last nicht angegeben, beziehen sie sich ausschließlich auf das Element mit dem Index first. Anstelle konkreter Indizes kann für first und last auch der String "end" übergeben werden.

Methode Bedeutung
curselection() Gibt eine Liste mit den Indizes der aktuell ausgewählten Einträge zurück.
delete(first, [last]) Löscht einen bzw. mehrere Einträge.
get(first, [last]) Gibt einen bzw. mehrere Einträge als String zurück.
insert(index, [*elements]) Fügt die Elemente elements an der Position index in die Listbox ein.
selection_clear(first, [last]) Hebt eine eventuelle Auswahl eines bzw. mehrerer Einträge auf.
selection_includes(index) Gibt an, ob ein Eintrag ausgewählt ist.
selection_set(first, [last]) Wählt ein bzw. mehrere Elemente aus.
size() Gibt die Anzahl der Einträge zurück.

Tabelle 39.8    Methoden einer Listbox

Das eingangs besprochene Beispielprogramm zur Listbox war statisch. Der Benutzer konnte zwar einen Eintrag der Listbox auswählen, doch passiert ist daraufhin nichts. Das folgende Beispielprogramm zeigt, wie man auf eine Änderung der Benutzerauswahl reagieren kann. Dazu erstellen wir die folgende Applikationsklasse:

class MyApp(tkinter.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.eintraege = ["Berlin", "London", "Moskau", "Ottawa",
"Paris", "Rom", "Tokio", "Washington DC"]
self.lb = tkinter.Listbox(self)
self.lb.pack(fill="both", expand="true")
self.lb["selectmode"] = "extended"
self.lb.insert("end", *self.eintraege)
self.lb.bind("<<ListboxSelect>>", self.selectionChanged)
self.lb.selection_set(0)
self.label = tkinter.Label(self)
self.label.pack()
self.selectionChanged(None)

Im Konstruktor wird zunächst eine Listbox angelegt und mit den bereits bekannten Städtenamen gefüllt. Der Benutzer soll eine beliebige Menge von Städten auswählen können. Dieses Verhalten entspricht dem Wert extended des Konfigurationsschlüssels selectmode. Andere mögliche Werte sind single, browse und multiple.

Danach verbinden wir eine Handler-Methode mit dem sogenannten virtuellen Event <<ListboxSelect>>. Ein virtuelles Event ist ein spezielles Event, das nur mit einem bestimmten Widget-Typ verwendet werden kann. Das <<ListboxSelect>>-Event wird immer dann gerufen, wenn der Benutzer die Auswahl in der Listbox verändert hat.

Dann wird das erste Element der Listbox als einziges ausgewählt und ein Label-Widget erzeugt. Zum Schluss wird die Handler-Methode selectionChanged aufgerufen, um das Label-Widget mit einem sinnvollen Text zu versehen. Die selectionChanged-Methode sieht folgendermaßen aus:

    def selectionChanged(self, event):
self.label["text"] = "Wir fahren nach: " + ", ".join(
(self.lb.get(i) for i in self.lb.curselection()))

Jedes Mal, wenn der Benutzer die Auswahl in der Listbox verändert hat, wird diese Methode gerufen. Hier werden die Indizes der ausgewählten Einträge ausgelesen, die dazugehörigen Städtenamen herausgefunden und durch ein Komma getrennt in das Label geschrieben.

Die mit diesem Beispielprogramm erzeugte grafische Oberfläche ist in der folgenden Abbildung dargestellt:

Eine Listbox mit Benutzerinteraktion

Abbildung 39.15    Eine Listbox mit Benutzerinteraktion

Menu

Bei einer komplexeren grafischen Benutzeroberfläche befindet sich direkt unter der Titelleiste eines Dialogs häufig eine Menüleiste, die mehrere Menüs enthält. Ein Menü ist eine Schaltfläche, über die der Benutzer eine Liste weiterer Kommandos erreichen kann. Üblicherweise findet man zum Beispiel im Menü Datei die Kommandos Speichern und Speichern unter. Das folgende Beispielprogramm zeigt exemplarisch, wie Sie einen tkinter-Dialog mit einem Menü ausstatten können:

class MyApp(tkinter.Frame):
def __init__(self, master):
super().__init__(master)
self.pack()
self.menuBar = tkinter.Menu(master)
master.config(menu=self.menuBar)
self.fillMenuBar()
def fillMenuBar(self):
self.menuFile = tkinter.Menu(self.menuBar, tearoff=False)
self.menuFile.add_command(label="Öffnen", command=self.handler)
self.menuFile.add_command(label="Speichern", command=self.handler)
self.menuFile.add_command(label="Speichern unter",
command=self.handler)
self.menuFile.add_separator()
self.menuFile.add_command(label="Beenden", command=self.quit)
self.menuBar.add_cascade(label="Datei", menu=self.menuFile)
def handler(self):
print("Hallo Welt!")

Die tkinter.Tk-Instanz, die jeder tkinter-Anwendung zugrunde liegt, besitzt eine Option menu, über die eine Menüleiste gesetzt werden kann. Das geschieht innerhalb des Konstruktors der Klasse MyApp, in dem über den Parameter master auf die tkinter.Tk-Instanz zugegriffen werden kann. Dort wird zunächst die Menüleiste als tkinter.Menu-Instanz erzeugt und schließlich über die Option menu als Menüleiste eingetragen.

Die Methode fillMenuBar, die vom Konstruktor aus aufgerufen wird, hat die Aufgabe, die frisch erzeugte Menüleiste zu befüllen. Dazu wird zunächst ein Menü erzeugt, das fortan unter dem Namen menuFile verfügbar ist. Über den Parameter tearoff kann gesteuert werden, ob ein Ablösen des Menüs möglich sein soll. Dieses Verhalten ist bei den meisten Desktop-Umgebungen unpassend und wurde deshalb nicht zugelassen. Sie können aber mit dieser Einstellung experimentieren.

Danach werden dem Menü über die Methode add_command Menüpunkte hinzugefügt. Diese erhalten eine Beschriftung (label) und eine Handler-Funktion (command), die analog zur Handler-Funktion eines Buttons aufgerufen wird, wenn der Benutzer den jeweiligen Menüpunkt angewählt hat. In diesem Beispiel wird dann die Methode handler aufgerufen, die durch Ausgabe eines Textes demonstriert, dass das Beispielprogramm funktioniert. Einzig beim Menüpunkt Beenden wird self.quit als Handler-Methode eingetragen, um die Anwendung zu beenden.

Über die Methode add_separator kann eine Trennlinie ins Menü eingefügt werden, um thematisch zusammengehörende Menüpunkte auch optisch zu gruppieren.

Schließlich wird über die Methode add_cascade der Menüleiste das neue Menü unter dem Titel Datei hinzugefügt.

Die grafische Oberfläche des Beispielprogramms sieht folgendermaßen aus:

Eine Menüleiste

Abbildung 39.16    Eine Menüleiste

Dieses simple Beispiel lässt sich noch erweitern, denn neben den Methoden add_command zum Hinzufügen eines einfachen Menüpunktes und add_separator zum Hinzufügen einer Trennlinie verfügt eine Menu-Instanz noch über die Methoden add_checkbutton und add_radiobutton. Diese beiden Methoden erlauben es, Radiobuttons und Checkbuttons in einem Menü zu verwenden. Die Optionen, die die Radio- bzw. Checkbuttons näher spezifizieren, werden den Methoden als Schlüsselwortparameter übergeben:

Eine komplexere Menüleiste

Abbildung 39.17    Eine komplexere Menüleiste

Menubutton

Bei einem Menubutton handelt es sich um eine Schaltfläche, die ein Menü anzeigt, wenn der Benutzer sie anklickt. Wie ein solches Steuerelement verwendet werden kann, zeigt der folgende Beispiel-Code:

class MyApp(tkinter.Frame):
def __init__(self, master):
super().__init__(master)
self.pack()
self.mb = tkinter.Menubutton(self, text="Hallo Welt")
self.menu = tkinter.Menu(self.mb, tearoff=False)
self.menu.add_checkbutton(label="Donald Duck")
self.menu.add_checkbutton(label="Onkel Dagobert")
self.menu.add_checkbutton(label="Tick, Trick und Track")
self.mb["menu"] = self.menu
self.mb.pack()

Zunächst wird eine Instanz der Klasse tkinter.Menubutton erzeugt. Die Beschriftung der Menubutton-Schaltfläche wird dabei über die Option text festgelegt. Danach erstellen wir das Menü, das beim Anklicken des Menubuttons angezeigt werden kann. Dabei handelt es sich um eine tkinter.Menu-Instanz, die wie im vorangegangenen Abschnitt erzeugt und verwendet werden kann.

Schließlich wird das Menü über die Option menu des Menubuttons an diesen gebunden und der Menubutton an den Packer übergeben. Die resultierende grafische Oberfläche sieht wie folgt aus:

Ein Menubutton im Einsatz

Abbildung 39.18    Ein Menubutton im Einsatz

Die Beschriftung des Menubuttons ändert sich unabhängig von dem im Menü ausgewählten Eintrag nicht.

OptionMenu

Bei dem vorhin besprochenen Menubutton haben wir festgestellt, dass sich die Beschriftung des Buttons nicht automatisch ändert, wenn der Benutzer einen Menüpunkt angewählt hat. Dieses Verhalten ist jedoch häufig gewünscht. Ein Steuerelement, das sich so verhält, wird oft als Dropdown-Liste bezeichnet. Im Tk-Framework nennt sich dieses Steuerelement OptionMenu. Der folgende Code zeigt, wie Sie das OptionMenu-Widget verwenden können:

class MyApp(tkinter.Frame):
def __init__(self, master):
super().__init__(master)
self.pack()
self.lst = ["Linux", "macOS", "Windows"]
self.var = tkinter.StringVar()
self.var.set("Linux")
self.op = tkinter.OptionMenu(self, self.var, *self.lst,
command=self.handler)
self.op.pack()
def handler(self, text):
print(text, self.var.get())

Nachdem wir eine Liste mit den für die Dropdown-Liste gewünschten Einträgen und eine Steuerelementvariable angelegt haben, instanziieren wir eine tkinter.OptionMenu-Instanz und übergeben dabei sowohl die Steuerelementvariable var als auch die Liste der Einträge. Beachten Sie, dass die Liste der Einträge ausgepackt wird, die Einträge also in Form von Positionsparametern übergeben werden müssen.

Über den Schlüsselwortparameter command wird eine Handler-Funktion festgelegt, die aufgerufen wird, wenn der Benutzer die Auswahl verändert hat. Im Gegensatz zu den Handler-Funktionen aus den vorangegangenen Beispielen bekommt handler in diesem Fall den Namen der neuen Auswahl übergeben. In der Handler-Funktion geben wir den Namen der neuen Auswahl zweimal aus: einmal durch den übergebenen Parameter und ein weiteres Mal durch Auslesen der Steuerelementvariablen. Beide Varianten sollten stets den gleichen Namen liefern.

Das vom Beispielprogramm erzeugte GUI sieht folgendermaßen aus:

Ein OptionMenu-Widget im Einsatz

Abbildung 39.19    Ein OptionMenu-Widget im Einsatz

Um auf eine Auswahl des Benutzers reagieren zu können, existiert wieder die Option command. Über diese Option kann eine Handler-Funktion angegeben werden, die immer dann vom Tk-Framework gerufen wird, wenn der Benutzer eine Auswahl getroffen hat. Die Handler-Funktion muss einen Parameter, den Text des aktuell ausgewählten Elements, erwarten.

Scrollbar

Es kommt häufig vor, dass der Inhalt eines Widgets, beispielsweise die Einträge einer Liste, mehr Platz benötigen, als das Widget bietet. Für solche Fälle erlauben es bestimmte Widget-Typen, eine sogenannte Scrollbar anzubinden. Das folgende Beispiel zeigt, wie Sie eine Scrollbar im Zusammenhang mit einer Listbox verwenden:

class MyApp(tkinter.Frame):
def __init__(self, master):
super().__init__(master)
self.pack()
self.lb = tkinter.Listbox(self)
self.lb.pack(side="left")
self.sb = tkinter.Scrollbar(self)
self.sb.pack(fill="y", side="left")
self.lb.insert("end", *[i*i for i in range(50)])
self.lb["yscrollcommand"] = self.sb.set
self.sb["command"] = self.lb.yview

Zunächst werden eine Listbox und eine Scrollbar erzeugt und auf der Oberfläche so angeordnet, dass die Scrollbar rechts neben der Listbox steht. Danach wird die Listbox mit den Quadraten der Zahlen zwischen 0 und 50 gefüllt. Was jetzt noch fehlt, ist die Verbindung zwischen der Scrollbar und der Listbox, denn momentan haben wir nur zwei voneinander unabhängige Widgets erzeugt.

Um die Scrollbar an die Listbox zu binden, setzen wir zunächst die Option yscrollcommand der Listbox auf die Methode set der Scrollbar. Dies erlaubt ein automatisches Anpassen der Scrollbar, wenn die Einträge der Listbox über die Pfeiltasten oder das Mausrad gescrollt werden. Danach wird die command-Option der Scrollbar auf die Methode yview der Listbox gesetzt. Nun ist auch das Scrollen der Listbox mit der Scrollbar möglich.

Die im oben genannten Beispiel erzeugte grafische Oberfläche ist in der folgenden Abbildung dargestellt:

Eine Listbox mit angebundener Scrollbar

Abbildung 39.20    Eine Listbox mit angebundener Scrollbar

Anstelle der Option yscrollcommand und der Methode yview hätten wir auch die Option xscrollcommand und die Methode xview verwenden können, um eine horizontale Scrollbar zu realisieren.

Die hier demonstrierte Möglichkeit, eine Scrollbar anzubinden, funktioniert nicht nur bei Listbox-, sondern auch bei Canvas-, Entry-, Spinbox- und Text-Widgets.

Spinbox

Bei einer Spinbox handelt es sich um ein Widget, in das der Benutzer eine ganze Zahl eintragen kann. Zusätzlich kann er die eingetragene Zahl über zwei Schaltflächen am Rand des Widgets nach oben oder unten korrigieren. Der folgende Code-Ausschnitt demonstriert die Verwendung einer Spinbox:

s = tkinter.Spinbox(master)
s["from"] = 0
s["to"] = 100
s.pack()

Die Spinbox wird instanziiert, danach werden über die Optionen from und to die Grenzen festgelegt, in denen sich die gespeicherte Zahl bewegen darf. In diesem Beispiel darf keine Zahl größer als 100 oder kleiner als 0 eingetragen werden. Die im Beispiel erzeugte Spinbox ist in der folgenden Grafik abgebildet:

Eine Spinbox im Einsatz

Abbildung 39.21    Eine Spinbox im Einsatz

Anstatt zwei Grenzen über die Optionen from und to anzugeben, können Sie die erlaubten Werte auch konkret angeben. Dies geschieht über die Option values:

s = tkinter.Spinbox(master)
s["values"] = (2,3,5,7,11,13,19)
s.pack()

In diesem Fall kann der Benutzer eine der Primzahlen zwischen 2 und 19 in der Spinbox auswählen. Die Reihenfolge, in der die Zahlen in der Spinbox erscheinen, ist durch die Reihenfolge der Werte im Tupel gegeben. Es muss kein Zusammenhang zur tatsächlichen Ordnungsrelation der Zahlen vorhanden sein.

Wenn die Werte, die die Spinbox annehmen kann, konkret angegeben werden, können dort auch andere Datentypen als int verwendet werden:

s["values"] = ("A", "B", "C")

In diesem Beispiel kann der Benutzer die Spinbox verwenden, um einen der drei Buchstaben auszuwählen.

Text

Bisher haben Sie nur eine Möglichkeit kennengelernt, mithilfe des Entry-Widgets einzeilige Texteingaben vom Benutzer zu verlangen. Das Text-Widget erlaubt es Ihnen, einen mehrzeiligen und formatierten Text anzuzeigen oder vom Benutzer eingeben zu lassen.

Das folgende Beispiel zeigt, wie Sie das Text-Widget dazu verwenden, formatierten Text anzuzeigen:

class MyApp(tkinter.Frame):
def __init__(self, master):
super().__init__(master)
self.pack()
self.text = tkinter.Text(master)
self.text.pack()
self.text.tag_config("o", foreground="orange")
self.text.tag_config("u", underline=True)
self.text.insert("end", "Hallo Welt\n")
self.text.insert("end", "Dies ist ein langer, oranger Text\n", "o")
self.text.insert("end", "Und unterstrichen ebenfalls", "u")

Zunächst wird das Text-Widget instanziiert und gepackt. Danach definieren wir sogenannte Tags, die es uns später erlauben, den darzustellenden Text zu formatieren. In diesem Fall definieren wir das Tag o für orangefarbenen und das Tag u für unterstrichenen Text.

Danach fügen wir drei Textzeilen jeweils ans Ende des im Widget enthaltenen Textes an. Die erste Textzeile soll unformatiert, die zweite orangefarben und die dritte unterstrichen angezeigt werden. Die folgende Abbildung zeigt, wie der Text dargestellt wird:

Das Text-Widget

Abbildung 39.22    Das Text-Widget

Standardmäßig ist es dem Benutzer erlaubt, den im Text-Widget dargestellten Text zu verändern. Das folgende Beispiel zeigt, wie man auf eine Eingabe des Benutzers im Text-Widget reagiert und den eingegebenen Text ausliest:

class MyApp(tkinter.Frame):
def __init__(self, master):
super().__init__(master)
self.pack()
self.text = tkinter.Text(master)
self.text.pack()
self.text.bind("<KeyRelease>", self.textChanged)
def textChanged(self, event):
print("Text:", self.text.get("1.0", "end"))

Im Gegensatz zum Entry-Widget ist es nicht möglich, eine Steuerelementvariable für den in einem Text-Widget enthaltenen Text einzurichten. Das liegt daran, dass das Text-Widget auch Formatierungsanweisungen für den darzustellenden Text benötigt. Diese müssten in einer String-Steuerelementvariablen umständlich über spezielle Code-Folgen nachgebildet werden.

Anstelle einer Steuerelementvariablen bietet das Text-Widget die Methode get, über die man den im Widget dargestellten Text auslesen kann. Es handelt sich dabei um den reinen Text, jegliche Formatierungsanweisungen gehen beim Auslesen mittels get verloren.

Im Beispielprogramm wurde ein Eventhandler für das KeyRelease-Event eingerichtet. Dieses wird immer dann ausgelöst, wenn der Benutzer eine Taste loslässt, während das Text-Widget den Eingabefokus besitzt. Beachten Sie, dass wir das KeyRelease-Event verwenden und nicht KeyPress. Würden wir das KeyPress-Event verwenden, würde unser Eventhandler aufgerufen, bevor das vom Benutzer eingegebene Zeichen ins Text-Widget eingetragen wurde.

Im Eventhandler textChanged rufen wir die Methode get des Text-Widgets auf. Diese Methode bekommt zwei Indizes übergeben, die angeben, welches Teilstück des Textes ausgelesen werden soll. In diesem Fall interessieren wir uns für den gesamten im Widget enthaltenen Text und geben die Indizes 1.0 und end an. Der Index 1.0 liest sich als »erste Zeile, nulltes Zeichen«, wobei zu beachten ist, dass die Indizierung der Zeilen bei 1 und die der Spalten, also der Zeichen, bei 0 beginnt. Der Index 1.0 bezeichnet also das erste Zeichen des im Widget dargestellten Textes. Der Index end bezeichnet selbstverständlich das letzte Zeichen des im Widget enthaltenen Textes.

Es ist möglich, eine horizontale oder vertikale Scrollbar mit einem Text-Widget zu verbinden. Dies geschieht analog zum Listbox-Widget über die Optionen xscrollcommand und yscrollcommand. Ein Beispiel dazu finden Sie im Abschnitt über die Scrollbar. In Abschnitt 39.2.7, »Weitere Module«, besprechen wir das Modul scrolledtext, das ein Text-Widget bereitstellt, das bereits mit Scrollbars ausgestattet ist.

 
Zum Seitenanfang

39.2.6    Zeichnungen – das Canvas-Widget Zur vorigen ÜberschriftZur nächsten Überschrift

Das Canvas-Widget (dt. »Leinwand«) ist ein Widget, in dem beliebige Grafiken dargestellt werden können. Sie können das Canvas-Widget beispielsweise benutzen, um ein Diagramm zu zeichnen oder um ein Bild darzustellen. Im folgenden Beispielprogramm wird das Canvas-Widget verwendet, um einen Kreis und zwei Linien zu zeichnen.

class MyApp(tkinter.Frame):
def __init__(self, master):
super().__init__(master)
self.pack()
self.cv = tkinter.Canvas(self, width=200, height=200)
self.cv.pack()
self.cv.create_oval(50, 50, 150, 150, fill="orange", width=3)
self.cv.create_line(50, 150, 150, 50, width=3)
self.cv.create_line(50, 50, 150, 150, width=3)

Zunächst wird ein quadratisches Canvas-Widget mit einer Seitenlänge von 200 Pixel erzeugt. In dieser Zeichenfläche können wir nun über die create-Methoden des Canvas-Widgets grundlegende geometrische Formen zeichnen. In diesem Fall verwenden wir die Methoden create_oval und create_line, um den Kreis bzw. die beiden Linien zu zeichnen.

Die create-Methoden bekommen jeweils zwei Koordinatenpaare als erste Parameter übergeben. Diese spezifizieren die Position, an der die Form gezeichnet werden soll. Die Koordinatenangaben beziehen sich auf das lokale Koordinatensystem im Canvas-Widget, dessen Ursprung in der oberen linken Ecke des Widgets liegt. Die positive Y-Achse dieses Koordinatensystems zeigt nach unten. Das Koordinatenpaar (50, 100) bezeichnet also den Punkt, der 50 Pixel rechts und 100 Pixel unterhalb der oberen linken Ecke des Canvas-Widgets liegt.

Das TkInter-Koordinatensystem

Abbildung 39.23    Das TkInter-Koordinatensystem

Die Methode create_oval bekommt die obere linke und die untere rechte Ecke des die Ellipse umgebenden Rechtecks übergeben. Dadurch sind Position und Form der Ellipse vollständig beschrieben. Die Methode create_line bekommt Start- und Zielpunkt der Linie übergeben.

Zusätzlich können den create-Methoden Optionen in Form von Schlüsselwortparametern übergeben werden, die das Aussehen der gezeichneten Form spezifizieren. In diesem Fall werden die Optionen fill für die Füllfarbe und width für die Stiftdicke gesetzt. Die von diesem Code erzeugte Zeichnung sieht folgendermaßen aus:

Eine Zeichnung in einem Canvas-Widget

Abbildung 39.24    Eine Zeichnung in einem Canvas-Widget

Im Folgenden möchten wir die create-Methoden, die das Canvas-Widget bereitstellt, besprechen und Ihnen gegebenenfalls ein Beispiel zeigen.

create_arc(x1, y1, x2, y2, {**kw})

Die Methode create_arc fügt ein Teilstück der durch die Koordinaten x1, y1, x2 und y2 festgelegten Ellipse zum Canvas-Widget hinzu. In Form von Schlüsselwortparametern können Optionen übergeben werden. Zwei wichtige Informationen werden durch diese Optionen festgelegt: welches Teilstück gezeichnet werden soll und welche Form es haben soll.

Die Option style darf auf einen der Strings "pieslice", "chord" oder "arc" gesetzt werden und legt fest, wie der Ellipsenausschnitt aussehen soll. Die folgende Grafik zeigt die Resultate dreier Aufrufe von create_arc, bei denen lediglich die style-Option verändert wurde.

Von links nach rechts: pieslice, chord und arc

Abbildung 39.25    Von links nach rechts: pieslice, chord und arc

Um festzulegen, welches Teilstück der Ellipse gezeichnet werden soll, sind die Optionen start und extent notwendig. Über die start-Option kann der Winkel bestimmt werden, ab dem das zu zeichnende Teilstück beginnt. Die extent-Option ist ebenfalls ein Winkel und bezeichnet die Ausdehnung des Teilstücks. Die Winkel werden in Grad gemessen und gegen den Uhrzeigersinn abgetragen. Die Optionen start und extent sind mit 0.0 bzw. 90.0 vorbelegt.

Über die Optionen fill und outline können Sie die Füll- bzw. Umrandungsfarbe der Ellipse festlegen. Farben werden als Strings im Format "#RRGGBB" angegeben, wobei die einzelnen Komponenten für die hexadezimalen Rot-, Grün bzw. Blauanteile stehen. Die Option width legt die Breite der Umrandung fest.

create_image(x, y, {**kw})

Die Methode create_image erlaubt es, ein Bild in einem Canvas-Widget darzustellen. Wie das funktioniert, wird anhand des folgenden Beispielprogramms erklärt:

class MyApp(tkinter.Frame):
def __init__(self, master):
super().__init__(master)
self.pack()
self.cv = tkinter.Canvas(self, width=200, height=200)
self.cv.pack()
self.img = tkinter.PhotoImage(file="lena.png")
self.cv.create_image(0, 0, image=self.img, anchor="nw")

Zunächst wird mithilfe der tkinter.PhotoImage-Klasse das Bild cover.png geladen. Beachten Sie, dass die PhotoImage-Klasse ausschließlich die Grafikformate GIF und PPM/PGM unterstützt.[ 189 ](Wenn Sie weitere Grafikformate benötigen, sollten Sie sich mit der Drittanbieterbibliothek Pillow auseinandersetzen, die eine zu PhotoImage kompatible Klasse bereitstellt und in Abschnitt 42.10 besprochen wird. ) Danach wird die geladene Grafik durch Aufruf der Methode create_image an der Stelle (0,0) ins Canvas-Widget gezeichnet. Über die image-Option teilen wir beim Aufruf von create_image mit, welche Grafik wir zeichnen möchten.

Die zweite angegebene Option anchor gibt an, in welcher Beziehung das übergebene Koordinatenpaar zur gezeichneten Grafik stehen soll. Der im Beispiel übergebene Wert "nw" bedeutet, dass an der Position (x,y) im lokalen Koordinatensystem des Canvas-Widgets die obere linke Ecke des Bildes liegen soll. Die anchor-Option ist mit "center" vorbelegt, hätten wir sie also nicht angegeben, läge die Mitte des Bildes im Punkt (x,y).

Die vom oben dargestellten Beispielprogramm erzeugte grafische Oberfläche sieht folgendermaßen aus:

Eine Grafik im Canvas-Widget

Abbildung 39.26    Eine Grafik im Canvas-Widget

create_line([*coords], {**kw})

Die Methode create_line zeichnet einen Linienzug ins Canvas-Widget. Dazu werden ihr eine beliebige Anzahl Koordinatenpaare als Positionsparameter übergeben. Die Optionen, über die die Eigenschaften des Linienzugs definiert werden können, werden als Schlüsselwortparameter übergeben. Im folgenden Beispiel zeichnen wir das bekannte »Haus vom Nikolaus« mithilfe eines einzigen Aufrufs der create_line-Methode:

class MyApp(tkinter.Frame):
def __init__(self, master):
super().__init__(master)
self.pack()
self.cv = tkinter.Canvas(self, width=200, height=200)
self.cv.pack()
punkte = (10, 140, 90, 60, 10, 60,
50, 10, 90, 60, 90, 140,
10, 140, 10, 60, 90, 140)
self.cv.create_line(*punkte, width=3)

Das Resultat dieses Beispielprogramms sieht folgendermaßen aus:

Das Haus vom Nikolaus

Abbildung 39.27    Das Haus vom Nikolaus

Beachten Sie, dass der mit der Methode create_line gezeichnete Linienzug nicht zwangsläufig geschlossen ist, auch wenn das im Beispiel der Fall ist. Dies ist ein Unterscheidungsmerkmal zu der sonst ähnlichen Methode create_polygon.

create_oval(x1, y1, x2, y2, {**kw})

Die Methode create_oval zeichnet eine Ellipse. Position und Form der Ellipse werden über die beiden Punkte (x1, y1) und (x2, y2) festgelegt, die den oberen linken und den unteren rechten Eckpunkt des die Ellipse umfassenden Rechtecks angeben. Die folgende Abbildung zeigt eine mittels create_oval gezeichnete Ellipse samt umfassendem Rechteck und Lage der beiden Eckpunkte:

Eine Ellipse im Canvas-Widget

Abbildung 39.28    Eine Ellipse im Canvas-Widget

Der Methode create_oval können dieselben Optionen übergeben werden wie der Methode create_line.

create_polygon([*coords], {**kw})

Die Methode create_polygon zeichnet ein Polygon in das Canvas-Widget. Das Polygon ist durch seine Eckpunkte gegeben, die in Form beliebig vieler Koordinatenpaare übergeben werden. Im folgenden Beispielprogramm verwenden wir die Methode create_polygon dazu, ein orange gefülltes Dreieck zu zeichnen:

class MyApp(tkinter.Frame):
def __init__(self, master):
super().__init__(master)
self.pack()
self.cv = tkinter.Canvas(self, width=200, height=200)
self.cv.pack()

punkte = (10, 10,
90, 50,
10, 90)
self.cv.create_polygon(*punkte, width=3,
fill="orange", outline="black")

Das resultierende Polynom sieht folgendermaßen aus:

Ein Dreieck mit create_polygon

Abbildung 39.29    Ein Dreieck mit create_polygon

Sie sehen, dass die übergebenen Punkte miteinander verbunden werden. Zusätzlich wird die Fläche durch eine Verbindung zwischen dem ersten und dem letzten Punkt geschlossen und kann gefüllt werden. Die Methode create_polygon ist auch dazu in der Lage, nichtkonvexe Polygone[ 190 ](Ein nichtkonvexes Polygon ist ein Polygon, das zwei Punkte enthält, deren Verbindungslinie nicht vollständig innerhalb des Polygons verläuft. Nichtkonvexe Polygone werden häufig als konkav bezeichnet, was streng genommen falsch ist: Ein konkaves Polygon ist ein Polygon, dessen Komplement konvex ist. ) zu zeichnen und zu füllen, wie die folgende Grafik zeigt:

Ein nichtkonvexes Polynom

Abbildung 39.30    Ein nichtkonvexes Polynom

In diesem Fall wurde die Liste der Punkte aus dem vorangegangenen Beispiel um den Punkt (90, 90) erweitert.

create_rectangle(x1, y1, x2, y2, {**kw})

Die Methode create_rectangle zeichnet ein Rechteck in das Canvas-Widget. Das Rechteck wird durch die obere linke Ecke (x1, y1) und die untere rechte Ecke (x2, y2) festgelegt.

Der einzige Unterschied zu create_ellipse besteht darin, dass nicht die von dem beschriebenen Rechteck eingefasste Ellipse, sondern das Rechteck selbst gezeichnet wird. Auch die möglichen Optionen entsprechen denen der Methode create_ellipse. Es sind daher keine weiteren Beispiele notwendig.

create_text(x, y, {**kw})

Die Methode create_text ermöglicht es, beliebigen Text in ein Canvas-Widget zu schreiben. Standardmäßig wird der Text zentriert an die durch x und y bestimmte Position geschrieben. Dieses Verhalten lässt sich über die Option anchor verändern.

Dies wird anhand des folgenden Beispielprogramms demonstriert:

class MyApp(tkinter.Frame):
def __init__(self, master):
super().__init__(master)
self.pack()
self.cv = tkinter.Canvas(self, width=200, height=200)
self.cv.pack()
self.font1 = ("Arial", 12, "italic")
self.font2 = ("Courier New", 12, "bold italic")
self.font3 = ("Comic Sans MS", 12, "bold")
self.cv.create_text(55, 30, text="Python ", font=self.font1)
self.cv.create_text(55, 50, text="Python ", font=self.font2)
self.cv.create_text(55, 70, text="Python ", font=self.font3)

Nach dem Erzeugen des Canvas-Widgets werden drei Tupel angelegt, die jeweils einen Schrifttyp definieren. Näheres dazu, wie Sie die auf dem aktuellen System verfügbaren Schriftarten herausfinden können, erfahren Sie in Abschnitt 39.2.7, »Weitere Module«, unter »tkinter.font«. An dieser Stelle möchten wir uns damit begnügen, dass das erste Element des Tupels die Schriftart enthält, das zweite die Schriftgröße und im dritten Element weitere Angaben wie Fettdruck (bold) oder Kursivschrift (italic) stehen können.

Nachdem die Schriftarten spezifiziert wurden, rufen wir die Methode create_text dreimal, jeweils mit einer anderen Schriftart, auf. Der zu schreibende Text wird über die Option text und der Schrifttyp über die Option font angegeben. Das Beispielprogramm erzeugt folgende grafische Oberfläche:

Text in einem Canvas-Widget

Abbildung 39.31    Text in einem Canvas-Widget

create_window(x, y, {**kw})

Die Methode create_window ermöglicht es, Steuerelemente innerhalb eines Canvas-Widgets zu platzieren. Das Steuerelement wird an die durch x und y festgelegte Position gezeichnet, wobei ein eventuell für die Option anchor übergebener Wert berücksichtigt wird. Das Steuerelement selbst muss vorher instanziiert werden und wird über die Option window angegeben.

Das folgende Beispielprogramm kombiniert eine kleine Zeichnung und ein Steuerelement zu einer bedrohlichen grafischen Oberfläche:

class MyApp(tkinter.Frame):
def __init__(self, master):
super().__init__(master)
self.pack()
self.cv = tkinter.Canvas(self, width=200, height=200)
self.cv.pack()
self.cv.create_oval(10, 10, 190, 90, fill="red", width=3)
self.b = tkinter.Button(None, text="Selbstzerstörung",
background="red", activebackground="red",
foreground="white", activeforeground="white")
self.cv.create_window(100, 50, window=self.b)

Der Quellcode sollte selbstverständlich sein und erzeugt die folgende GUI:

Ein in einem Canvas-Widget eingebetteter Button

Abbildung 39.32    Ein in einem Canvas-Widget eingebetteter Button

Nun ja, Sie müssen den Button ja nicht anklicken ...

Zum Abschluss möchten wir noch einige Bemerkungen dazu machen, wie Sie eine einmal angefertigte Zeichnung wieder modifizieren können. Dazu ist zu sagen, dass jede create-Methode einen ganzzahligen Index zurückliefert, über den Sie nachträglich auf das erstellte Element zugreifen können.

 
Zum Seitenanfang

39.2.7    Weitere Module Zur vorigen ÜberschriftZur nächsten Überschrift

Zum Schluss möchten wir Ihnen noch einen Überblick über weitere, im tkinter-Paket enthaltenen Module geben, da diese einige durchaus interessante Bereiche abdecken und in mancher Hinsicht die Arbeit mit dem Tk-Framework erleichtern. Im Folgenden werden die Module scrolledtext, filedialog, font und messagebox besprochen.

tkinter.scrolledtext

Im Abschnitt über das Text-Widget haben wir gesagt, dass es über die Option yscrollcommand möglich ist, ein Text-Widget mit einer vertikalen Scrollbar auszustatten. Da aber eine solche vertikale Scrollbar bei einem Text-Widget in der Regel erwünscht ist, wäre es umständlich, jedes Mal den Code zum Instanziieren und Anbinden der Scrollbar schreiben zu müssen.

Für diesen Zweck existiert das Modul scrolledtext im Paket tkinter, das das Widget ScrolledText bereitstellt. Dieses Widget verhält sich wie ein Text-Widget, ist aber standardmäßig mit einer vertikalen Scrollbar ausgestattet, sodass sich der Programmierer um diese nicht mehr zu kümmern braucht.

Intern besteht das ScrolledText-Widget aus einem Text-Widget, einer Scrollbar und einem Frame-Widget, das die beiden anderen Widgets umfasst. Über die Attribute vbar und frame des ScrolledText-Widgets können Sie auf die Scrollbar und das Frame-Widget zugreifen.

Beachten Sie, dass das ScrolledText-Widget standardmäßig keine horizontale Scrollbar mitbringt. Diese müssen Sie genau wie beim Text-Widget selbst erzeugen und anbinden.

Das ScrolledText-Widget

Abbildung 39.33    Das ScrolledText-Widget

tkinter.filedialog

Bei der Programmierung grafischer Benutzeroberflächen gibt es Standarddialoge, die für bestimmte Fragen an den Benutzer gemacht sind, die immer wieder gestellt werden. Solche Standarddialoge haben für den Programmierer den Vorteil, dass er keinen eigenen kreieren muss. Für den Benutzer liegt der Vorteil darin, dass er sich nicht ständig mit verschiedenen grafischen Oberflächen für dieselbe Fragestellung konfrontiert sieht, sondern immer denselben vertrauten Dialog vorfindet. Auch im Tk-Framework ist es möglich, die Standarddialoge des Betriebssystems bzw. der Desktop-Umgebung zu nutzen.

Eine wichtige Klasse von Standarddialogen sind Dateidialoge, die den Benutzer dazu auffordern, Dateien oder Ordner von der Festplatte auszuwählen. Sei es, um sie in das Programm hineinzuladen oder Inhalte dorthin zu speichern. Dateidialoge werden ständig benötigt.

Das Modul filedialog des Pakets tkinter stellt vorgefertigte Dateidialoge bereit. In der Regel genügt ein Funktionsaufruf, um den Dialog in die eigene Anwendung zu integrieren. Im Folgenden besprechen wir die vom Modul filedialog bereitgestellten Funktionen.

Funktion Standarddialog für
askdirectory({**options}) die Auswahl eines Verzeichnisses
askopenfilename({**options}) die Auswahl einer Datei
asksaveasfilename({**options}) die Auswahl eines Speicherorts für eine Datei

Tabelle 39.9    Standarddialoge im Modul tkinter.filedialog

tkinter.font

In Abschnitt 39.2.6 haben Sie in den Ausführungen über das Canvas-Widget erfahren, wie Sie einen beliebigen Text in das Canvas-Widget zeichnen können. Dabei konnten Sie über eine Option eine Schriftart angeben, die zum Schreiben des Textes verwendet werden sollte. Zu diesem Zeitpunkt haben wir uns einiger Standardschriftarten bedient, die auf jedem System verfügbar sind. Es fehlte eine Möglichkeit herauszufinden, auf welche Schriftarten wir tatsächlich zurückgreifen können.

Das Modul font des Pakets tkinter bietet in Form der Funktion families eine Lösung für dieses Problem. Diese Funktion gibt ein Tupel zurück, das die Namen aller Schriftarten enthält, die Sie beispielsweise im Zusammenhang mit der font-Option bei einem Aufruf der Methode create_text des Canvas-Widgets verwenden können. Der Funktion families brauchen keine Parameter übergeben zu werden.

Abgesehen von dieser Funktion enthält das font-Modul noch eine Klasse Font, deren Beschreibung außerhalb des Fokus dieser Einführung liegt. Nähere Informationen dazu finden Sie beispielsweise in der interaktiven Hilfe zu tkinter.font.

tkinter.messagebox

Das Modul messagebox des Pakets tkinter ermöglicht es, durch einen einfachen Funktionsaufruf sogenannte Message Boxes anzuzeigen. Eine Message Box ist ein kleines Fenster mit einer Botschaft an den Benutzer. Sie kann dazu verwendet werden, den Benutzer über einen Fehler zu informieren oder ihm eine Frage zu stellen. Es gibt mehrere Typen von Message Boxes, beispielsweise einen, der zusätzlich zur Botschaft ein entsprechendes Icon für eine Fehlermeldung anzeigt, oder einen, der die beiden Buttons Ja und Nein anbietet, über die der Benutzer eine in der Botschaft gestellte Frage beantworten kann.

Das Modul messagebox stellt eine Reihe von Funktionen bereit, über die verschiedene Arten von Message Boxes erzeugt und angezeigt werden können. Diese Funktionen verfügen alle über dieselbe Schnittstelle und können wie im folgenden Beispiel verwendet werden:

import tkinter.messagebox
tkinter.messagebox.askokcancel("Beispielprogramm",
"Die Installation von 'virus.exe' wird jetzt gestartet.")

Die Message Box kann über die Buttons OK und Abbrechen geschlossen werden. Welchen der Buttons der Benutzer angeklickt hat, erfahren Sie über den Rückgabewert der Funktion. Die Funktion gibt True zurück, wenn der Benutzer die Message Box über den Button OK geschlossen hat, und False bei Abbrechen.

Eine mit askokcancel erzeugte Message Box

Abbildung 39.34    Eine mit askokcancel erzeugte Message Box

Abgesehen von askokcancel sind die in Tabelle 39.10 aufgelisteten Funktionen vorhanden:

Funktion Schaltflächen Rückgabewert
askokcancel OK
Abbrechen
True
False
askquestion Ja
Nein
"yes"
"no"
askretrycancel Wiederholen
Abbrechen
True
False
askyesno Ja
Nein
True
False
askyesnocancel Ja
Nein
Abbrechen
True
False
None
showerror OK "ok"
showinfo OK "ok"
showwarning OK "ok"

Tabelle 39.10    Standarddialoge im Modul tkinter.messagebox

 


Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Python 3 Python 3
Jetzt Buch bestellen

 Buchempfehlungen
Zum Rheinwerk-Shop: Einstieg in Python
Einstieg in Python


Zum Rheinwerk-Shop: Python. Der Grundkurs
Python. Der Grundkurs


Zum Rheinwerk-Shop: Algorithmen mit Python
Algorithmen mit Python


Zum Rheinwerk-Shop: Objektorientierte Programmierung
Objektorientierte Programmierung


Zum Rheinwerk-Shop: Raspberry Pi. Das umfassende Handbuch
Raspberry Pi. Das umfassende Handbuch


Zum Rheinwerk-Shop: Roboter-Autos mit dem Raspberry Pi
Roboter-Autos mit dem Raspberry Pi


Zum Rheinwerk-Shop: Neuronale Netze programmieren mit Python
Neuronale Netze programmieren mit Python


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo

 
 


Copyright © Rheinwerk Verlag GmbH 2020
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 
[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern