Python 02

Werbung
GUI –Programmierung
mit Python
1
Inhaltsverzeichnis
1. Grundlagen der Objektorientierten Programmierung (OOP) ............................................................. 4
1.1. Klassen und Objekte ..................................................................................................................... 4
1.2. Kapselung ..................................................................................................................................... 6
1.3. Vererbung ..................................................................................................................................... 8
2. Erste Oberflächen ................................................................................................................................ 9
2.1. Hallo Welt!.................................................................................................................................. 10
2.1.1. Basisprogramm .................................................................................................................... 10
2.2.1. Dekorative Verfeinerungen ................................................................................................. 10
2.2.2. Programmende .................................................................................................................... 13
2.2.3. Eigenschaftsänderungen zur Laufzeit.................................................................................. 14
2.2.4. Benutzereingaben - Steuerelementvariablen ..................................................................... 15
2.3. Farbmischer ................................................................................................................................ 16
2.3.1. Layout .................................................................................................................................. 16
2.3.2. Eine Farbe – Rot.................................................................................................................. 18
2.3.3. Ein Mischlabel...................................................................................................................... 21
2.4. Zeichnen mit Python .................................................................................................................. 23
2.4.1. Canvas.................................................................................................................................. 23
2.4.1. Noch ein Nikolaus ................................................................................................................ 23
2.4.2. Farbwahl .............................................................................................................................. 24
2.4.3. Kreise und Kreisbögen ......................................................................................................... 24
2.4.4. Dreiecke – Vierecke – Polygone .......................................................................................... 25
2.4.5. Texte .................................................................................................................................... 25
2.4.6. Bewegung ............................................................................................................................ 26
2.4.7. Der springende Punkt. ......................................................................................................... 27
3. Ein Spiel ............................................................................................................................................. 29
3.1. Basics .......................................................................................................................................... 29
3.1.1. Leinwand ............................................................................................................................. 29
3.1.2. Der Ball ................................................................................................................................ 29
3.2. Bewegung für den Ball................................................................................................................ 30
3.2.1. Einfache Bewegung ............................................................................................................. 30
3.2.2. Hin und wieder zurück......................................................................................................... 31
3.2.3. Irgendwo hin........................................................................................................................ 31
3.2.4. Ein Prallbrett ........................................................................................................................ 32
3.2.5. Bewegung mit den Maustasten........................................................................................... 32
3.2.6. Treffer feststellen ................................................................................................................ 33
2
3.2.7 Quelltext ............................................................................................................................... 36
6. Literatur ............................................................................................................................................. 38
3
1. Grundlagen der Objektorientierten Programmierung
(OOP)
Vor der objektorientierten Programmierung wurden Programme von oben nach unten abgearbeitet.
Wollte man an eine bereits abgearbeitete Stelle oder musste eine Stelle im Quelltext übersprungen
werden, dann gab es Befehle, mit denen man an diese Sprünge bewerkstelligen konnte. Diese Art der
Programmierung war unübersichtlich und fehleranfällig. Insbesondere war ein Programmierer, der
das Programm nicht entwickelt hatte, kaum in der Lage, sich schnell in das Programm einzuarbeiten.
Mit der objektorientierten Programmierung gab es dann einige Veränderungen.
Die OOP wird durch drei Grundelemente bestimmt:
Klassen und Objekte
Kapselung
Vererbung
1.1. Klassen und Objekte
Jedes Programm, das man benutzt, besteht aus Klassen mit Objekten. Den Klassen und ihren
Objekten sind bestimmte Attribute (Eigenschaften) und bestimmte Methoden
(Veränderungsmöglichkeiten) zugeordnet.
Ein Beispiel aus der analogen Welt: das Pferd.
PFERD
Geschlecht
Größe
Farbe
traben
schreiten
galoppieren
Falbe: PFERD
Geschlecht: männlich
Größe: 2,00m
Farbe: falb
traben
schreiten
galoppieren
Schimmel: PFERD
Geschlecht: männlich
Größe: 2,00m
Farbe: weiß
traben
schreiten
galoppieren
Dies ist die Klassenkarte. Sie
beschreibt alle Eigenschaften aller
Mitglieder dieser Klasse und alle
Methoden aller Klassenmitglieder.
Aus der Klasse werden Objekte
abgeleitet. Im Gegensatz zur Klasse
besitzen hier die Eigenschaften
Werte. Grundsätzlich lassen sich
unendlich viele Objekte von einer
Klasse ableiten. Allerdings besitzen
sie nur die Attribute und Methoden
der Klasse, aus der sie stammen.
Klassen in Python werden über die Module bereitgestellt, lassen sich aber mit dem class-Befehl auch
direkt erstellen.
class meineklasse(object):
pass
Nun gibt es eine Klasse meineklasse, die allerdings, festgelegt durch pass, leer ist.
4
Methoden innerhalb einer Klasse sind eigentlich nichts anderes als Funktionen, die als ersten
Parameter den Bezug zur Klasse beinhalten.
# Definition der Klasse Baum
class Baum:
wachstum = 0
# Eigenschaft
def wachstumstempo(self, wert):
# Methode
self.wachstum += wert
def ausgabe(self):
# Methode
print ("Wachstum:", self.wachstum)
Der Parameter self verweist auf die Klasse Baum.
Diese Klasse kann man nun auch nutzen:
# Definition der Klasse Baum
class Baum:
wachstum = 0
# Eigenschaft
def wachstumstempo(self, wert):
# Methode
self.wachstum += wert
def ausgabe(self):
# Methode
print ("Wachstum:", self.wachstum)
# Objekte der Klasse Baum erzeugen
eiche = Baum()
ulme = Baum()
# Objektmethoden
eiche.ausgabe()
eiche.wachstumstempo(200)
eiche.ausgabe()
#Ursprungswert anzeigen
#neuen Wert hinzufügen
#neuen Wert anzeigen lassen
# Objekt betrachten
ulme.ausgabe()
#vgl. Theis: Einstieg in Python
Die Ausgabe sähe so aus:
Wachstum: 0
Wachstum: 200
Wachstum: 0
In komplexeren Programmen werden Objekte initialisiert, d.h. ihnen werden von vornherein
bestimmte Attribute zugewiesen, deren Wert bei Programmstart festgelegt wird, damit das
Programm korrekt startet.
5
Das obige Beispiel verändert sich dadurch etwas.
# Definition der Klasse Baum
class Baum:
def __init__(self,wachstum):
#Objekt initialisieren,
Parameter nennen
self.wachsen = wachstum #der klasseeigenen Variablen
den Wert des Parameters
Wachstum geben
def wachstumstempo(self, wert):
# Methode mit einem
Parameter
self.wachsen += wert
# Variable wachsen wird
um Wert des Parameters
verändert, d.h.
self.wachsen=self.wachsen+wert
def ausgabe(self):
# Methode
print ("Wachstum:", self.wachsen)
# Objekte der Klasse Baum erzeugen
eiche = Baum(0)
#Wert aller Parameter muss in
der Klammer stehen
ulme = Baum(1)
# Objektmethoden
eiche.ausgabe()
eiche.wachstumstempo(200)
eiche.ausgabe()
# Objekt betrachten
ulme.ausgabe()
Bei der Initialisierung wird festgelegt, dass alle Objekte der Klasse Baum einen Parameter
wachstum haben. Dieser muss, wenn ein Objekt, z.B. eiche oder ulme, erstellt wird, auch mit
einem Wert versehen werden.
1.2. Kapselung
(oop_kapselung.py)
Objekte besitzen, wie bereits gesagt, nur die Attribute und Methoden, die ihre Klasse bereitstellt. Auf
Eigenschaften kann nur mit Hilfe der korrekten Methoden zugegriffen werden. Dieses Konzept
innerhalb der Objektorientierung nennt man Kapselung.
Anders formuliert: Pferde können nicht fliegen.
Dies zwingt zu sauberer Programmierung und Nutzung der für eine Aufgabe passenden Objekte.
Bislang sind die Daten in unserem Beispiel nicht gekapselt, d.h. theoretisch kann von außen auf sie
zugegriffen werden. Möglich wäre also dies:
# Definition der Klasse Baum
class Baum:
6
def __init__(self,wachstum):
self.wachsen = wachstum
def wachstumstempo(self, wert):
self.wachsen += wert
def ausgabe(self):
print ("Wachstum:", self.wachsen)
# Objekte der Klasse Baum erzeugen
eiche = Baum(0)
ulme = Baum(1)
eiche.wachsen=100
# Objektmethoden
eiche.ausgabe()
eiche.wachstumstempo(200)
eiche.ausgabe()
# Objekt betrachten
ulme.ausgabe()
In der fett gedruckten Zeile wird auf den Wert des Attributs zugegriffen. Dies ermöglicht
Manipulationen und ist nicht gewollt. Deshalb gibt es die Möglichkeit, Attribute mittels Unterstrich
als protected oder doppeltem Unterstrich private zu deklarieren, um sie zu schützen.
Attribut
wachsen
_wachsen
__wachsen
Kapselung
Public
Protected
Private
auch von außen les- und schreibbar
von außen les- und schreibbar, aber dies ist unerwünscht
nicht von außen les- und schreibbar
Aufgabe:
1. Erstelle ein Programm mit verschiedenen Klassen und Objekten.
2. Wende die Datenkapselung an und teste ihre Auswirkungen!
7
1.3. Vererbung
Die OOP und übrigens auch das wirkliche Leben
kennen Ober- und Unterklassen. Dabei gilt:
Unterklassen erhalten von der Oberklasse alle
Attribute und Methoden.
Das Beispiel aus der analogen Welt:
An nebenstehendem Schema wird deutlich, dass
die Klasse PFERD offensichtlich eine Reihe von
Attributen und Methoden von übergeordneten
Klassen geerbt hat, z. B. Atmung, Knochen etc.
In Python lassen sich dann auch neue Klassen und
Objekte generieren, die Attribute und Methoden
aus zwei Oberklassen enthalten. Dies nennt man
Mehrfachvererbung.
In Python hieße das z. B.
class Baum(Algen, Moose).
Darüber können Klassen auch Methoden erben,
die ihnen eigentlich nicht zugedacht wären:
Class Pferde(saeugetiere,
voegel)
In Python können Pferde fliegen.
http://www.digitalefolien.de/biologie/tiere/tntierr.gif
Aufgabe:
Erstelle eine Übersicht zu Ober- und Unterklassen im Fahrzeugbereich.
8
2. Erste Oberflächen
Moderne Programme verarbeiten zwar Programmtext, sobald sie aber auch Nutzereingaben
einschließen, werden sie mit einer grafischen Benutzeroberfläche versehen, damit die Nutzer nicht
nur vor einer Textkonsole sitzen müssen. Python stellt dafür das Modul tkinter (Tool Kit Interface)
bereit, das, ähnlich wie z.B. turtle, über den entsprechenden Befehl importiert werden muss.
from tkinter import *
Hiermit werden alle Klassen des Moduls mit ihren Attributen und Methoden bereitgestellt.
TKinter bietet die folgenden Widgets (Fensterkomponenten):
Klasse
Tk
Beschreibung
Hauptfenster einer Applikation mit Knöpfen zum lkonisieren, Vergrößern und
Schließen
Label
Feld mit vorgegebenem Text oder Bild
Button
Schaltfläche
Canvas
Vektorgrafiken
Checkbutton Quadratisches Feld für Mehrfachauswahl (m aus n)
Entry
Einfaches Eingabefeld für Texte
Frame
Rechteckiger Bereich (»Oberfläche«), in den man andere Widgets platzieren kann
Listbox
Liste von Auswahlfeldern (Einfach- und Mehrfachauswahl)
Menu
Darstellung von Toplevel-, Pulldown- und Popup-Menüs
Photoimage Bild (GIF oder PPM /PGM)
Radiobutton Rundes Feld für Einfachauswahl (1 aus n)
Scale
Waagrechte oder senkrechte Schieberegler
Scrollbar
Scrollleiste, die z.B. in Listbox-, Text- oder Canvas-Widgets eingebaut werden
kann, um die Darstellung umfangreicher Inhalte zu ermöglichen
Text
Textfeld, in dem mehrzeilige formatierte Texte bearbeitet werden können
(M. Weigend: Python GE-PACKT. Mitp, Heidelberg 2013, 5. Aufl., S. 458f)
9
2.1. Hallo Welt!
Der Klassiker ist natürlich das altbekannte „Hallo Welt!“.
2.1.1. Basisprogramm
Zunächst wird das Modul tkinter
importiert.
from tkinter import *
fenster=Tk()
Die Benutzeroberfläche des
späteren Programms erhält die
Bezeichnung fenster.
lbl1=Label(fenster,text="Hallo Welt!")
Der sog. Packer packt – daher die
Bezeichnung - das Objekt auf die
Oberfläche.
lbl1.pack()
Tipp
Objekte sollten, ebenso wie Variablen und Funktionen, so benannt werden, dass sie
problemlos zugeordnet werden. So erhalten Label z.B. den Vorsatz lbl, Buttons ein
btn etc. In komplexeren Programmen ist dies für erfolgreiches Arbeiten unerlässlich.
2.2.1. Dekorative Verfeinerungen
Natürlich ist dies nur ein Anfang. Hier wird das Label weiter gestaltet. Die Kommentare erläutern die
einzelnen Befehle. Der Backslash wird in Python genutzt, um lange Programmzeilen mit einem
Zeilenumbruch zu versehen, ohne dass sie als getrennte Befehlsfolgen interpretiert werden.
from tkinter import *
fenster=Tk()
lbl1=Label(fenster, #Festlegung des Eltern-Elements \
text="Hallo Welt!", #Festlegung des Textes auf dem Label\
bg="yellow",
#Hintergrundfarbe(background)\
bd=5,
#Randstärke(border)\
relief=GROOVE, #Randform\
fg="Red",
#Schriftfarbe (foreground)\
font=('Verdana',25,'bold'), #Schrift(-art, -größe, -stil)\
padx=10,
#Innenabstand in x-Richtung\
pady=10,
#Innenabstand in y-Richtung\
height=2,
#Höhe in Textzeilen(nur Elemente mit Text)\
width=12)
#Breite in Pixel\
lbl1.pack()
#Positionierung auf dem Fenster\
fenster.mainloop() #Endlosschleife für Useraktionen\
10
Das ist das Ergebnis des obigen
Quelltextes.
Die Standard – Optionen der tkinterWidgets sind:
Option
activebackground
activeforeground
anchor
bd, borderwidth
bg, background
Bitmap
Cursor
default
disabledforeground
fg, foreground
font
height
image
justify
padx
pady
relief
text
textvariable
underline
width
xscrollcommand
yscrollcommand
Erklärung
Hintergrundfarbe, wenn das Widget aktiv ist (z.B. wenn Button
»gedrückt« wird)
Vordergrundfarbe, wenn das Widget aktiv ist
Mögliche Werte: CENTER, E, N, W, S, NE, NW, SE, SW Immer wenn ein
Widget kleiner ist als der Platz, der für es vorgesehen ist (Zelle), wird mit
diesem Attribut die Platzierung in der Zelle durch »Himmelsrichtungen«
festgelegt. Default ist meist CENTER (in der Mitte), NE: rechte obere
Ecke, N: mittig an der oberen Seite, E: mittig an der rechten Seite usw.
Breite des Rahmens des Widgets, z.B. "lc" oder 10
Hintergrundfarbe
Name einer Standard-Bitmap, die auf dem Widget zu sehen sein soll
Name eines Standard-Cursors, der über dem Widget verwendet wird
(X_cursor, ,
...)
Voreingestellt ist NORMAL. Mit dem Wert DISABLED wird das Widget
deaktiviert.
Vordergrundfarbe (Textfarbe), falls das Widget deaktiviert ist
Vordergrundfarbe (Textfarbe)
Front-Deskriptor für den verwendeten Schrifttyp (Font)
Höhe des Widgets (senkrecht), z.B. "lc" oder 100. Bei Elementen mit
Text in Zeilen, sonst in Pixeln angegeben.
Name eines Bildes (Image-Objekt), das auf dem Widget (z.B. Button) zu
sehen ist
Ausrichtung von Textzeilen auf dem Widget:
CENTER: zentriert
LEFT, RIGHT: links- oder rechtsbündig
Leerer Raum rechts und links vom Widget oder Text, z.B. "0.5c" oder 10
Leerer Raum über und unter dem Widget oder Text, z.B. "lc" oder 10
Form des Rahmens: SUNKEN, RAISED, GROOVE, RIDGE, FLAT
Beschriftung des Widgets (z.B. Button oder Label)
Ein Objekt der Klasse StringVar, das den (variablen) Text enthält, der auf
dem Widget (z.B. Button oder Label) erscheint
Default ist -1. Wenn die Zahl nicht negativ ist, gibt sie die Nummer des
Zeichens an, das unterstrichen sein soll.
Breite des Widgets (horizontal), z.B. "3c" oder 100
Wenn das Widget scrollbar ist, wird die set( )-Methode des horizontalen
Scrollbar-Objektes angegeben.
Wenn das Canvas scrollbar ist, wird die set( )-Methode des vertikalen
Scrollbar-Objektes angegeben.
11
Der Packer bietet die Möglichkeit, die von ihm gepackten Objekte zu positionieren bzw.
Eigenschaften mit Wirkung auf die Position festzulegen.
Die wichtigsten Optionen für den Packer
Option
Erklärung
anchor
Mögliche Werte: CENTER, E, N, NE, NW, S, SE, SW, W.
Widget wird in eine Ecke oder mittig an einer Seite der Zelle platziert, entsprechend
der angegebenen »Himmelsrichtung« (z.B. NE = Nordost = rechts oben).
expand
expand=0: Die Größe des Widgets ändert sich nicht, wenn das Anwendungsfenster
vergrößert wird.
expand=1: Die Größe des Widgets passt sich an, wenn das Anwendungsfenster
vergrößert wird.
fill
fi 11=X: Das Widget wird in waagrechter Richtung (»x-Achse«) soweit mit leerem
Raum gefüllt, dass sich seine Ausmaße der Größe des Masters anpassen.
fill=Y: Das Widget wird in senkrechter Richtung (»y-Achse«) an die Größe des Masters
angepasst.
fill=BOTH: Das Widget passt sich in beiden Richtungen dem Master an.
fill=None: Das Widget behält seine Größe unabhängig von den Ausmaßen des
Masters.
padx
Die Zelle wird rechts und links vom Widget in der angegebenen Länge mit leerem
Raum gefüllt. Die Option padx=10 bewirkt z.B., dass die Zelle rechts und links um
zehn Pixel verbreitert wird.
pady
Die Zelle wird oberhalb und unterhalb des Widgets mit leerem Raum gefüllt.
side
LEFT: Das Widget wird an den linken Rand des Masters gesetzt. RIGHT: Das Widget
wird an den rechten Rand des Masters gesetzt.
TOP: Das Widget wird nach oben gesetzt.
BOTTOM: Das Widget wird nach unten gesetzt.
Bei komplexeren Benutzungsoberflächen sind an einigen Stellen Widgets nebeneinander und an
anderen Stellen untereinander angeordnet. In diesem Fall verwendet man Frames. Für die Planung
eines Layouts mit dem Packer kann man den Entwurf durch gerade Schnitte so weit zerlegen, bis jede
zusammenhängende Gruppe von Widgets einheitlich orientiert ist. Das heißt, die Widgets der
Gruppe sind entweder von rechts nach links oder von oben nach unten aufgereiht. Für jede Gruppe,
die aus mehr als einem Widget besteht, erzeugen Sie einen Frame, dem die enthaltenen Widgets als
Slaves zugewiesen werden.
(in: M. Weigend: Python 3. Mitp, Heidelberg 2013, 5. Aufl., S. 462)
12
2.2.2. Programmende
Um ein Programm zu beenden, reicht es zwar, das Fenster zu schließen. Aber auch ein Button wäre
schön.
Mit dem Befehl
meinbutton=Button(fenster, text="Ende")
meinbutton.pack()
wird ein Button in das Fenster gelegt.
Aufgabe:
1. Gestalte Button und Label passend.
2. Positioniere beide mit Hilfe des Packers.
Allerdings ist eine Nutzung noch nicht möglich. Dafür muss dem Button noch Befehlstext zugewiesen
werden.
Dazu wird zunächst eine Funktion erstellt, die das Hauptfenster beendet.
def ende():
fenster.destroy() #Beendet das Hauptfenster
Dann wird diese Funktion im Button als Parameter verankert.
meinbutton=Button(fenster, text="Ende",command=ende)
Aufgabe:
Ergänze das Programm entsprechend.
Hinweis
Farben in Tkinter
Zwei Standardfarben sind black und white. Dazwischen liegen Grautöne. gray ist ein
mittleres Grau. Es gibt aber 101 feine Abstufungen von schwarzem Grau, gray0, bis zu
weißem Grau, gray100.
color("red")
color("red1")
color("red2")
color("red3")
color("red4")
ergibt ein strahlendes Rot
entspricht color("red ")
etwas dunkler
noch etwas dunkler
ziemlich dunkel
Farbtabelle aller Farbnamen
Snow
LightCyan
Seashell
PaleTurquoise
AntiqueWhite
CadetBlue
Bisque
turquoise
Peach Puff
cyan
NavajoWhite
DarkSlateGray
LemonChiffon
aquamarine
cornsilk
DarkSeaGreen
ivory
SeaGreen
honeydew
PaleGreen
LavenderBlush
SpringGreen
MistyRose
green
azure
chartreuse
tan
chocolate
firebrick
brown
salmon
LightSalmon
orange
DarkOrange
coral
tomato
OrangeRed
red
DeepPink
LightBlue
LightCyan
PaleTurquoise
LightBlue
SkyBlue
IndianRed
sienna
burlywood
wheat
gold
DarkOrchid
purple
Medium Purple
13
SlateBlue
RoyalBlue
blue
DodgerBlue
SteelBlue
DeepSkyBlue
LightSkyBlue
SlateGray
OliveDrab
DarkOliveGreen
khaki
LightGoldenrod
LightYellow
yellow
goldenrod
HotPink
pink
LightPink
PaleVioletRed
maroon
VioletRed
orchid
Thistle
magenta
plum
MediumOrchid
DarkGoldenrod
RosyBrown
LightSteelBlue
2.2.3. Eigenschaftsänderungen zur Laufzeit
Bislang kann man sich das Fenster ansehen, aber nichts machen, außer das Fenster zu schließen.
Aber natürlich können Attribute auch verändert werden. Die Veränderungen werden zunächst in
eine Funktion gelegt und dann über den command-Befehl in den Button-Parametern aufgerufen.
def labeltext():
lbl1.config(fg="RED4",font=('Verdana',15,'bold'),\
text="Schöne neue Welt", width=0)
btn_neutext=Button(fenster, text="Neuer
Text",command=labeltext)
Tipp
Es ist sinnvoll, seinen Quelltext zu
ordnen, um den Überblick zu
behalten. Zuerst müssen die
Module importiert werden, die
man benötigt, dann wird das
Fenster initialisiert.
Anschließend sollten die
Funktionen erstellt werden.
Die Objekte werden für die erste
Verwendung erstellt.
Aufgabe:
from tkinter import *
fenster=Tk()
#Meine Funktionen
def ende():
fenster.destroy()
#meine Objekte
btn_ende=Button(fenster,
text="Ende",command=ende)
Die Objekte werden dann über
den Packer auf dem Fenster
platziert werden.
#Ins Fenster damit!
btn_ende.pack(anchor=S,padx=10,
pady=10,expand=0,side=RIGHT)
Am Schluss wird das Fenster auf
permanente Aktivität gestellt.
#Immer auf Eingaben warten
fenster.mainloop()
1. Gestalte das Label selbst.
2. Integriere einen weiteren Button, der den Schließen-Button mit neuer Aufschrift
anderen Farben etc. versieht.
14
2.2.4. Benutzereingaben - Steuerelementvariablen
Natürlich sind auch hier Benutzereingaben möglich. Dazu wird die Klasse entry genutzt, die ein
Eingabefeld bereitstellt.
Der Inhalt des Text-Attributs kann über sog. Kontroll- oder Steuerelementvariablen genutzt werden.
Python stellt in Tkinter drei Variablentypen bereit:
DoubleVar()
- eine Gleitkommazahl, Standardwert: 0.0.
IntVar()
- eine Ganzzahl, Standardwert: 0.0
StringVar()
- ein String, als Standard leer: „“
Laden des Tkinter-Moduls und
initialisieren des Festers.
Um das Text-Attribut auszulesen,
muss zunächst eine entsprechende
Steuerelementvariable initialisiert
werden.
In den Parametern des Objekts,
das den Text enthält, z.B. einem
Eingabefeld, wird über das
Signalwort Textvariable festgelegt,
dass die Steuerelementvariable
den Inhalt des Textattributs
erhalten soll.
from tkinter import *
fenster=Tk()
eingabetext=StringVar()
eingabe_01=Entry(fenster,textvariable=ei
ngabetext)
def labeltext():
Das Übergeben der Textvariablen
wird in eine Funktion gelegt.
Ein Label mit der Aufschrift „Hallo
Welt!“ wird generiert.
Die Objekte werden auf das
Hauptfenster gelegt.
Die Funktion mit der Übergabe der
Variablen an das Label wird
aufgerufen.
lbl1.config(textvariable=eingabetext)
lbl1=Label(fenster,text="Hallo Welt!")
eingabe_01.pack()
lbl1.pack()
labeltext()
#Immer auf Eingaben warten
fenster.mainloop()
Ergebnis:
15
Aufgabe
Erweitere dein Programm,
so dass ein Klick auf einen
Button den Text eines
Labels ändert.
2.3. Farbmischer
2.3.1. Layout
Ziel dieses Abschnittes ist es, einen Farbmischer
mit grafischer Oberfläche zu erstellen, das Farben
über Schieberegler einstellt, die Farbwerte
anzeigt und die Mischfarbe mit ihrem
Hexadezimalwert anzeigt.
2.3.1.1. Frames
(farbmischer_01.py)
Hier reicht es nicht mehr, Objekte auf dem Fenster zu
platzieren. Vielmehr müssen diese über Frames angeordnet
werden.
Dazu wird das Fenster in einzelne Frames aufgeteilt, die auch
verschachtelt sein können.
Ein Weg besteht darin, das Fenster in Frames zu teilen.
In diesem Beispiel hätte das Fenster vier Frames, die
übereinander liegen.
Der Quelltext ist für alle drei Frames ähnlich. Hier am Beispiel des Fußbereichs mit dem Ende-Button.
16
Die Ende-Funktion wird
erstellt.
Das Fuss-Label wird
erstellt inkl. Text- und
Randformatierung.
Der Ende-Button wird im
Frame fuss erstellt.
Das Frame wird auf das
Fenster gepackt. Es soll
sich auf die gesamte
Fensterbreite ausdehnen
und 10px Außenabstand
haben.
Der Button wird rechts auf
das Frame gepackt.
Hinweis
def ende():
fenster.destroy()
fuss=LabelFrame(fenster,text="Farbmischer",width=900,height=50,\
borderwidth=6,font=('Verdana',15,'bold'))
btn_ende=Button(fuss, text="Ende", command=ende,\
font=('Verdana',15,'bold'))
fuss.pack(fill=X, padx=10,pady=10)
btn_ende.pack(anchor=E,padx=10,pady=10,expand=0,side=RIGHT)
Untergeordnete Elemente erhalten als ersten Parameter in der Klammer den Namen
des übergeordneten Elements, des sog. Masters. Bei komplexeren Programmen
sollte dies auch in einem Schema dargestellt werden.
2.3.1.2. Strukturschema
Für den Überblick ist ein Strukturschema sinnvoll, das die Nehmen und Über- und Unterordnungen
enthält. Dieses Beispiel enthält nur die Objektnamen und den Parameter, der das übergeordnete
Element benennt,
Fenster
fenster=Tk()
titel=LabelFrame(
fenster)
farben=LabelFra
me(fenster)
mischfarbe=Lab
elFrame(fenster)
fuss=LabelFram
e(fenster)
btn_ende=
Button(fuss)
17
2.3.1.3. Grids
Neben Frames bieten Grids eine gute Möglichkeit zur Layoutgestaltung. Hierbei wird über eine
Tabelle festgelegt, wie viele Zeilen und Spalten ein Element haben kann. Die einzelnen Objekte
werden dann entsprechenden Spalten zugewiesen und die Zellen passen sich in der Größe den
Objekten an.
So legt der Nachsatz .grid(column=1,row=1) fest, dass das Objekt sich auf Zeile 1 in Süalte 1
befindet.
Dies kann beliebig erweitert werden.
lbl_red=Label(fenster,height=10,width=20,bg='grey55').grid(colu
mn=1,row=1)
Typische Grid-Layouts wären z.B. Taschenrechner.
2.3.1.4. Pack
Der Packer ist der älteste und einfachste Layoutmanager in Python. Er legt alle Elemente auf eine
Ebene. Er wurde unter 2.2.1. bereits vorgestellt und wird im Folgenden verwendet.
Der Farbmischer besteht aus drei Scrollbars (Scale) und vier Labeln (Label) sowie einem Button
(Button) zum Schließen des Programms. Diese werden mittels Pack auf der Oberfläche angeordnet.
lbl_red.pack(anchor=W, side=LEFT)
skala_rot.pack(anchor=W, side=LEFT)
lbl_green.pack(anchor=W, side=LEFT)
skala_green.pack(anchor=W, side=LEFT)
lbl_blue.pack(anchor=W, side=LEFT)
skala_blue.pack(anchor=W, side=LEFT)
lbl_mix.pack(anchor=W, side=LEFT)
btn_ende.pack(anchor=S, side=BOTTOM,padx=10,pady=10,expand=0)
Hier werden die Labels und Scrollbars nebeneinander auf die Oberfläche gelegt. Die südliche
Ausrichtung des Schließen-Buttons bewirkt, dass dieser unten im Fenster liegt.
2.3.2. Eine Farbe – Rot
Nach dem Import des tkinter-Moduls, dem generieren des Fensters und der Änderung des
Fenstertitels wird der Schließen-Button konfiguriert.
from tkinter import *
fenster=Tk()
fenster.title('Farbmischer')
def ende():
fenster.destroy()
btn_ende=Button(fenster,command=ende,text='Ende')
Die ende-Funktion ist bereits bekannt. Sie schließt beim Aufruf das aktive Fenster.
Nun werden das Label für die rote Farbe und die Scrollbar für die rote Farbe generiert.
18
lbl_red=Label(fenster,height=10,width=20,bg='grey55')\
skala_rot=Scale(fenster, length=150,width=25, \
orient=VERTICAL, from_=1,to=255,\
command=farbanzeige,variable=r_wert)
Der Parameter command ruft eine Funktion auf, die für die Übergabe des Scrollwertes genutzt
werden wird. Scrollbars können den Scrollwert zurückgeben. Der Parameter variable generiert
eine Steuerelementvariable, die diesen Scrollwert der Scrollbar enthält. Über
r_wert=IntVar()
r_wert.set(200)
wird die Scrollwert-Variable als Integer-Variable initiiert und mit einem Startwert von 200 versehen.
Die Funktion get() erfasst dann die Werte der Scrollbar.
r_str=str(r_wert.get())
Exkurs: Werte konvertieren
Scrollbars geben Integer-Werte zurück. Diese müssen, damit sie als Farbangabe
genutzt werden, in Hexadezimal-Werte umgeformt werden. Die Funktion hex(x)
leistet dies und gibt einen entsprechenden String zurück.
Allerdings birgt diese Umwandlung ein paar Probleme, denn für die Farbangabe im
Label werden zweistellige Hexadezimalwerte für jeden der drei Farbbereiche Rot,
Grün und Blau benötigt. Es muss also sichergestellt werden, dass auch wirklich so
konvertiert wird. In der Konsole ergibt
>>> hex(9)
'0x9'
Das vorangestellte 0x ist das Hexadezimal-Kennzeichen, der eigentliche
Hexadezimalwert ist also 9.
Es muss also dafür gesorgt werden, dass das Hexadezimal-Kennzeichen verschwindet
und der Wert zweistellig wird, also ggf. eine Null vorangestellt wird.
Über die Methode replace kann man in den drei Argumenten festlegen, welches
Element eines Strings ersetzt werden soll, wodurch es ersetzt werden soll und wie oft
diese Ersetzung in einem String vorgenommen werden soll.
Beispiel
>>> a='Otto'
>>> a=a.replace('O','Mo',1)
>>> print(a)
Motto
>>>
Der Befehl len(x) gibt die Länge eines Strings zurück.
>>> len(a)
5
>>>
So ergibt sich für die Umwandlung der Steuerelementvariablen folgender Quelltext:
r_str=str(r_wert.get())
r=int(r_str)
print(r)
#Farbwerte in Hex-Werte umrechnen
r_hex=hex(r)
19
#Das HEX-Zeichen (0x) entfernen
r_hex=r_hex.replace('0x','',1)
#korrekte Länge der Hex-Werte herstellen
if len(r_hex)<2:
r_hex='0'+r_hex
Der print-Befehl dient nur der Kontrolle. Die Bedingung am Schluss prüft die
Länge und fügt eine Null hinzu, falls nötig.
Nun wird der Hexadezimalwert in eine neue Variable integriert und zur Kontrolle auf der Konsole
angezeigt. Fir RGB-Abfolge bleibt dabei auch bei der Aneinanderreihung der Hexadezimalwerte
erhalten.
farbe_red='#'+r_hex+'00'+'00'
print('#'+r_hex+'00'+'00')
lbl_red['bg']='#'+r_hex+'00'+'00'
Jetzt kann das Ganze in eine Funktion gelegt werden. Der Parameter self sorgt, dass die Funktion
immer wieder aufgerufen wird, damit Änderungen der Farbwerte auch erfasst werden. Die Funktion
wird durch das Bewegen der Scrollbar aufgerufen, muss also nicht gesondert im Programmtext
aufgerufen werden.
def farbanzeige(self):
# Die Farbwerte festlegen
r_str=str(r_wert.get())
r=int(r_str)
print(r)
#Farbwerte in Hex-Werte umrechnen
r_hex=hex(r)
#Das HEX-Zeichen (0x) entfernen
r_hex=r_hex.replace('0x','',1)
#korrekte Länge der Hex-Werte herstellen
if len(r_hex)<2:
r_hex='0'+r_hex
#neue Variable für den Farbwert festlegen
farbe_red='#'+r_hex+'00'+'00'
print('#'+r_hex+'00'+'00')
lbl_red['bg']='#'+r_hex+'00'+'00'
Aufgabe:
1. Jetzt sollte ein erster Test möglich sein. Teste das Programm!
2. Ergänze das Programm um die Quelltexte für die Farben Grün und Blau.
20
2.3.3. Ein Mischlabel
Ohne die Mischfarbe zu sehen, ist ein Farbmischer nicht sehr hilfreich. Das Problem dabei ist, dass
die Variablen, die zur Farbgestaltung von den Scrollbars übergeben werden, nur lokal in der Funktion
vorhanden sind.
Exkurs – Lokal – Global
Funktionen nutzen Variablen. Werden diese in der Funktion erstellt, dann sind sie
außerhalb der Funktion nicht nutzbar. Wurden die Variablen im Hauptquelltext
erstellt, kann die Funktion sie zwar nutzen, aber nicht umschreiben. Dieses Feature
sichert ungewolltes Verhalten von Programmen ab, muss aber manchmal
übergangen werden. Versucht man, eine lokale Variable global zu nutzen, gibt dies
eine Fehlermeldung.
>>> def test():
a=22
print(a)
>>> test()
22
#lokale Variable lokal genutzt
>>> print(a)
Traceback (most recent call last):
File "<pyshell#31>", line 1, in <module>
print(a)
NameError: name 'a' is not defined
>>>
Dementsprechend muss eine Variable global verfügbar, d.h. auf überschreibbar
gemacht werden. Möglich macht dies der Befehl global, gefolgt vom
Variablennamen. Die Variable kann allerdings nicht lokal generiert und dann
übergeben werden. Um sie zu nutzen, muss sie also einmalig global erstellt und mit
einem fiktiven Wert versehen werden.
>>> def test():
global a
#macht eine lokale Variable global
a=22
print(a)
>>> a=10
#Variable wird einmalig erstellt
>>> print(a)
10
>>> test()
22
>>> print(a)
22
>>>
Für den Farbmischer bedeutet dies, dass die Variablen für Rot, Grün, Blau und die Mischfarbe global
erstellt werden müssen.
fenster.title('Farbmischer')
r_hex='00'
21
g_hex='00'
b_hex='00'
farbe_mix='000000'
Dann beginnt die Funktion für die rote Farbanzeige mit der globalen Deklaration.
def farbanzeige_red(self):
global r_hex
Noch zeigt das Mischlabel aber keine Farben, Dazu muss dem Label permanent die korrekte
Hintergrundfarbe übergeben werden. Am einfachsten erfolgt dies durch eine Funktion in der
Funktion für die Farbe Rot.
Die Funktion legt die Variable farbe_mix als global fest und erstellt den kompletten
Hexadezimalwert aus den drei Farbwerten für Rot, Grün und Blau.
Die print-Anweisung dient wieder der Kontrolle und schließlich werden dem Label die
Hintergrundfarbe und ein Text mit dem Hexadezimalwert übergeben.
def farbanzeige_mix():
global farbe_mix
farbe_mix='#'+r_hex+g_hex+b_hex
print('Mixfarbe '+'#'+r_hex+g_hex+b_hex)
lbl_mix['bg']='#'+r_hex+g_hex+b_hex
lbl_mix['text']='#'+r_hex+g_hex+b_hex
farbanzeige_mix()
Aufgabe
1. Gestalte den Farbmischer für alle drei Farben.
22
2.4. Zeichnen mit Python
2.4.1. Canvas
Natürlich lassen sich mit Python auch Elemente auf der Programmoberfläche zeichnen. Nötig ist dazu
eine Zeichenfläche (Canvas).
Zunächst sind die Standartelemente des Skripts zu erstellen. Die Zeichenfläche wird dann über
z_flaeche=Canvas(fenster, width=900, height=900)
erstellt.
Aufgabe:
Erstelle ein Programmfenster mit einem Schließen-Button und einer Zeichenfläche in
der Größe 900x900.
2.4.1. Noch ein Nikolaus
Auf der Zeichenfläche kann nun gezeichnet werden. Dabei wird ein Koordinatensystem genutzt. Der
Nullpunkt liegt links oben, die Maximalwerte für x und y rechts unten.
Linien werden über den folgenden Befehl kreiert:
z_flaeche.create_line(0,0,900,900)
Die ersten beiden ersten Parameter geben den x - und y – Wert des Startpunktes, die beiden anderen
den des Endpunktes an.
Aufgabe:
1. Erstelle eine Linie, die diagonal von links oben nach rechts unten die Zeichenfläche
durchteilt.
2. Zeichne das Haus des Nikolaus in der richtigen Reihenfolge.
3. Ergänze die Linien um die Argumente width= und fill= für Linienbreite und
Linienfarbe. Nutze als Farbe die Farbnamen, also Blue, Red etc.
23
2.4.2. Farbwahl
Der Nutzer kann auch selbst Farben wählen. Dazu dient die Funktion colorchooser in tkinter. Sie ruft
ein Farbwahlfenster aus. Die Funktion
colorchooser.askcolor()
gibt einen Tupel zurück, der aus zwei Teilen besteht. Teil eins ist ein Tupel, der die drei RGBFarbwerte enthält, der zweite Teil ist ein String, der den Hexadezimalwert enthält.
Beispiel:
print(colorchooser.askcolor())
ergibt also
((251.98046875, 0.0, 6.0234375), '#fb0006')
Rot
Green
Blue
Hex
Dementsprechend kann man eine Variable mit dem Hex-Wert dieses Tupels füllen und damit die
Farbe des Hauses festlegen:
farbe=colorchooser.askcolor()
z_flaeche.create_line(200,800,400,800,width=7, fill=farbe[1])
Die in eckige Klammern gibt an, dass der zweite Wert des Tupels, in dem ja mit Null beginnend
gezählt wird, für die Farbangabe genutzt werden soll.
Aufgabe:
Gestalte das Haus so um, dass der Nutzer die Farbe selbst wählen kann.
2.4.3. Kreise und Kreisbögen
Kreise und Kreisbögen sind ebenso möglich. Sie werden als
Element innerhalb eines Quadrats angesehen. Das bedeutet,
dass bei der Koordinatenangabe ein Quadrat angenommen wird,
dessen Koordinaten angegeben werden und in dem dann der
Kreisbogen bzw. der Kreis liegt.
Dementsprechend werden in den Argumenten zunächst die xund y-Werte des Rechtecks angegeben, dann die Koordinaten
der rechten unteren Ecke des Rechtecks, anschließend die
Gradzahl, bei der negative oder positive Werte erlaubt sind, und
der Stil, der das Aussehen des Kreisbogens festlegt. Für
nebenstehendes Bild hieße dies:
z_flaeche.create_arc(100,100,500,500,extent=359,
style=ARC,width=3)
z_flaeche.create_rectangle(100,100,500,500,width=3)
24
Mögliche Stile der Kreisbögen sind
ARC – Kreisbogen
CHORD – Kreisbogen mit Sehne
PIESLICE - Tortenstück
Kreise und Kreisbögen mit den Stilen PIESLICE und CHORD können gefüllt und mit einer Linienfarbe
versehen werden:
z_flaeche.create_arc(100,100,500,500,extent=359,
style=PIESLICE,width=3, outline='red',fill='blue')
Hinweis
Hier wird offensichtlich, was aufmerksamen Lesern bereits aufgefallen ist: Kreise mit
360° werden als 0°-Kreise interpretiert. Entweder man beschränkt sich auf 359° oder
man zeichnet zwei Kreisbögen mit je 180° und demselben Mittelpunkt, einen in
positive, einen in negative Richtung. Die Richtung wird über eine positive oder
negative Gradzahl festgelegt.
2.4.4. Dreiecke – Vierecke – Polygone
Im Prinzip sind dies für tkinter immer die gleichen Elemente, nur mit verschiedenen Argumenten. Für
Rechtecke gibt es zwar den Sonderfall create_rectangle(x1,y1,x2,y2), der aber eigentlich nur der
Erleichterung für Programmierer dient.
Polygone werden über ihre Eckpunkte definiert. Also ein Dreieck:
z_flaeche.create_polygon(50,50,300,50,300,300,\
fill='green', outline='Red')
Und ein Rechteck:
z_flaeche.create_polygon(50,50,300,50,300,300,50,300,\
fill='green', outline='Red')
Und ein Fünfeck:
z_flaeche.create_polygon(100,50,200,50,300,300,250,400,50,300,\
fill='green', outline='Red')
und so weiter.
2.4.5. Texte
Texte lassen sich ebenso einbinden. Der erste Parameter gibt dabei die Position des umschließenden
Rechtecks wieder. Dann folgt der Text, die Fülldfarbe und die Angaben zur Schrift, also Schriftart,
Schriftgröße und Schriftstil.
z_flaeche.create_text(300,200,text='Kaa ist auch ein Python!',\
fill='red', font=('Arial',30,'bold'))
Schriftstile:
bold – fett
italic - kursiv
25
underline – unterstrichen
overstrike – durchgestrichen
Aufgabe:
Generiere ein Gesicht. Gib dem Gesicht einen Namen.
(zeichnen_07.py)
2.4.6. Bewegung
Bewegung ist auch mit Python möglich. Über die Methode move(id,x,y) kann ein Objekt in jede
beliebige Richtung bewegt werden. Die id wird dabei in der Reihenfolge festgelegt, in der die
Zeichnungen erstellt werden, beginnend mit 1.
z_flaeche.create_polygon(10,10,10,60,50,35)
z_flaeche.create_polygon(30,80,30,120,40,85)
for x in range (0,60):
z_flaeche.move(1,5,0)
z_flaeche.update()
time.sleep(0.5)
for x in range (0,60):
z_flaeche.move(2,5,0)
z_flaeche.update()
time.sleep(0.5)
In diesem Skript werden nach den hier weggelassenen Standardelementen, also Fenster und EndeButton, zwei Polygone erstellt, die dann nacheinander von links nach rechts bewegt werden. Welches
Polygon als erstes bewegt wird, hängt von der Reihenfolge im Quelltext ab.
Der Befehl time.sleep(0.5) sorgt dafür, dass die Bewegung immer wieder unterbrochen wird,
denn sonst wäre nur das Ergebnis des Programmverlaufs zu sehen.
26
2.4.7. Der springende Punkt.
Etwas Anderes ist es, wenn der Benutzer eingreifen soll. Dafür werden sog. Events genutzt. Ein Event,
also ein Ereignis, kann ein Mausklick sein oder ein Tastendruck.
Eventtypen sind z. B.:
Button
ButtonRelease
MouseWheel
Enter
KeyPress
KeyRelease
Leave
Maustaste gedrückt
Maustaste losgelassen
Mausrad bewegt
Mauscursor in sichtbarem Bereich des Objekts
Tastendruck
Taste losgelassen
Maustaste wird aus dem Objekt herausgezogen
Eine kleine Auswahl an Tastatur-Attribute der Eventtypen.
keysym
BackSpac
Control_
Delete
Down
End
Escape
Fl
F2
Left
Next
Return
Right
space
Up
Tipp
keysym_num
65288
65507
65535
65364
65367
65307
65470
65471
65361
65366
65293
65363
32
65362
Das Tastensymbol (keysym) ist ein String, die
Tastensymbolnummer eine ganze Zahl.
Das folgende Skript zeigt Tastensymbole und –nummern der betätigten Tasten an:
from tkinter import *
def out(event):
print(event.keysym, " \ t", event.keysym_num)
window = Tk( )
window. bind_all("<Any-KeyPress>" , out)
window.mainloop( )
(M. Weigend: Python GE-PACKT, S, 548)
Events können modifiziert werden, z.B.:
Modifizierer
Erklärung
Alt
Alt-Taste gedrückt
Any
Generalisierung (Any-Key-Press = irgendeine Taste gedrückt)
Double
zwei Ereignisse kurz hintereinander
Double-Button-3 = Doppelklick mit rechter Maustaste
Triple
drei Ereignisse kurz hintereinander
Control
Strg-Taste gedrückt
Shift
Umschalttaste gedrückt
27
Das Ereignis muss zunächst in einer Funktion mit dem Argument event erstellt werden.
def springpunkt(event):
if event.keysym == 'Up':
z_flaeche.move(1,0,-5)
elif event.keysym == 'Down':
z_flaeche.move(1,0,5)
elif event.keysym == 'Left':
z_flaeche.move(1,-5,0)
else:
z_flaeche.move(1,5,0)
Hier wird die Event-Funktion erstellt und dann mit einer Abfolge von Bedingungen geprüft, welche
Taste gedrückt wurde.
Anschließend muss das Ereignis noch aufgerufen werden. Über die Methode bind_all werden die
Ereignisse für alle Objekte der Applikation an den Eventhandler, also hier einen Tastendruck,
gebunden.
In der Klammer folgt der sog. Event-Pattern. Er ist folgendermaßen aufgebaut:
<[Modifizierer-] Typ [-Qualifizierer]>
Double-Keypress-Return
In diesem Beispiel würde auf ein Doppeltes Drücken der Enter-Taste gewartet.
Um den Punkt in Bewegung zu bringen, ergibt sich folgender Quelltext:
z_flaeche.bind_all('<KeyPress-Up>',springpunkt)
z_flaeche.bind_all('<KeyPress-Down>',springpunkt)
z_flaeche.bind_all('<KeyPress-Left>',springpunkt)
z_flaeche.bind_all('<KeyPress-Right>',springpunkt)
Am Ende der Bindung wird die Funktion aufgerufen, die bei Eintreten des Ereignisses erfolgen soll.
Aufgaben
1. Erstelle das Programm mit dem springenden Punkt.
2. Integriere für ein weiteres Objekt eine Steuerung mit Buchstabentasten.
28
3. Ein Spiel
3.1. Basics
Nun soll ein ganz einfaches Spiel erzeugt werden, dass einen Ball auf einer Fläche hüpfen lässt.
Hierfür sind einige Vorarbeiten nötig.
3.1.1. Leinwand
Zunächst wird die Leinwand erzeugt. Dabei werden neben Tkinter die Module time und random
importiert, die im Verlauf des Spiels noch gebraucht werden. Zusätzlich erhält das Fenster
Festlegungen zur Gestaltung des Rands.
from tkinter import *
import random
import time
fenster=Tk()
fenster.title('PingPong')
fenster.resizable(0,0)
#Fenster nicht veränderlich
fenster.wm_attributes('-topmost',1) #Fenster immer ganz vorn
z_flaeche=Canvas(fenster,width=500,height=400, \
bd=0,highlightthickness=0)
#Zeichenfläche ohne Rahmen
z_flaeche.pack()
fenster.update()
3.1.2. Der Ball
Der Ball wird zunächst als Klasse erstellt. Über class Klassenname wird ein Name vergeben,
dann folgt der Inhalt der Klassen. In einer Klasse werden Methoden definiert, die ein Objekt dieser
Klasse haben kann. Diese Definition erfolgt in Form von Funktionen innerhalb der Klasse.
class Spielball:
#Die Klasse für den Spielball
def __init__(self,z_flaeche,color): #Initialisierung mit
#den dazugehörigen Parametern des Balls
self.z_flaeche=z_flaeche
self.ball_id=z_flaeche.create_oval(10,10,25,25,fill=color)
#Id des gezeichneten Objekts erfassen
self.z_flaeche.move(self.ball_id,245,100)
#Objekt mit Hilfe der ID bewegen
def bewegen(self):
pass
#leeres Schlüsselwort,
#Platzhalter für spätere Eingaben
29
Achtung
Die init-Anweisung hat je zwei Unterstriche vor und nach init.
Damit das Programm startet, also den Ball zeigt und sich immer wieder aktualisiert, wird eine sog.
Hauptschleife eingefügt. Sie kontrolliert das Programm.
while 1:
fenster.update_idletasks()
# Alle anstehenden Events abarbeiten
fenster.update()
time.sleep(0.01)
#Bildaufbau für 1 hundertstel Sekunde pausieren lassen
3.2. Bewegung für den Ball
3.2.1. Einfache Bewegung
Um den Ball zu bewegen wird zunächst die bisher leere Methode bewegen mit Inhalt gefüllt:
def bewegen(self):
self.z_flaeche.move(self.ball_id,0,-1)
#den Ball nach oben schweben lassen.
Damit diese Bewegung auch ausgeführt wird, muss das Objekt mit dieser Methode aufgerufen
werden. Dazu wird die Hauptschleife entsprechend ergänzt:
while 1:
ball.bewegen()
#Schweben starten
fenster.update_idletasks()
# Alle anstehenden Events abarbeiten
fenster.update()
time.sleep(0.01)
#Bildaufbau für 1 hundertstel Sekunde pausieren lassen
30
3.2.2. Hin und wieder zurück
Damit der Ball die Fläche nicht verlässt, müssen die x – und y - Position des Balls ausgelesen und die
Größe der Leinwand erfasst werden, um dann zu erfassen, ob sich der Ball noch innerhalb des
Spielfeldes bewegt.
Der Klasse Spielball werden deshalb für diese drei Werte drei Variablen hinzugefügt.
self.x=0
self.y=-1
self.z_flaeche_height=self.z_flaeche.winfo_height()
# Höhe der Fläche über winfo_height auslesen und
#in eine Variable legen
In die Bewegen-Methode werden nun Bedingungen eingefügt, die die Position des Balls erfassen.
Außerdem wird die Positionierung des Balls auf die beiden o.g. Variablen umgestellt.
def bewegen(self):
self.z_flaeche.move(self.ball_id,self.x,self.y)
#den Ball nach oben schweben lassen.
pos=self.z_flaeche.coords(self.ball_id)
#Ball-Position über coords erfassen,
#gibt x und y - Wert als Liste mit 4 Werten zurück
if pos[1]<=0.0:
self.y=1
if pos[3]>= self.z_flaeche_height:
self.y=-1
print(self.z_flaeche.coords(self.ball_id))
#Position des Balls in der Konsole anzeigen
Die Print-Funktion an dieser Stelle dient der Kontrolle. Sie gibt in der Konsole die Werte aus, die mit
der coords-Funktion ausgelesen wurden.
3.2.3. Irgendwo hin
Nun soll der Ball variabel werden. Dafür binden wir einen kleinen Zufall ein. Zunächst wird eine
Variable erstellt, die sechs Werte in einer Liste enthält. Dann wird die Abfolge der Listenelemente
durcheinandergewirbelt. Der x-Wert des Balls erhält den ersten Wert in der starts-Variablen.
starts=[-3,-2,-1,1,2,3]
# Variable starts enthält eine Liste
random.shuffle(starts)
#der Inhalt von starts wird gemischt
self.x=starts[0]
# x erhält den ersten Wert in der Liste,
#die starts enthält
self.y=--3
#Ball wird schneller
Nun brauchen wir aber auch die Breite der Leinwand, also
31
self.z_flaeche_width=self.z_flaeche.winfo_width()
Außerdem muss die bewegen-Funktion um die Bedingungen für die Breite ergänzt werden.
if pos[0]<=0:
self.x=3
if pos[2] >= self.z_flaeche_width:
self.x=-3
3.2.4. Ein Prallbrett
Nun muss der Ball im Spielfeld gehalten werden und wenn er unten
auftrifft, soll der Spieler verlieren.
Zunächst benötigen wir also ein Prallbrett, von dem der Ball abprallen
kann. Es wird ebenso erstellt wie der Kreis, nur natürlich als rectangle.
class prallbrett:
def __init__(self,z_flaeche,color):
self.z_flaeche=z_flaeche
self.prall_id=z_flaeche.create_rectangle(0,0,100,10,fill=color)
self.z_flaeche.move(self.prall_id,200,300)
def bewegen(self):
# keine Namenskollision, da einer andern
#Klasse zugeordnet
Pass
#leerer Platzhalter
Nun muss das Prallbrett als Objekt erstellt und in die Hauptschleife integriert werden:
prallbr=prallbrett(z_flaeche,'blue')
while 1:
ball.bewegen()
prallbr.bewegen()
Jetzt sieht man das Brett.
3.2.5. Bewegung mit den Maustasten
Über Tastaturereignisse wird nun das Prallbrett bewegt. Da sich das Brett nur innerhalb der
Leinwand bewegen darf, muss auch die Leinwandbreite erfasst werden. Dieser Wert muss auch in
dieser Klasse wieder erfasst werden, da eine Übergabe des Werts aus einer andern Klasse nicht
möglich ist.
class Prallbrett:
def __init__(self,z_flaeche,color):
self.z_flaeche=z_flaeche
32
self.prall_id=z_flaeche.create_rectangle(0,0,100,10,fill=color)
self.z_flaeche.move(self.prall_id,200,300)
self.x=0
self.z_flaeche_width=self.z_flaeche.winfo_width()
self.z_flaeche.bind_all('<KeyPress-\
Left>',self.nach_links)
self.z_flaeche.bind_all('<KeyPress-\
Right>',self.nach_rechts)
def bewegen(self):
# keine Namenskollision, da einer andern Klasse zugeordnet
self.z_flaeche.move(self.prall_id,self.x,0)
pos=self.z_flaeche.coords(self.prall_id)
if pos[0]<=0:
self.x=1
if pos[2]>= self.z_flaeche_width:
self.x=0
def nach_links(self,event):
self.x=-2
def nach_rechts(self,event):
self.x=2
Die Methoden nach_links und nach_rechts werden die x-Werte des Prallbretts verändern.
Die Bewegung erfolgt analog der des Balls, aber nur in der x-Achse. Die Y-Werte werden
grundsätzlich auf null gesetzt.
Nun sollten Ball und Prellbrett über die Fläche pendeln.
3.2.6. Treffer feststellen
Damit das Prallbrett vom Ball erfasst wird, muss es als Parameter in die Initialisierung der Klasse
Spielball geschrieben werden. Außerdem muss das Prallbrett als Objektvariable gespeichert werden.
Die ersten Zeilen der Klasse Spielball sehen dann so aus:
class Spielball:
#Die Klasse für den Spielball
def __init__(self,z_flaeche,prallbrett,color):
#Initialisierung mit den dazugehörigen Parametern des Balls
self.z_flaeche=z_flaeche
self.prallbrett=prallbrett:
Nun muss erfasst werden, ob denn der Ball das Brett getroffen hat.
33
Der Quellcode sieht so aus:
def treffer(self,pos):
# der Parameter pos macht die Variable
#aus der bewegen-Methode nutzbar
prallbrett_pos=self.z_flaeche.coords(self.prallbrett.prall_id)
if pos[2] >=prallbrett_pos[0] and pos[0] <=
prallbrett_pos[2]:
if pos[3] >= prallbrett_pos[1] and pos[3] <=
prallbrett_pos[3]:
return True
return False
Zunächst wird die Methode definiert. Der Parameter pos macht die Variable aus der bewegenMethode hier nutzbar.
Dann wird die Position des Prallbretts erfasst.
Die If-Bedingung ist zunächst etwas verwirrend.
Zur Erklärung:
Der Ball hat vier Werte, die seine Position beschreiben
pos[0]
x-Wert links oben
pos[1]
y-Wert links oben
pos[2]
x-Wert rechts unten
pos[3]
y-Wert rechts unten
Das Prallbrett hat diese vier Werte auch:
prallbrett_pos[0]
prallbrett_pos[1]
prallbrett_pos[2]
prallbrett_pos[3]
x-Wert links oben
y-Wert links oben
x-Wert rechts unten
y-Wert rechts unten
34
In der Bedingungsschleife wird zunächst der Vergleich der x-Positionen vorgenommen. Dann wird der
Vergleich der y-Positionen vorgenommen, aber nur, wenn die Bedingungen der umschließenden
Bedingung wahr sind. Dass hier Ober- und Unterseite des Balls und des Prallbretts mit and
verkoppelt geprüft werden, liegt daran, dass bei der Bewegung in Dreierschritten, die das Programm
vorsieht, das Prallbrett durchschritten sein könnte, bevor dies vom Programm bemerkt wird.
Nun kann diese Methode in der bewegen – Methode aufgerufen werden. Dies geschieht, nachdem
die y-Werte in der bewegen-Methode geprüft wurden. Die Änderung der y-Variablen i einen
negativen Wert bewirkt, dass der Ball die Flugrichtung umdreht.
def bewegen(self):
self.z_flaeche.move(self.ball_id,self.x,self.y) #den
Ball nach oben schweben lassen.
pos=self.z_flaeche.coords(self.ball_id)
#Ball-Position über coords erfassen, gibt x und y Wert als Liste mit 4 Werten zurück
if pos[1]<=0.0:
35
self.y=1
if pos[3]>= self.z_flaeche_height:
self.y=-1
if self.treffer(pos) == True:
self.y=-3
if pos[0]<=0:
self.x=3
if pos[2] >= self.z_flaeche_width:
self.x=-3
3.2.7 Quelltext
Hier der gesamte Quelltext ohne Kommentare:
from tkinter import *
import random
import time
class Spielball:
def __init__(self,z_flaeche,prallbrett,color):
self.z_flaeche=z_flaeche
self.prallbrett=prallbrett
self.ball_id=z_flaeche.create_oval(10,10,25,25,fill=color)
self.z_flaeche.move(self.ball_id,245,100)
starts=[-3,-2,-1,1,2,3]
random.shuffle(starts)
self.x=starts[0]
self.y=--3
self.z_flaeche_height=self.z_flaeche.winfo_height()
self.z_flaeche_width=self.z_flaeche.winfo_width()
def bewegen(self):
self.z_flaeche.move(self.ball_id,self.x,self.y)
pos=self.z_flaeche.coords(self.ball_id)
if pos[1]<=0.0:
self.y=1
if pos[3]>= self.z_flaeche_height:
self.y=-1
if self.treffer(pos) == True:
self.y=-3
if pos[0]<=0:
self.x=3
if pos[2] >= self.z_flaeche_width:
self.x=-3
print(self.z_flaeche.coords(self.ball_id))
def treffer(self,pos):
prallbrett_pos=self.z_flaeche.coords(self.prallbrett.prall_id)
36
if pos[2] >=prallbrett_pos[0] and pos[0] <=
prallbrett_pos[2]:
if pos[3] >= prallbrett_pos[1] and pos[3] <=
prallbrett_pos[3]:
return True
return False
class prallbrett:
def __init__(self,z_flaeche,color):
self.z_flaeche=z_flaeche
self.prall_id=z_flaeche.create_rectangle(0,0,100,10,fill=color)
self.z_flaeche.move(self.prall_id,200,300)
self.x=0
self.z_flaeche_width=self.z_flaeche.winfo_width()
self.z_flaeche.bind_all('<KeyPressLeft>',self.nach_links)
self.z_flaeche.bind_all('<KeyPressRight>',self.nach_rechts)
def bewegen(self):
self.z_flaeche.move(self.prall_id,self.x,0)
pos=self.z_flaeche.coords(self.prall_id)
if pos[0]<=0:
self.x=1
if pos[2]>= self.z_flaeche_width:
self.x=0
def nach_links(self,event):
self.x=-4
def nach_rechts(self,event):
self.x=4
fenster=Tk()
fenster.title('PingPong')
fenster.resizable(0,0)
fenster.wm_attributes('-topmost',1)
z_flaeche=Canvas(fenster,width=500,height=400,
bd=0,highlightthickness=0)
z_flaeche.pack()
fenster.update()
prallbrett=prallbrett(z_flaeche,'blue')
ball=Spielball(z_flaeche,prallbrett,'red')
while 1:
ball.bewegen()
prallbrett.bewegen()
fenster.update_idletasks()
fenster.update()
time.sleep(0.01)
37
6. Literatur
J. Ernesti / P. Kaiser: Python 3. Das umfassende Handbuch. Galileo Computing, Bonn 2012, 3. Aufl.
T. Theis: Einstieg in Python. Galileo Computing, Bonn 2014, 4. Aufl.
M. Weigend: Python 3. Mitp, Heidelberg 2013, 5. Aufl.
M. Weigend: Python GE-PACKT. Mitp, Heidelberg 2013, 5. Aufl.
B. Klein: Einführung in Python. Hanser Verlag, München 2013
J. Briggs: Python kinderleicht. dpunkt.verlag, Heidelberg 2013
G. Lingl: Python für Kids. Bhv, Heidelberg, 2010, 4. Auflage
W. u. C. Sande: Hello World. Hanser Verlag, München 2014
38
Herunterladen