Hier sind Sie richtig, wenn Sie noch nie programmiert haben. Erst einmal haben Sie die richtige Sprache erwischt, aus meiner Sicht natürlich das richtige Buch und vor allem nun auch noch den richtigen Abschnitt.
Sollten Sie allerdings schon Programmiererfahrung haben, werden Sie sich hier vielleicht langweilen. Und bevor Sie noch einschlafen und mir den Abschnitt verschnarchen, blättern Sie lieber zum nächsten Abschnitt weiter.
Programmieren ist eine schöne Beschäftigung. Sie können herumkommandieren, ohne dass es Ihnen irgendjemand übel nimmt. Anstatt selbst zu arbeiten, überlassen Sie dies dem Computer. Sie tun Dinge, die andere Leute nicht können. Und Sie erschaffen kreativ eine virtuelle Welt.
Ein Programm besteht aus Anweisungen, die Sie dem Computer vorlegen. Und er wird sie geduldig genau so ausführen, wie Sie sie formuliert haben, also auch mit den Fehlern, die Sie einbauen. Also werden Sie irgendwann nach diesen Fehlern suchen müssen. Der frustrierende Teil der Arbeit ist, dass Sie immer auf Ihre eigenen Fehler stoßen werden.
Da ein Computer von Haus aus nicht fantasiebegabt ist, sind die Anweisungen sehr einfach und klar. Man merkt an dieser Stelle, dass der Computer ursprünglich geschaffen wurde, um mathematische Probleme zu lösen. Weiß der Computer, wie er ein Problem lösen kann, dann kann er das immer wieder tun. Und er tut es eben sehr schnell. Sie werden noch sehen, dass man mit einem Computer neben der Mathematik noch anderen Unsinn machen kann.
Nehmen wir an, Sie wollen für die nächste Demo Tomaten kaufen. Sie wissen, dass fünf Tomaten 1,35 Euro kosten. Sie wollen aber 17 Tomaten, weil Ihr Patronengurt genau so viele aufnimmt. Sie erkennen sofort: Das ist der klassische Dreisatz. Sie zücken vielleicht schon Ihren Taschenrechner. Aber halt! Anstatt den Dreisatz jedes Mal selbst durchzurechnen, schreiben Sie ein Programm. Das bedeutet, Sie erklären dem Computer einmal, wie er mit dem Dreisatz umzugehen hat, und danach kann er das alleine. Leider müssen Sie dazu einmal darüber nachdenken, wie so ein Dreisatz funktioniert. Er besteht aus folgenden Schritten. Es sind drei. Logisch. Sonst hieße das Verfahren auch nicht Dreisatz.
Dies ist ein Programm. Allerdings ist die Programmiersprache für den Computer schwer nachvollziehbar. Der hätte es nämlich gern ein wenig formaler. Nehmen wir also irgendeine Programmiersprache. Wie wäre es mit Python?
PreisTomatenPaket = 1.35
Anzahl = 5
GewuenschteAnzahl = 17
PreisEinerTomate = PreisTomatenPaket / Anzahl
Ergebnis = PreisEinerTomate * GewuenschteAnzahl
print(Ergebnis)
Wenn der Computer das Programm durchläuft, ermittelt er als Ergebnis 4,59. Wollen Sie eine andere Anzahl an Tomaten, ändern Sie im Programm den entsprechenden Wert und starten das Programm noch einmal. Ändert sich der Preis, ändern Sie ihn.
Natürlich gibt es auch eine Anweisung, mit der der Computer den Benutzer auffordert, Zahlen einzugeben. So müssen Sie nicht für jede Wertänderung das Programm anpassen. Aber lassen Sie mir dieses süße Geheimnis für später.
Eine Beschreibung zur Lösung eines Problems nennt man Algorithmus. Ein gut gegliederter Algorithmus kann leicht in ein Programm überführt werden.
Das Programm könnte aber noch mehr. Wenn Sie ab 10 Tomaten Rabatt von 10 Prozent bekommen, würde dies der Computer auch berechnen können. Dazu schieben Sie an geeigneter Stelle den folgenden Algorithmus ein.
• ... ziehe vom Einzelpreis 10 Prozent ab.
10 Prozent abzuziehen ist dasselbe wie 90 Prozent vom Preis. 90 Prozent von irgendetwas errechnet man einfach, indem man irgendetwas mit 0,9 multipliziert. Das vereinfacht den Programmiercode etwas. Die Abfrage wird dann an passender Stelle in unser Listing 1.1 eingebaut.
PreisTomatenPaket = 1.35
Anzahl = 5
GewuenschteAnzahl = 17
PreisEinerTomate = PreisTomatenPaket / Anzahl
if GewuenschteAnzahl >= 10:
PreisEinerTomate = PreisEinerTomate * 0.90
Ergebnis = PreisEinerTomate * GewuenschteAnzahl
print(Ergebnis)
Sie könnten jetzt noch weitere Sonderfälle in das Programm einbauen. So könnte es sein, dass ein Beutel mit 7 Tomaten mit 1,40 billiger ist als die einzelnen Tomaten. Also soll der Computer doch bitte errechnen, wie viele Beutel ich kaufen muss, wie viele einzelne Tomaten und was das alles zusammen kostet.
Ihre Freundin erzählt Ihnen, dass der Nachbar seit einem Jahr mit dem Rauchen aufgehört hat und dadurch 1.000 Euro gespart hat, die er nun auf der Bank in einem Sparbrief angelegt hat. Sie rauchen zwar nicht. Diese Einnahmequelle fällt flach. Aber Sie beschließen, das Tomatenwerfen einzustellen, und wollen nun auch 1.000 Euro anlegen.
Bei Sparbriefen werden die Zinsen immer wieder am Ende des Jahres hinzugefügt und das führt uns zu einem weiteren Element der Programmierung, nämlich der Wiederholung. Der Programmierer spricht auch von Schleifen. Sie bekommen für einen Sparbrief jedes Jahr 6 Prozent Zinsen, die auf die Einlagen aufgeschlagen werden, also in den Folgejahren mitverzinst werden. Der Vertrag läuft über 7 Jahre. Wenn Sie das dem Computer vorlegen, können Sie ihm einfach sagen: Wiederhole diese Berechnung bitte 7 Mal.
Damit das Beispiel nicht zu trivial wird, nehmen wir noch an, dass Sie ab dem 3. Jahr sogar 9 Prozent Zinsen bekommen. Ich kann bei den Zinsen großzügig sein. Ich muss sie Ihnen ja nicht auszahlen.
Sie sehen, dass die Abfrage in die Schleife eingebaut wurde. Diese Art der Kombination der Programmierelemente ist zunächst vielleicht verwirrend, aber bei längerem Nachdenken logisch. Diese Art, Aufgaben zu formalisieren, damit der Computer es versteht, ist die Aufgabe eines Programmierers.
Der Vollständigkeit halber zeige ich Ihnen, wie das Programm aussieht, das aus dem Algorithmus hervorgeht. Vielleicht werden Sie die Befehle nicht verstehen. Keine Sorge, die werden in den nächsten Kapiteln im Detail erläutert. Wichtig ist nur, dass Sie sehen, wie sich aus dem Algorithmus das fertige Programm entwickelt.
Einlage = 1000
Jahr = 1
while Jahr <= 7:
if Jahr < 3:
Einlage = Einlage + Einlage * 0.06
if Jahr >= 3:
Einlage = Einlage + Einlage * 0.09
Jahr = Jahr + 1
print(Einlage)
Tatsächlich kann der Computer gar nicht viel mehr als Abläufe, Abfragen und Schleifen. Programmiersprachen kommen nur deshalb nicht mit fünf Befehlen aus, weil es noch einige Mechanismen gibt, die dem Programmierer helfen, Programmteile zu strukturieren, um sie mehrfach verwenden zu können.
Wenn Computer und Menschen über Zahlen reden, meinen sie nicht ganz dasselbe. Der Mensch verwendet das Dezimalsystem, vermutlich, weil er zehn Finger hat. Computer dagegen kennen nur zwei Zustände. Entweder es ist Strom auf der Leitung oder nicht. Aber genauso wie die meisten Menschen mit größeren Zahlen als 10 umgehen können, kann auch der Computer nicht nur bis 2 zählen. Durch das Verwenden mehrerer Stellen können beinahe beliebige Zahlen dargestellt werden, ob im Zehner- oder im Binärsystem.
In den meisten Fällen kann es Ihnen egal sein, wie Ihr Computer seine Zahlen intern darstellt. Ich will Sie damit auch gar nicht länger belästigen. Aber Sie werden feststellen, dass die 2 und ihre Potenzen magische Zahlen für Computer sind. Acht Stellen sind ein Byte. Und da 28 256 ist, können darin 256 Zustände abgespeichert werden. Ein Kilobyte sind 1024 Byte, weil 1024 210 sind. Zur leichteren Unterscheidung vom Kilo Zucker wird das Kilo für Bytes mit einem großen K abgekürzt.
Die unterschiedlichen Codierungen haben für die Nachkommazahlen fatale Folgen. Sie kennen vielleicht das Ergebnis, wenn Sie in Ihrem Taschenrechner 1 durch 3 teilen. Das erwartete Drittel erscheint als eine endlose Kolonne von Dreien hinter der 0 vor dem Komma. Multiplizieren Sie diese Zahl dann mit 3, werden die meisten Taschenrechner spontan eine Reihe von Neunen auf dem Display haben. Wenn das ein Außerirdischer sähe, der mit drei Fingern geboren ist, würde er sehr verblüfft sein. Denn vermutlich hätte er ein Zahlensystem, das auf der 3 basiert, und kann sich nicht vorstellen, dass ein Drittel mal 3 irgendetwas anderes als 1 ergibt. Und ganz ähnlich geht es dem Computer mit dem menschlichen Zehntel, das binär codiert ebenfalls ein endloser Bruch ist.
Der Computer speichert die Buchstaben natürlich auch in Bytes, die wie erwähnt 256 Zustände kennen. Die englischen Buchstaben umfassen 26 Zeichen. Hinzu kommen die Großbuchstaben und die zehn Ziffern. Das sind also 62 verschiedene Zeichen. Wenn noch ein paar Satzzeichen wie Punkt, Komma und Fragezeichen hinzukommen, sind es schnell mehr als 64 Zeichen, so dass 6 Bits für die Aufnahme aller Zeichen nicht ausreichen. Darum wurden zunächst 7 Bits verwendet, die einen Zeichenvorrat von 128 ermöglichen. Das letzte Bit wurde erst einmal auf 0 belassen. Diese Codierung wurde als ASCII-Zeichensatz (American Standard Code for Information Interchange) genormt.
Wenn Sie dieses Buch lesen, werden Sie vermutlich die deutsche Sprache verwenden und ahnen, dass die Amerikaner beim ASCII nicht viel Aufwand um die Umlaute getrieben haben. Darum wird das Thema wiederkommen.
>Entgegen anderslautenden Gerüchten verstehen Computer uns Menschen gar nicht. Das Herz des Computers, oder besser das Gehirn des Computers, ist der Prozessor, der auch gern CPU (Central Processing Unit) genannt wird. Dieser Prozessor kennt eine übersichtliche Zahl von Befehlen, die einfach durchnummeriert sind. Hinter jedem Befehl steht, mit welcher Adresse oder Wert gearbeitet werden soll, und auch das ist nichts als eine Zahl. Solch einen Zahlensalat kann sich aber kein Mensch merken.
Um dieses gegenseitige Unverständnis zu überbrücken, sind Programmiersprachen entstanden, die etwas dichter an die Vorstellung des Menschen gerückt sind. Ihre Befehle bestehen aus englischen Wörtern. Damit sie auch der Computer versteht, müssen sie in seine Zahlenbefehle übersetzt werden. Dazu gibt es zwei Techniken:
Ja, die Computerei ist voller Anglizismen. Die Befehle sind englisch, die Bezeichnungen sind englisch, selbst das Wort Computer ist englisch. Wenn Ihr Englisch nicht so toll ist, muss Sie dies nicht beunruhigen. Sie müssen genauso wenig Englisch lernen wie Ihr Apotheker Latein. Denn obwohl die meisten Präparate in der Apotheke lateinische Namen tragen, wird sich Ihr Apotheker schwertun, Julius Caesars Schriften über den gallischen Krieg im Original zu lesen. Und wozu auch? Seit Gründung der Europäischen Gemeinschaft hat es keine kriegerischen Auseinandersetzungen zwischen Franzosen und Italienern mehr gegeben, wenn wir mal von den Stadienkämpfen während der verschiedenen Fußballturniere absehen.
Viele Computerbegriffe sind längst übersetzt, sodass man sich damit nicht wehtut. Ein paar angelsächsische Besonderheiten sollten Sie aber im Hinterkopf behalten.
Bisher waren die Programme auf die Textkonsole beschränkt. Aber es ist doch viel netter, wenn das Programm ein Fenster öffnet. Leider ist die Programmierung der GUI (Graphical User Interface) nicht trivial. Und die größte Gemeinheit ist, dass jedes System eine eigene Art hat, seine GUI zu programmieren. So unterscheidet sich ein grafisches Programm für den Mac auch in der Programmierung sehr deutlich von dem für Windows.
Es ist etwas frustrierend, wenn man die Programmierumgebung für jede grafische Oberfläche neu lernen muss, und darum gibt es einige Bibliotheken, die die Eigenheiten der verschiedenen Oberflächen unter einer gemeinsamen Schale verbergen. Python unterstützt mehrere dieser Bibliotheken. Wenn Ihnen einer der folgenden Kandidaten schon von einer anderen Programmiersprache bekannt ist, können Sie diese Kenntnisse unter Python wiederverwenden.
Tkinter ist wohl die populärste Bibliothek für Python. Darum wird sie bei einem Download eines Python-Pakets von www.python.org
bereits mitgeliefert. Da sie auch am schnellsten zu schicken Programmen führt, wird dieses Kapitel sich damit befassen. Unter Linux ist zwar Python bereits vorinstalliert. Da Python dort aber auf mehrere kleine Pakete aufgeteilt ist, kann es sein, dass auf Ihrem System Tkinter gar nicht vorhanden ist. Dann müssen Sie das entsprechende Paket python-tk beziehungsweise python3-tk nachinstallieren.
Um auf Tkinter zugreifen zu können, müssen Sie die Bibliothek erst importieren. Als kleine Gemeinheit erweist sich, dass die Bibliothek unter Python 2 noch als Tkinter einzubinden war, also mit einem großen T. Python 3 wollte die Namen der Bibliotheken vereinheitlichen und legte fest, dass alle Bibliotheksnamen aus Kleinbuchstaben bestehen. So wurde das T klein.
Wenn Sie nun ein Programm schreiben wollen, das sowohl mit dem Interpreter von Python 2 als auch mit Python 3 kompatibel ist, können Sie sich mit einem schmutzigen Trick helfen. Sie laden einfach Tkinter mit einem großen T und geben ihm den Alias von tkinter. Klappt das, handelt es sich um Python 2, das im weiteren Verlauf des Skripts nun aber mit dem Modulnamen tkinter hausieren geht.
Wird das Skript von Python 3 interpretiert, erhalten Sie eine Exception. Diese fangen Sie mit dem Befehl try
ab. Nun können Sie im except
-Fall einfach tkinter importieren.
try:
import Tkinter as tkinter
except:
import tkinter
Auf diese Art heißt das Modul nach dem Import auf alle Fälle tkinter mit kleinem T. Sollte Tkinter gar nicht zur Verfügung stehen, wird das Programm beim ersten Versuch eines Zugriffs auf die Bretter gehen.
Zuerst soll ein einfaches kleines Fenster enstehen. Das Programm soll eine Beschriftung haben und einen Button.
Bei den meisten grafischen Programmen benötigt das Programm dazu einigen Vorlauf. Wie Sie es unter Python gewohnt sind, ist das Programm erfrischend kurz.
import tkinter
fenster = tkinter.Tk()
fenster.title("Mein Programm")
anzeige = tkinter.Label(fenster, text="Guten Tag")
knopf = tkinter.Button(fenster, text="Ende")
anzeige.pack()
knopf.pack()
fenster.mainloop()
Auf Anhieb erschließen sich diese Zeilen sicher nicht. Aber die folgenden Bemerkungen sollten Ihnen ein Gefühl vermitteln, wie tkinter-Programme ticken.
Tk()
erzeugt.pack()
werden diese im Eltern-Fenster angeordnet (siehe Abschnitt 10.3) und sichtbar.mainloop()
startet den Ablauf des Fensters.Das Programm ist nicht gerade spektakulär. Eine freundliche Meldung und ein Button, allerdings ohne Wirkung. Wenn Sie das Programm beenden wollen, müssen Sie die Schließbox einsetzen. Schade, eigentlich.
Grafische Programme tun so lange gar nichts, bis von außen ein Ereignis auf sie einschlägt, wie ein Tastendruck oder ein Mausklick. Dazu muss sich das Programm für den Empfang solcher Ereignisse mit einer Funktion anmelden, die diese Ereignisse dann behandelt.
Leider passiert nichts, wenn Sie auf den Knopf drücken. Um das zu ändern, muss das Programm eine Information bekommen, wenn der Anwender auf den Knopf drückt.
Die Funktion ende()
erweitert das Programm. Diese Funktion soll der Button aufrufen. Sie beendet das Programm durch einen Aufruf von sys.exit(0)
(siehe Abschnitt 19.5). Dass die Funktion ende()
beim Drücken des Buttons aufgerufen werden soll, erfährt der Button über seinen Konstruktor-Parameter command. Nun sollte das Programm enden, sobald der Button gedrückt wird.
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()
Falls Ihr Programm mehrere Buttons verwendet, die durch eine einzige Funktion bedient werden sollen, wird es für diese etwas schwierig, den Absender zu erkennen. Hier hilft ein Aufruf über lambda
(siehe Abschnitt 5.5). Dazu versehen Sie die Rückruffunktion des Buttons mit einem Parameter. Anstatt nun die Funktion direkt anzugeben, verwenden Sie den Befehl lambda
für die Definition eines anonymen Funktionsrumpfes, der nichts anderes tut, als die Rückruffunktion mit einem eindeutigen Parameter aufzurufen.
import tkinter
def klick(nummer):
print(nummer)
fenster = tkinter.Tk()
eins = tkinter.Button(fenster, text=’eins’,
command= lambda: klick(1))
eins.pack()
zwei = tkinter.Button(fenster, text=’zwei’,
command= lambda: klick(2))
zwei.pack()
drei = tkinter.Button(fenster, text=’drei’,
command= lambda: klick(3))
drei.pack()
vier = tkinter.Button(fenster, text=’vier’,
command= lambda: klick(4))
vier.pack()
fenster.mainloop()
Die Nummer wird im Beispielprogramm einfach per print()
auf dem Bildschirm ausgegeben. Aber wo befindet sich dieser bei einem grafischen Programm? Tatsächlich wird die Ausgabe von print()
auf keinen Fall im Fenster erscheinen. Aber wenn Sie das Programm von einer Konsole aus gestartet haben, dann sehen Sie die Ausgabe dort. Wenn Sie es von IDLE aus starten, finden Sie die Ausgabe dort. Eclipse hat für diese Zwecke extra ein Konsolenfenster. Wurde das Programm von einem Desktop gestartet, verschwinden die Ausgaben von print()
.
print()
können bei grafischen Programmen für die Kontrolle des Programms während der Erstellung verwendet werden. Die übergebene Nummer kann von der Funktion klick()
für die Identifizierung des Ausgangsbuttons verwendet werden. In Abbildung 10.2 sehen Sie die Buttons harmonisch in einer Reihe stehen. An den Schließknopf kommen Sie mangels Breite nicht heran. Dazu müssen Sie das Fenster etwas breiter ziehen.
Eine grafische Oberfläche ist ereignisreich. Ein typisches Programm baut einmal sein Fenster auf, meldet sich für einige Events an und freut sich, wenn ein solches stattfindet. Neben den oben beschriebenen Ereignissen, die durch die Kontrollelemente ausgelöst werden, können Sie darüber hinaus auch direkt die Ereignisse für Maus, Tastatur oder Fensterveränderungen fangen und verarbeiten.
Das direkte Abfangen vor allem von Mausereignissen per bind()
kommt beim Canvas (siehe Kapitel 11) sehr viel häufiger vor als bei einem Fenster. Darum finden Sie die Beschreibung in Abschnitt 11.2.8. Das Canvas ist eine freie Zeichenfläche, das die Ereignisse komplett selbst fangen muss. Bei einem normalen Fenster übernehmen zumeist die Kontrollelemente die Ereignisse.
Die Tastaturereignisse werden allerdings nicht von einem Canvas, sondern nur von einem Fenster gefangen. Das Ereignis wird mit der Methode bind()
an das Fenster gebunden. Der erste Parameter ist eine Zeichenkette, die den Ereignistyp bezeichnet. Die normalen Tasten werden über das Ereignis ’<Key>’
gebunden. Der zweite Parameter enthält den Namen der Funktion, die das Ereignis bearbeitet. Diese hat immer einen Parameter, der das Event beschreibt. Die gedrückte Taste befindet sich im Attribut char der Event-Variablen.
def fangtaste(event):
print("char:", event.char)
...
fenster.bind(’<Key>’, fangtaste)
Wenn nur einzelne, spezielle Tasten interessant sind, können Sie auch einfach den Buchstaben als Ereignis anmelden. Das Programm in Listing 10.5 fängt nur den kleinen Buchstaben a.
fenster.bind(’a’, fangtaste)
Für die Pfeiltasten (Cursortasten) gibt es die Ereignisse ’<Left>’
, ’<Up>’
, ’<Right>’
und ’<Down>’
. Sie können diese sogar mit den Wörtern Shift
, Alt
und Control
beispielsweise zu
’<Control-Left>’
kombinieren. Darüber hinaus sind noch die Sondertasten aus Tabelle 10.1definiert.
Bezeichnung | Taste |
’<Return>’ |
Die Return- oder Enter-Taste |
’<BackSpace>’ |
Die Backspace-Taste (Korrektur) |
’<Tab>’ |
Die Tabulatortaste |
’<Shift_L>’ |
Die linke Großstelltaste |
’<Shift_R>’ |
Die rechte Großstelltaste |
’<Control_L>’ |
Die linke Strg-Taste |
’<Control_R>’ |
Die rechte Strg-Taste |
’<Alt_L>’ |
Die linke Alt-Taste |
’<Alt_R>’ |
Die rechte Alt-Taste |
’<Pause>’ |
Die Pause-Taste |
’<Caps_Lock>’ |
Dauerhafte Großstelltaste |
’<Escape>’ |
Die ESC-Taste |
’<Prior>’ |
Seite nach oben (Page Up) |
’<Next>’ |
Seite nach unten (Page Down) |
’<End>’ |
Die Ende-Taste |
’<Home>’ |
Die Pos1-Taste (Home) |
’<Print>’ |
Die Druck-Taste |
’<Insert>’ |
Die Einf-Taste |
’<Delete>’ |
Die Entf-Taste |
’<F1>’ bis ’<F12>’ |
Die Funktionstasten |
’<Num_Lock>’ |
Die Nummernblock-Taste |
Sie können sich auch für das Ereignis anmelden, dass das Fenster den Fokus erhält oder verliert. Wenn mehrere Fenster aktiv sind, empfängt nur eines davon die Tastaturereignisse. In den meisten Fällen ist es dasjenige Fenster, das oben liegt. Kommt ein anderes Fenster nach oben, verliert es den Fokus (’<FocusOut>’
), kommt es wieder nach oben, erhält es den Fokus wieder (’<FocusIn>’
).
Vor der Erforschung weiterer Kontrollelemente möchte ich Sie noch einen Moment mit dem Thema Layout aufhalten. Es ist zwar schön, wenn Sie mit Buttons, Listen und anderen Kontrollelementen umgehen können, aber sehr frustrierend, wenn die alle übereinandergestapelt in einem immer länger werdenden Fenster angeordnet sind. Das sieht doof aus und wirkt nicht sehr professionell.
Das Layout ist die Kunst, Kontrollelemente in den Fenstern so anzuordnen, dass der Benutzer sich darin zurechtfindet. Das Layout ist ein dynamischer Vorgang, der vor allem dann sichtbar wird, wenn ein Fenster in der Größe verändert wird. Die Kontrollelemente melden bei ihren Eltern-Frames Platz an, die diese gegebenenfalls wieder an deren Eltern weiterreichen. Die lachen dann herzlich darüber, verteilen den zur Verfügung stehenden Platz an die Kinder und die versuchen, unter den gegebenen Platzverhältnissen eine hübsche Darstellung zu erreichen.
Zwischen Eltern und Kindern steht jeweils ein Layout-Manager, der vorgibt, wie der verfügbare Platz aufgeteilt werden soll. Für verschiedene Situationen gibt es aber unterschiedliche Strategien. Darum müssen Sie das Layout auswählen, das für die Darstellung der Elemente am besten geeignet ist.
Um verschiedene Layouts kombinieren zu können, stellt Ihnen Tkinter Frames zur Verfügung. Das sind Rahmen, in die Sie Kontrollelemente, aber auch weitere Rahmen einsetzen können. Keine Angst: Der Rahmen ist unsichtbar. So kann niemand erkennen, wie Sie tricksen.
Da sich innerhalb eines Layouts Kontrollelemente und Frames gleich verhalten, werden sie auch als Widgets bezeichnet. Ein Widget ist ganz allgemein ein Fensterelement.
Im ersten Beispiel haben wir die Elemente mit der Methode pack()
einfach in das Hauptfenster hineingepackt. Die Methode pack()
ordnet die hinzukommenden Elemente standardmäßig einfach untereinander an.
Das erfordert einen hohen Monitor. Dumm nur, dass heutige Monitore eher immer breiter als immer länger werden. Aber Sie können über die Option side auch eine horizontale Anordnung erreichen, indem Sie ihr den Wert LEFT zuweisen.
meinLabel.pack(side=tkinter.LEFT)
Neben LEFT sind auch TOP, BOTTOM und RIGHT zulässig. Die Packerei geschieht nacheinander. Wenn Sie also einmal von dem Standard TOP auf LEFT wechseln, wird das obere Element nicht mehr angefasst. Das hört sich kompliziert an. Einfacher geht es mit einem Beispiel.
import tkinter
fenster = tkinter.Tk()
eins = tkinter.Label(fenster, text=’eins’)
zwei = tkinter.Label(fenster, text=’zwei’)
drei = tkinter.Label(fenster, text=’drei’)
vier = tkinter.Label(fenster, text=’vier’)
fuenf = tkinter.Label(fenster, text=’fuenf’)
sechs = tkinter.Label(fenster, text=’sechs’)
eins.pack()
zwei.pack(side=tkinter.LEFT)
drei.pack(side=tkinter.RIGHT)
vier.pack(side=tkinter.LEFT)
fuenf.pack()
sechs.pack(side=tkinter.TOP)
fenster.mainloop()
Wenn Sie das Programm starten, erscheint ein Fenster wie in Abbildung 10.3. Vermutlich wird dies nicht die Anordnung sein, die Sie sich für Ihr Programm wünschen. Aber es zeigt die Möglichkeiten von pack()
. Kombinert mit anderen Layouts in anderen Frames kann daraus etwas werden.
Eine typische Anordnung von Kontrollelementen sieht so aus, dass links eine Beschriftung und rechts das passende Eingabefeld stehen soll. Darunter sollen weitere Eingaben folgen und am Ende des Fensters sitzen die Bestätigungsbuttons, wie es in Abb. 10.4 für die Eingabe einer Adresse zu sehen ist. Auch solche Anordnungen können Sie gestalten. Sie werden aber wohl noch den einen oder anderen Rahmen, also einen Frame verwenden müssen. In einem Frame können Sie Kontrollelemente, aber auch weitergehende Frames einsetzen und anordnen.
import sys
import tkinter
def ende():
sys.exit(0)
fenster = tkinter.Tk()
frEingabe = tkinter.Frame(fenster)
frEingabe.pack()
frLabel = tkinter.Frame(frEingabe)
frLabel.pack(side=tkinter.LEFT)
frEntry = tkinter.Frame(frEingabe)
frEntry.pack(side=tkinter.LEFT)
nameLbl = tkinter.Label(frLabel, text = "Name: ")
nameLbl.pack()
nameEntry = tkinter.Entry(frEntry)
nameEntry.pack()
strasseLbl = tkinter.Label(frLabel, text = "Straße Nr: ")
strasseLbl.pack()
strasseEntry = tkinter.Entry(frEntry)
strasseEntry.pack()
ortLbl = tkinter.Label(frLabel, text = "PLZ Ort: ")
ortLbl.pack()
ortEntry = tkinter.Entry(frEntry)
ortEntry.pack()
knopf = tkinter.Button(fenster, text = "Ok", command=ende)
knopf.pack()
fenster.mainloop()
Achten Sie darauf, dass die Elemente im ersten Parameter des Konstruktors nun nicht mehr alle das Hauptfenster erhalten. Stattdessen gibt es nun mehrere Frames.
Zuletzt werden einfach die Kontrollelemente je nach Art in den linken (frLabel) oder rechten Frame (frEntry) einsortiert. Das Ganze wirkt deshalb so schön gleichmäßig, weil Entry und Label glücklicherweise gleich hoch sind. Wäre das nicht der Fall, würde eine von beiden Seiten immer mehr heruntersacken. Dann müssten Sie beispielsweise auf das Layout Grid zurückgreifen, das im nächsten Abschnitt beschrieben wird.
Vielleicht missfällt Ihnen, dass NAME und PLZ ORT zentriert sitzen. Das könnten Sie ändern, wenn Sie bei den Pack-Aufrufen der Label-Felder in die Option anchor den Wert tkinter.W für Westen eintragen.
nameLbl.pack(anchor=tkinter.W)
Die folgende Aufstellung zeigt weitere Optionen, die die Methode pack()
verarbeiten kann.
pack(fill=tkinter.X)
Beim Befüllen des freien Raums benutzen die Widgets nur das, was sie wirklich benötigen. Mit der Option fill=X
dehnt sich das Widget so weit wie möglich in X-Richtung aus. BOTH
dehnt sich in beide Richtungen aus und bei NONE
dehnt sich gar nichts. Mögliche Werte sind X
, Y
, BOTH
und NONE
.pack(side=tkinter.LEFT)
Die Elemente werden horizontal ausgerichtet. Bei LEFT
werden die Widgets von links nach rechts aufgefüllt, bei side=RIGHT
von rechts nach links. Für die vertikale Ausrichtung gibt es BOTTOM
und TOP
.pack(expand=1)
Das Element wird sich auch dann ausdehnen, wenn es den möglichen Bereich gar nicht zur Darstellung benötigt. Vorgabe ist 0.pack(anchor=tkinter.NW)
Diese Option bestimmt, wie die Widgets innerhalb ihres Freiraums ausgerichtet werden. Vorgabe ist CENTER
, möglich sind alle Himmelsrichtungen: N, W, S, E, NW,
NE, SW, SE und eben CENTER.Wenn Sie Eingabe-Elemente gitterförmig anordnen wollen, wie beispielsweise in Abbildung 10.4 zu sehen ist, verhält sich das Grid-Layout etwas angenehmer als pack()
. Grid bildet ein Raster aus Spalten (column) und Zeilen (row). Diese sind durchnummeriert. Und wie Sie vermutlich bereits ahnen, beginnt auch hier die Zählung bei 0.
Ein Widget wird in dem Grid positioniert, indem dessen Methode grid()
aufgerufen wird. Die Parameter row und column übergeben die Position des Widgets im Raster. Das folgende Beispiel zeigt, wie die Beschriftung und das Eingabefeld jeweils nebeneinander und Name, Straße und Ort schön untereinander angeordnet werden.
import sys
import tkinter
def ende():
sys.exit(0)
fenster = tkinter.Tk()
frEingabe = tkinter.Frame(fenster)
frEingabe.pack()
nameLbl = tkinter.Label(frEingabe, text = "Name: ")
nameLbl.grid(row=0, column=0)
nameEntry = tkinter.Entry(frEingabe)
nameEntry.grid(row=0, column=1)
strasseLbl = tkinter.Label(frEingabe, text = "Straße Nr: ")
strasseLbl.grid(row=1, column=0)
strasseEntry = tkinter.Entry(frEingabe)
strasseEntry.grid(row=1,column=1)
ortLbl = tkinter.Label(frEingabe, text = "PLZ Ort: ")
ortLbl.grid(row=2, column=0)
ortEntry = tkinter.Entry(frEingabe)
ortEntry.grid(row=2, column=1)
knopf = tkinter.Button(fenster, text = "Ok", command=ende)
knopf.pack()
fenster.mainloop()
Sie sehen im Vergleich zu Listing 10.7, dass Sie für den Frame frEingabe nun keine weiteren Unterteilungen mehr benötigen. So wird das Programm kürzer und übersichtlicher. Außerdem bleiben die Elemente auch dann im Raster, falls sich die Höhen der Widgets unterscheiden. Die Frames frEingabe und der Button werden nach wie vor mit einem Pack untereinander angeordnet. Das ist auch völlig unproblematisch, solange Pack und Grid nicht im gleichen Frame angewendet werden.
Sie dürfen pack()
und grid()
niemals im selben Frame benutzen. Aber Sie können natürlich in einem Frame pack()
und in einem anderen grid()
verwenden.
Anstatt row oder column explizit auf 0 zu setzen, könnten Sie den entsprechenden Parameter auch weglassen, da 0 der Vorgabewert ist. Die möglichen weiteren Parameter von grid()
heißen:
row=0
Die Gitterzeile, in der das Widget positioniert werden soll.column=0
Die Gitterspalte, in der das Widget positioniert werden soll.sticky=W
Die Ausrichtung des Widgets im Raum. Mögliche Werte sind N, W, S, E, NW, NE,
SW, SE und CENTER.rowspan=2
Das Element nimmt zwei Zeilen in Anspruch.columnspan=3
Das Element nimmt drei Spalten in Anspruch.Wenn Sie eher für die Diktatur des Programmierers schwärmen, können Sie auch das Layout komplett selbst in die Hand nehmen und Ihre Widgets pixelgenau in das Eltern-Fenster platzieren. Dazu verwenden Sie die Methode place()
, deren Anwendung Sie in der folgenden Beispielzeile sehen.
meinLabel.place(x=10, y=20, width=50, height=24)
Damit nageln Sie Ihr Label an der Position 10,20 mit der Breite 50 und der Höhe 24 fest. Das folgende Beispiel zeigt eine Maske, in der Beschriftung und Eingabefelder von Hand gesetzt worden sind.
nameLabel.place(x=10, y=20, width=50, height=24)
nameEntry.place(x=70, y=20, width=50, height=24)
strasseLabel.place(x=10, y=50, width=50, height=24)
strasseEntry = tkinter.Entry(fenster)
strasseEntry.place(x=70, y=50, width=50, height=24)
ortLabel.place(x=10, y=80, width=50, height=24)
ortEntry.place(x=70, y=80, width=50, height=24)
Der Vorteil dieses Verfahrens ist, dass Sie die Kontrolle über die Positionierung Ihrer Kontrollelemente komplett selbst übernehmen. Sie können auch ungewöhnliche Anordnungssituationen damit lösen.
Der Nachteil ist aber, dass Sie wirklich alles unter Kontrolle haben müssen. Das Beispiel in Abbildung 10.5 sieht grausig aus. Um ein ordentliches Layout zu erstellen, müssten Sie beispielsweise auch prüfen, ob die Schriften so groß sind, dass sie gut in den von Ihnen freigehaltenen Raum passen. Schnell sieht ein Fenster völlig überlaufen aus oder die Beschriftung verliert sich in leeren Fenstern, deren Freiraum danach schreit, als Werbefläche vermietet zu werden.
x=0
Position in der Waagerechten in Pixeln (Vorgabe ist 0)y=0
Position in der Senkrechten in Pixeln (Vorgabe ist 0)width=0
Breite des Widgets in Pixelnheight=0
Höhe des Widgets in Pixelnanchor=NW
Ausrichtung des Widgets. Alle Himmelsrichtungen werden als englische Abkürzung angegeben, also N, NE, E, SE, S, SW, W und NW.Die Positionen können allerdings auch als Verhältnis angegeben werden. Dazu gibt es spezielle Optionen, denen die Silbe rel für relativ vorangestellt werden. Die Fließkommawerte liegen zwischen 0 und 1 und geben das Verhältnis in Bezug auf das Eltern-Fenster an.
relx=
Fließkommawert zwischen 0 und 1. Er wird mit der Breite des Eltern-Fensters multipliziert, um die Entfernung vom linken Rand festzulegen.rely=
Fließkommawert zwischen 0 und 1. Er wird mit der Höhe des Eltern-Fensters multipliziert, um die Entfernung vom oberen Rand festzulegen.relwidth=
Fließkommawert zwischen 0 und 1. Er wird mit der Breite des Eltern-Fensters multipliziert, um die Breite des Widgets festzulegen.relheight=
Fließkommawert zwischen 0 und 1. Er wird mit der Höhe des Eltern-Fensters multipliziert, um die Höhe des Widgets festzulegen.Und wenn Sie eh schon alles von Hand festlegen wollen, dann können Sie auch gleich noch die Größe und Position des Eltern-Fensters über die Methode geometry()
setzen. Die Methode erwartet als Parameter eine Zeichenkette, in der Größe und Position codiert werden.
fenster = tkinter.Tk()
fenster.geometry("{}x{}+{}+{}".format(300, 400, 190, 500))
Das Fenster ist nun 300 Pixel breit und 400 Pixel hoch. Es steht 190 Pixel vom linken Rand und 500 Pixel vom oberen Rand entfernt auf dem Bildschirm.
Einige Kontrollelemente haben sich in den bisherigen Abschnitten bereits kurz vorgestellt: der Button, das Entry und das Label. Alle Kontrollelemente besitzen Attribute und Methoden. Beim Layout haben wir bereits gesehen, dass alle Widgets die Methoden pack()
, grid()
und place()
besitzen. Darüber hinaus haben die Kontrollelemente auch weitere individuellere Methoden.
Hinzu kommen Optionen. Dazu zählen beispielsweise Beschriftungen, Farben und Größen. Diese können bereits beim Erzeugen der Widgets als zusätzliche Parameter über ihren Namen gesetzt werden. Alternativ ist es möglich, diese mit der Methode configure()
anzusprechen. Darüber hinaus können Sie ein Kontrollelement wie ein Dictionary behandeln und die Optionen über rechteckige Klammern setzen.
In der folgenden Zeile wird ein Button erzeugt, dessen Eltern-Widget elternfenster heißt. Die Optionen text und command werden vorbelegt.
knopf = Button(elternfenster, text="Ende", command=ende)
Die folgende Übersicht zeigt verschiedene Optionen, die bei den meisten Widgets gesetzt werden können.
DISABLE
gesetzt wird. Standardmäßig ist dies Grau.Bei allen Farbangaben können Sie die Standardfarben als Zeichenkette übergeben, also ’black’
, ’white’
, ’yellow’
, ’blue’
, ’red’
und ’green’
verwenden.
Alternativ können Sie die Farben als hexadezimale RGB-Zeichenkette eingeben, wie Sie es vielleicht von HTML, der Beschreibungssprache von Websites her kennen. Diese wird durch ein Doppelkreuz eingeleitet. Dann folgen sechs Ziffern, jeweils zwei für den Rot-, Grün- und Blau-Anteil.
farbe=’#45E01A’
Je höher der jeweilige Wert, desto intensiver die Farbe. ’#000000’
ist Schwarz. Falls Ihnen hexadezimale Werte nicht liegen, können Sie die Zeichenkette auch leicht über einen format()
-Aufruf (siehe Abschnitt 8.4.1) erstellen.
farbe=’#{:2x}{:2x}{:2x}’.format(88, 255, 199) # ergibt ’#58ffc7’
Da für jeden der RGB-Anteile ein Byte zur Verfügung steht, können die drei Werte zwischen 0 und 255 liegen. Python erlaubt auch, drei Stellen pro Farbanteil zu verwenden und erreicht damit eine 12-Bit-Codierung, die einen deutlich weiteren Farbraum bietet. Die Farbe Grün wäre dann mit ’#000FFF000’
codiert.
In Listing 10.3 haben Sie gesehen, wie zwei Optionen des Buttons bei seiner Initialisierung gesetzt wurden.
knopf = Button(fenster, text=’Ende’, command=ende)
Die Beschriftung (text) wurde auf ’Ende’
gesetzt und die Eventbehandlung (command) auf die Funktion ende()
gelenkt. Wollen Sie die Optionen nachträglich verändern, können Sie die Methode configure()
aufrufen.
knopf.configure(text = ’Ende’)
Sie können Optionen auch über eine Dictionary-Funktionalität des Buttons verändern. Dazu setzen Sie die Option als Zeichenkette in rechteckigen Klammern ein. Damit wird die Option zum Schlüsselwert.
knopf[’text’] = ’Ende’
Sie können über die rechteckigen Klammern den Inhalt der Optionen auch auslesen.
print(knopf[’text’])
print(knopf[’bg’])
Wollen Sie alle Optionen eines Kontrollelements kennenlernen, können Sie seine Methode keys()
aufrufen.
print(knopf.keys())
Einige Kontrollelemente korrespondieren mit Datenvariablen. So können Sie dem Eingabefeld eine Textvariable zuweisen, die ständig mit dem Widget abgeglichen wird. Änderungen an der Variablen schlagen sich im Eingabefeld nieder und Änderungen im Eingabefeld wirken sich sofort auf die Variable aus.
Leider können Sie nicht einfach die normalen Python-Variablen verwenden, sondern müssen auf die Tkinter-Variablen zugreifen. Davon gibt es vier Typen.
StringVar()
: nimmt eine Zeichenkette aufIntVar()
: nimmt einen ganzzahligen Wert aufDoubleVar()
: nimmt einen Fließkommawert aufBooleanVar()
: nimmt einen booleschen Wert aufAuch das Zuweisen und Auslesen funktioniert nicht einfach per Zuweisung, sondern über die Methoden get()
zum Auslesen und set()
zum Zuweisen von Inhalten.
Ein Label ist ein statisches Element ohne Funktion. Es dient in erster Linie dazu, Beschriftungen zu präsentieren.
Wie jedes Kontrollelement braucht auch das Label als ersten Parameter das Eltern-Fenster. Meist ändert das Label seine Beschriftung im Programmverlauf nicht. Insofern erhält es seinen Text meist bereits bei der Erstellung.
anzeige = Label(fenster, text="Guten Tag")
Sie können einem Label aber auch nachträglich jederzeit einen anderen Text zukommen lassen. Das funktioniert durch Zuweisung an das Options-Dictionary oder über die Methode configure()
.
anzeige["text"] = "Guten Abend"
anzeige.configure(text= "Guten Abend")
Mit der Option bitmap erhält das Label keinen Text, sondern ein Symbol. Python liefert einige Symbole wie ’error’
, ’hourglass’
, ’info’
, ’questhead’
, ’question’
und ’warning’
. Hinzu kommen zwei Symbole, die lediglich ein dunkleres Rechteck darstellen: ’gray25’
und ’gray50’
. Die Zeichenkette wird einfach der Option bitmap zugewiesen.
hinweis = tkinter.Label(fenster, bitmap="error")
Eine Schriftart wird als Tupel (siehe Abschnitt 9.2) übergeben. Es enthält folgende Informationen:
bold
für Fettdruck und normal
für Normalgewichtige möglich.italic
wird die Schrift kursiv und mit roman
wird sie normal gesetzt.Es ist möglich, alles in einer Zeichenkette abzulegen. Dazu werden die Angaben durch Leerzeichen getrennt. Die folgenden Zeilen liefern das gleiche Ergebnis.
Label(fenster, text=’abc’, font=(’Times’,’16’,’bold’,’italic’))
Label(fenster, text=’abc’, font=’Times 16 bold italic’)
Es ist auch möglich, ein Font-Objekt anzulegen. Leider ist das wieder zwischen Python 2 und Python 3 ein wenig unterschiedlich gelöst.
tkinter.font
importiert, die ebenfalls Font zur Verfügung stellt.Auch hier können wir mit den Exceptions eine Portabilität schaffen.
try:
import tkinter
import tkinter.font as font
except:
import Tkinter as tkinter
import tkFont as font
fenster = tkinter.Tk()
meinfont = font.Font(family=’Times’, size=16, weight=’bold’,
slant=’italic’, underline=1, overstrike=1)
eins = tkinter.Label(fenster, text="Gestrichen und kursiert",
font=meinfont)
eins.pack()
fenster.mainloop()
Zu guter Letzt sehen Sie hier noch einmal die Aufzählung der wichtigsten Optionen eines Labels.
Ein Button ist eigentlich nur ein bewegliches Label. Seine Hauptfunktionalität besteht darin, dass der Anwender ihn drücken kann, also quasi ein Schmuse-Widget.
Nach dem Drücken sollte aber auch etwas passieren. Was passieren soll, schreiben Sie zunächst in eine Funktion und geben deren Name der Option command an. Dann wird Ihre Funktion jedes Mal aufgerufen, wenn jemand den Button drückt. Das folgende Beispiel zeigt, wie es geht.
import tkinter def rufMich():
anzeige.configure(text="Jaaaa")
fenster = tkinter.Tk()
anzeige = tkinter.Label(fenster, text="Hallo")
knopf = tkinter.Button(fenster, text="Mach Druck!", command=rufMich)
anzeige.pack()
knopf.pack()
fenster.mainloop()
Ein Button kennt alle Optionen eines Labels, darüber hinaus aber noch einige, die sich aus der Besonderheit eines Buttons ergeben.