Event-Bearbeitung in Pygame
Event ist der engliche Begriff für Ereignis. Jedes Pygame-Programm hat eine interne Schleife, die ständig prüft, ob ein Event aufgetreten ist – also zum Beispiel, ob eine Taste gedrückt, die Maus bewegt oder eine Maustaste betätigt wurde.
Alle eingetretenen Events werden von Pygame automatisch in einer Liste gespeichert. Jedes Event enthält neben dem Typ des Events (z. B. MOUSEBUTTONDOWN, MOUSEBUTTONUP, MOUSEMOTION, KEYDOWN) auch zusätzliche Informationen – etwa die Position der Maus, welche Maustaste gedrückt wurde oder welche Taste der Tastatur gedrückt wurde.
Mit dem Befehl pygame.event.get() kann man die Liste der aktuellen Events abrufen. Um diese zu bearbeiten, wird oft eine Funktion wie handle_events()) verwendet, die in einer Schleife alle Events einzeln verarbeitet. Als erstes Beispiel hast du in der Einführung schon kennengelernt, wie durch Betätigen der rechten Maustaste ein Pygame-Programm beendet wird.
Programm 7
import pygame
WIDTH = 650
HEIGHT = 450
BLUE = (0, 0, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
LIGHTGREY = (240, 240, 240)
def init():
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
screen.fill(LIGHTGREY)
pygame.display.flip()
return screen
def handle_events():
global screen
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 3: # Rechte Maustaste
return False
elif event.button == 1:
pygame.draw.circle(screen, RED, event.pos, 20)
return True
return True
def main():
global screen
clock = pygame.time.Clock()
screen = init()
running = True
while running:
running = handle_events()
pygame.display.flip()
clock.tick(60)
pygame.quit()
main()
Erläuterung
Wir erläutern die Funktion handle_events beginnend in Zeile 19. zeilenweise.
| Zeile | Erläuterung |
|---|---|
| 20 | Die Variable screen, die die Zeichenfläche darstellt, wird hier als global deklariert, damit sie auch innerhalb der Funktion handle_events() verwendet werden kann. Alternativ hätte man sie als Parameter übergeben können. |
| 21 | Mit pygame.event.get() wird die Liste aller Ereignisse (Events) abgefragt, die seit dem letzten Aufruf aufgetreten sind. Diese werden in einer Schleife einzeln durchlaufen. |
| 22 | Es wird geprüft, ob es sich bei dem aktuellen Event um ein Maustastendruck-Ereignis (MOUSEBUTTONDOWN) handelt. |
| 23 - 24 | event.button gibt an, welche Maustaste gedrückt wurde. Die rechte Taste hat den Wert 3, die linke den Wert 1. Wird rechts geklickt, wird das Programm beendet (False wird zurückgegeben). |
| 25 - 26 | Bei einem linken Mausklick wird ein Kreis an der aktuellen Mausposition gezeichnet. Die Koordinaten des Mausklicks erhält man über event.pos. Danach wird True zurückgegeben, damit das Programm weiterläuft. |
|
Teste das Programm in Webtigerpython. Was ändert sich, wenn du MOUSEBUTTONDOWN durch MOUSEBUTTONUP ersetzt? |
Farbwechsel bei Klick auf Kreis
Jetzt entwickeln wir ein Programm, das bei einem Klick auf einen Kreis dessen Farbe zufällig ändert. Damit das funktioniert, müssen wir wissen, ob die Maus innerhalb des Kreises geklickt wurde. Dafür speichern wir die Daten des Kreises wieder in einem Dictionary. Dazu benötigen wir einige Hilfsfunktionen, die wir zum großen Teil schon besprochen haben.
def get_circle_dict(x, y, r, col, dx, dy):
return {"x":x, "y":y, "radius": r, "color":col,"dx":dx, "dy":dy}
def draw_circle(screen,c):
pygame.draw.circle(screen, c["color"], ( c["x"],c["y"]), c["radius"])
def get_random_color():
return (random.randint(0,255),random.randint(0,255),random.randint(0,255))
Die Funktionen get_circle_dict und draw_circle sind bereits bekannt. Die Funktion get_random_color liefert eine Zufallsfarbe, indem die drei RGB_Werte zufällig aus dem Bereich von 0 bis 255 bestimmt werden. In der main-Funktion wird durch Aufruf der Funktion get_circle_dict ein Kreis erzeugt und in der Programm-Schleife gezeichnet. Diesmal übergeben wir die Variablen screen und circle an die Parameter der Funktion handle_events.
def main():
clock = pygame.time.Clock()
screen = init()
running = True
circle = get_circle_dict(300, 200, 50, get_random_color(),0,0)
while running:
screen.fill(LIGHTGREY)
draw_circle(screen, circle)
pygame.display.flip()
running = handle_events(screen, circle)
clock.tick(60)
pygame.quit()
Fehlt nur noch die Weiterentwicklung von handle_events. Die Funktion hat die Aufgabe, die Farbe des Kreises zu ändern, wenn die Mausposition beim Klick innerhalb des Kreises war. Um das zu prüfen, schreiben wir die Funktion contains(c, pos), die True zurückliefert, wenn die Mausposition pos innerhalb des Kreises c liegt. Das ist der Fall, wenn der Abstand des Mittelpunktes des Kreises von der Mausposition kleiner als der Radius des Kreises ist. Wir berechnen den Abstand mithilfe der Funktion hypot(d1, d2) aus der Bibliothek math. Wenn die Parameter d1, d2 die Differenzen der x- und y-Koordinaten zweier Punkte sind, berechnet die Funktion den Abstand der beiden Punkte. Wenn man die Differenzen als Länge der Katheten eines rechtwinkligen Dreiecks betrachtet, berechnet die Funktion die Länge der Hypotenuse des rechtwinkligen Dreiecks, die dem Abstand der beiden Punkte entspricht. Damit ergibt sich folgende Funktion:
def contains(c, pos):
if math.hypot(pos[0] -c["x"], pos[1] - c["y"]) < c["radius"]:
return True
return False
|
Implementiere mithilfe der entwickelten Funktionen das vollständige Programm und teste es. Beachte, welche Bibliotheken du importieren musst. |
import pygame
import random
import math
WIDTH = 650
HEIGHT = 450
BLUE = (0, 0, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
LIGHTGREY = (240, 240, 240)
def init():
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
screen.fill(LIGHTGREY)
pygame.display.flip()
return screen
def get_circle_dict(x, y, r, col, dx, dy):
return {"x":x, "y":y, "radius": r, "color":col,"dx":dx, "dy":dy}
def draw_circle(screen,c):
pygame.draw.circle(screen, c["color"], ( c["x"],c["y"]), c["radius"])
def get_random_color():
return (random.randint(0,255),random.randint(0,255),random.randint(0,255))
def contains(c, pos):
if math.hypot(pos[0] -c["x"], pos[1] - c["y"]) < c["radius"]:
return True
return False
def handle_events(screen, circle):
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 3: # Rechte Maustaste
return False
elif event.button == 1:
if contains(circle,event.pos):
circle["color"]=get_random_color()
return True
return True
def main():
clock = pygame.time.Clock()
screen = init()
running = True
circle = get_circle_dict(300, 200, 50, get_random_color(),0,0)
while running:
screen.fill(LIGHTGREY)
draw_circle(screen, circle)
pygame.display.flip()
running = handle_events(screen, circle)
clock.tick(60)
pygame.quit()
main()
Kreise mit Klick entfernen
Jetzt soll ein Programm entwickelt werden, das auf der Zeichenfläche 10 Zufallskreise erzeugt, die nacheinander per Mausklick gelöscht werden können. Die Kreise erzeugen wir wieder mit einer leicht veränderten Funktion get_random_circle_list(n), die du bereits in Programm 6 kennengelernt hast..
def get_random_circle_list(n):
circle_list = []
for _ in range(n):
radius = random.randint(10, 20)
x = random.randint(radius, WIDTH - radius)
y = random.randint(radius, HEIGHT - radius)
dx =0
dy = 0
col = (random.randint(0, 255),random.randint(0, 255),random.randint(0, 255))
circle_list.append(get_circle_dict(x, y, radius, col, dx, dy ))
return circle_list
Im Gegensatz zu Programm 6 haben wir die Bewegunngsdistanzen dx und dy auf 0 gesetzt, da sich die Kreise in diesem Programm nicht bewegen sollen.
In der main-Funktion des letzten Beispiels müssen wir die Anweisung, die das Circle-Dictionary erzeugt durch circlelist = get_random_circle_list(10) eresetzen und den Aufruf draw_circle durch eine Schleife, die die circle_list durchläuft und jeden der Kreise zeichnet. An die Paramter der Funktion handle_events-Funktion übergeben wir die Variablen screen und circle_list . Damit ergibt sich folgende main-Funktion:
def main():
clock = pygame.time.Clock()
screen = init()
running = True
circle_list = get_random_circle_list(10)
while running:
screen.fill(LIGHTGREY)
for circle in circle_list:
draw_circle(screen, circle)
running, circle_list = handle_events(screen, circle_list)
pygame.display.flip()
clock.tick(60)
pygame.quit()
Um die Kreise per Mausklick entfernen zu können, müssen wir die Funktion handle_events überarbeiten. Wir übergeben screen und die Liste circle_list der Kreise als Parameter an die Funktion. Bei einem Klick mit der linken Maustaste durchlaufen wir die Liste der Kreise und entfernen den Kreis aus der Liste, auf den geklickt wurde. Dazu benötigen wir wieder die im letzten Beispiel entwickelte Funktoin contains(c, p). Da sich die Kreisliste in handle_events verändert, übergeben wird diese wieder zurück an die main-Funktion. Damit ergeben sich folgende Funktionen.
def contains(c, pos):
if math.hypot(pos[0] -c["x"], pos[1] - c["y"]) < c["radius"]:
return True
return False
def handle_events(cl, screen):
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 3: # Rechte Maustaste
return False, cl
elif event.button == 1:
for circle in cl
if contains(circle, event.pos):
c.remove(circle)
return True, cl
return True, cl
|
Implementiere mithilfe der entwickelten Funktionen das vollständige Programm und teste es. Acht auf den Import der richtigen Bibliotheken. |
import pygame
import random
import math
WIDTH = 650
HEIGHT = 450
BLUE = (0, 0, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
LIGHTGREY = (240, 240, 240)
def init():
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
screen.fill(LIGHTGREY)
pygame.display.flip()
return screen
def random_color():
return (random.randint(0,255),random.randint(0,255),random.randint(0,255))
def get_circle_dict(x, y, r, col, dx, dy):
return {"x":x, "y":y, "radius": r, "color":col,"dx":dx, "dy":dy}
def draw_circle(screen,c):
pygame.draw.circle(screen, c["color"], ( c["x"],c["y"]), c["radius"])
def get_random_circle_list(n):
circle_list = []
for _ in range(n):
radius = random.randint(10, 20)
x = random.randint(radius, WIDTH - radius)
y = random.randint(radius, HEIGHT - radius)
dx = random.randint(-2,2)
if dx == 0:
dx = dx + 1
dy = random.randint(-2,2)
if dy == 0:
dy = dy - 1
col = (random.randint(0, 255),random.randint(0, 255),random.randint(0, 255))
circle_list.append(get_circle_dict(x, y, radius, col, dx, dy ))
return circle_list
def contains(c, pos):
if math.hypot(pos[0] -c["x"], pos[1] - c["y"]) < c["radius"]:
return True
return False
def handle_events(screen, cl):
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 3: # Rechte Maustaste
return False
elif event.button == 1: #linke Maustaste
for circle in cl:
if contains(circle, event.pos):
cl.remove(circle)
return True, cl
return True, cl
def main():
clock = pygame.time.Clock()
screen = init()
running = True
circle_list = get_random_circle_list(10)
while running:
screen.fill(LIGHTGREY)
for circle in circle_list:
draw_circle(screen, circle)
pygame.display.flip()
running, circle_list = handle_events(screen, circle_list)
clock.tick(60)
pygame.quit()
main()
|
Bearbeite von Aufgaben 2 nun die erste Aufgabe |
Kreis verschieben
In diesem Abschnitt beschäftigen wir uns mit mit dem Mousevent MOUSEMOTION, das ausgelöst wird, wenn die Maus bewegt wird, egal ob eine Maustaste gedrückt ist oder nicht.. Im ersten Beispiel erläutern wir ein Programm, mit dem ein Kreis im Grafikfenster mit der Maus bewegt werden kann, wenn die linke Maustaste gedrückt ist.
Programm 10
import pygame
WIDTH = 650
HEIGHT = 450
WHITE = (255, 255, 255)
BLACK = (0, 0, 0, 0)
RED = (255, 0, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
LIGHTGREY = (240, 240, 240)
def init():
pygame.init()
screen = pygame.display.set_mode((600, 400))
screen.fill(LIGHTGREY)
return screen
def get_circle_dict(x, y, r, col, dx, dy):
return {"x":x, "y":y, "radius": r, "color":col,"dx":dx, "dy":dy}
def draw_circle(screen,c):
pygame.draw.circle(screen, c["color"], ( c["x"],c["y"]), c["radius"])
def handle_events(circle, dragging):
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
dragging = True
elif event.button == 3:
return False, dragging, circle
elif event.type == pygame.MOUSEBUTTONUP:
if event.button == 1:
dragging = False
elif event.type == pygame.MOUSEMOTION and dragging:
circle["x"], circle["y"] = event.pos
return True, dragging, circle
def main():
screen = init()
clock = pygame.time.Clock()
circle = get_circle_dict(320, 240, 30, BLUE, 0, 0)
running = True
dragging = False
while running:
screen.fill(LIGHTGREY)
draw_circle(screen, circle)
pygame.display.flip()
running, dragging, circle = handle_events(circle, dragging)
clock.tick(60)
main()
Erläuterung
In der main-Funktion erzeugen wir wieder ein Circle-Dictionary, das in der Programm-Schleife immer wieder in das gelöschte Grafikfenster gezeichnet wird. Zusätzlich haben wir eine Variable dragging die die Werte True und False annehmen kann. Wenn dragging True ist, befindet sich die Maus im "Ziehmodus", d.h. die Maustaste ist gedrückt und bei einer Bewegung wird der Kreis im Grafikfenster so bewegt, dass der Mittelpunkt auf die aktuelle Mausposition verschoben wird. Dragging wir in der Funktion handle-events gesetzt und von ihr zurückgegeben. In der folgenden Tabelle erläutern wir handle_events zeilenweise.
| Zeile | Erläuterung |
|---|---|
| 24 | Die Event-Handler-Funktion wird mit zwei Parametern: circle (Kreis-Objekt) und dragging (Drag-Status) deklariert. dragging ist True, wenn die linke Maustaste gedrückt ist. |
| 25 | Mit pygame.event.get() wird die Liste aller Ereignisse (Events) abgefragt, die seit dem letzten Aufruf aufgetreten sind. Diese werden in einer Schleife einzeln durchlaufen. |
| 26 | Es wird geprüft, ob es sich bei dem aktuellen Event um ein Maustastendruck-Ereignis (MOUSEBUTTONDOWN) handelt. |
| 27 | Es wird geprüft, ob die linke Maustaste gedrückt wurde (Button 1 = links, 2 = mittel, 3 = rechts) |
| 28 | dragging wir auf True gesetzt - der Kreis kann jetzt mit der Maus bewegt werden |
| 29 | Prüft, ob die rechte Maustaste gedrückt wurde. |
| 30 | Die Funktion wird beendet mit der Rückgabe von False, damit das Programm beendet wird. Die Rückgabe von dragging und circle spielt für den Programmablauf keine Rolle, muss aber erfolgen, da in main drei Rückgabewerte erwartet werden. |
| 31 | Es wird geprüft, ob eine Maustaste losgelassen wurde |
| 32 | dragging wird auf False gesetzt, wenn die linke Maustaste losgelassen wird |
| 33 | Prüft, ob sich die Maus bewegt UND dragging den Wert True hat ist |
| 34 | Setzt die Position des Kreises auf die aktuelle Mausposition (event.pos ist ein (x,y)-Tupel) |
| 35 | Gibt drei Werte zurück, weil sie u.U verändert wurden:True (Programm soll fortgesetzt werden) ,dragging (aktueller Drag-Status) und circle evtl. mit neuen Koordinaten. |
|
Bearbeite von Aufgaben 2 nun die Aufgaben 2 und 3 |
Malen mit der Maus
Als zweite Anwendung für das MOUSEMOTION-Event entwickeln wir ein Programm, mit dem man mit der Maus oder mit dem Finger auf dem Tablet malen kann. Bevor wir das Programm zeigen, müssen wir noch erklären, was das "Nichts" in Python ist.
None ist ein spezieller Wert in Python, der bedeutet: "Hier ist nichts" oder "Dieser Platz ist leer". Man verwendet ihn z.B., wenn man eine Variable deklarieren will, ohne ihr sofort einen Wert zuzuweisen. Mit if Variablenname: liefert True, wenn die Variable einen Wert hat und False, wenn die Variable noch keinen Wert hat also auf None gssetzt wurde. Funktionen können den Wert None zurückgeben.
Beispiele
zahl = None
if !zahl:
print("Die zahl hat noch keinen Wert")
else:
print("Die Zahlen hat einen Wert")
def suche_schluessel():
# Schauen wir in der Tasche nach...
if schluessel_gefunden:
return"Schlüssel"
else
return None # "Nichts gefunden"
Das nebenstehende Video zeigt ein (zugegeben sehr einfaches) Beispiel für die Ausführung des Malprogramms.
Der Algorithmus läuft nach folgendem Prinzip ab. Sobald die linke Maustaste gedrückt wird, wird in der Funktion handle_events die Mausposition in einer Variablen from_pos gespeichert und die Varaible dragging auf True gesetzt. Bei jeder Mausbewegung wird die Mausposition in der Variablen to_pos gespeichert. Der Drag-Status sowie die Anfangs- und Endposition werden sofort von der Funktion handle_events an die main-Funktion zurückgegeben. Wenn dragging gleich True ist und from_pos und to_pos nicht None sind, zeichnet diese eine Linie zwischen den beiden Punkten. Da die Endposition der Linie die neue Anfangsposition ist, erhölt from_pos den Wert von to_pos. Wenn die linke Maustaste losgelassen wird, wird drawing auf False gesetzt und es wird bei einer Mausbewegung nicht mehr gezeichnet. Die Variable drawing wird zu Beginn des Programms mit False, from_pos und to_pos werden mit None initialisiert.
Daraus ergibt sich folgendes Python-Programm:
Programm 11
import pygame
WIDTH = 650
HEIGHT = 450
WHITE = (255, 255, 255)
RED = (255, 0, 0)
LIGHTGREY = (240, 240, 240)
def init():
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
screen.fill(LIGHTGREY)
pygame.display.flip()
return screen
def draw_line(screen, start_pos, end_pos):
pygame.draw.line(screen, RED, start_pos, end_pos, 3)
def handle_events(drawing, from_pos):
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1: # Linke Maustaste
drawing = True
from_pos = event.pos
elif event.button == 3: # Rechte Maustaste
return False, drawing, from_pos, None
elif event.type == pygame.MOUSEBUTTONUP:
if event.button == 1:
drawing = False
from_pos = None # Wichtig: Zurücksetzen beim Stoppen
elif event.type == pygame.MOUSEMOTION and drawing:
# Aktuelle Position für das Zeichnen zurückgeben
return True, drawing, from_pos, event.pos
return True, drawing, from_pos, None
def main():
global screen
clock = pygame.time.Clock()
screen = init()
drawing = False
from_pos = None
to_pos = None
running = True
while running:
# Zeichnen nur in main - nur wenn beide Positionen gültig sind und drawing = True
if drawing and from_pos and to_pos and from_pos != to_pos:
draw_line(screen, from_pos, to_pos)
from_pos = to_pos
pygame.display.flip()
running, drawing, from_pos, to_pos = handle_events(drawing, from_pos)
clock.tick(60)
pygame.quit()
main()
Buttons
Unser Malprogramm ist sehr einfach und wir wollen es jetzt verbessern, indem wir verschiedene Buttons hinzufügen, mit denen wir die Zeichnung löschen können und verschiedene Farben zum Zeichnen auswählen können. Zunächst erläutern wir, was ein Button ist und welche Eigenschaften ein Button hat.
Ein Button (auch Schaltfläche genannt) ist ein interaktives Element in der Benutzeroberfläche, das Benutzer anklicken können, um eine Aktion auszulösen. Buttons können verschiedene Formen haben, z.B. Rechtecke mit Aufschrift, Kreise oder irgendwelche Grafiksymbole.In vielen Programmiersprachen kann man einen Button durch eine einfache Anweisung in die Benutzeroberfläche einfügen. In Pygame müssen wir Buttons selbst programmieren. Wir beschränken uns auf rechteckige, farbige Buttons mit folgenden Eigenschaften (in der Fachsprache Attribute genannt) .
| Attribut | Beschreibung | Beispiel |
|---|---|---|
| rect | Das Rechteck, das dem Button die Form gibt | button_rect = pygame.Rect(x, y, width, height) |
| color | Farbe des Buttons als RGB-Tupel | (255, 255, 255) |
| text | Aufschrift | "Löschen" |
Darüber hinaus müssen wir eine Funktion deklarieren, die beim Klick auf den Button aufgerufen wird.
Zunächst erweitern wir unser Malprogramm um einen Button, mit dem die Zeichnung wieder gelöscht werden kann. Dazu deklarieren wir weitere Funktionen.
def get_button(x, y, w, h, c, t):
return {"rect":pygame.Rect(x, y, w, h),"color":c, "text":t}
def draw_button(screen, button):
# Button-Rechteck zeichnen
pygame.draw.rect(screen, button["color"], button["rect"])
# Button-Text zeichnen
font = pygame.font.Font(None, 24)
text = font.render(button["text"], True, WHITE)
text_rect = text.get_rect(center=button["rect"].center)
screen.blit(text, text_rect)
def is_button_clicked(pos, button):
"""Prüft, ob der Button geklickt wurde"""
return button["rect"].collidepoint(pos)
def clear_screen(screen):
screen.fill(LIGHTGREY)
Die Funktion get_button(x, y, w, h, c, t) gibt uns ein Button-Objekt als Dictionary zurück. Als Parameter übergeben wir die x- und y-Poition der linken oberen Ecke des Button-Rechtecks sowie dessen Breite und Höhe. Der Parameter c bekommt die Hintergrundfarbe des Buttons zugewiesen und t den Text, mit dem der Button beschriftet wird.
Die Funktion def draw_button(screen, button) zeichnet den Button. Als Parameter werden die Zeichenfläche und das Button-Dictionary übergeben.
Die Funktion def is_button_clicked(pos, button) prüft, ob der Punkt pos, also in der Regel die Klickposition der Maus, innerhalb des buttons liegt. Zurückgegeben werden True oder False. Dazu wird die Methode colloidepoint verwendet, die eine Methode von Rect - Objekten ist.
Die Funktion def clear_screen(screen) löscht den Bildschirm.
Jetzt müssen wir noch die Funktionen handle_events und main erweitern.
def handle_events(drawing, from_pos, button):
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1: # Linke Maustaste
# Prüfe zuerst, ob Button geklickt wurde
if is_button_clicked(event.pos, button):
drawing = False
return True, None, None, None, button
else:
# Normales Zeichnen
drawing = True
from_pos = event.pos
elif event.button == 3: # Rechte Maustaste
return False, None,None, None, None
elif event.type == pygame.MOUSEBUTTONUP:
if event.button == 1:
drawing = False
from_pos = None # Wichtig: Zurücksetzen wenn Maustaste losgelassen
elif event.type == pygame.MOUSEMOTION and drawing:
# Aktuelle Position für das Zeichnen zurückgeben
return True, drawing, from_pos, event.pos, None
return True, drawing, from_pos, None, None
| Zeile | Erläuterung |
|---|---|
| 1 | Die handle_events-Funktion benötigt die drei Parameter drawing, from_pos und button, die unter Umständen verändert an main zurückgegeben werden. Insgesamt werden beim Beenden der Funktion fünf Parameter zurückgegeben und den Variablen running, drawing, from_pos, to_pos, button zugewiesen. Wenn eine Variable für das jeweilige Event nicht relevant ist, wird stattdessen None zurückgegeben. |
| 4 | Wenn die linke Maustaste gedrückt wurde, müssen wir zwei Fälle unterscheiden. |
| 6 - 8 | 1. Fall: Ein Button wurde gedrückt. In diesem Fall muss drawing auf False gesetzt werden und drawing und button werden zurückgegeben. In weiteren Versionen des Programms werden wir mehrere Buttons haben, die als Liste an handle_events übergeben werden. In diesem Fall wird der Button zurückgegeben, der geklickt wurde. |
| 13 -14 | Die rechte Maustaste wurde gedrückt und für running wird False zurückgegeben. |
| 15 - 18 | Die rechte Maustaste wurde losgelassen, das heißt, dass nicht weiter gezeichnet werden soll. Daher müssen drawing auf False und from_pos auf None gesetzt werden. |
| 19 - 21 | Die Maus wurde bewegt und es soll gezeichnet werden. Daher wird die Funktion beendet und die aktuelle Mausposition an to_pos zurückgegeben. |
| 22 | Diese return-Anweisung wird nur erreicht, wenn entweder die linke Maustaste in der Zeichenfläche geklickt wurde oder die Event-Schleife leer ist. Da im ersten Fall drawing und from_pos verändert wurden, müssen Werte für running, drawing und from_pos zurückgegeben werden. |
Jetzt fehlt nur noch die main-Funktion
def main():
clock = pygame.time.Clock()
screen = init()
clear = clear_screen(screen)
del_button = get_button(5, HEIGHT - 35, 100, 30, BLUE, "Löschen")
draw_button(screen, del_button)
drawing = False
from_pos = None
to_pos = None
running = True
while running:
# Events verarbeiten
running, drawing, from_pos, to_pos, button = handle_events(drawing, from_pos, del_button)
if button:
# Button wurde geklickt - Bildschirm löschen
clear_screen(screen)
draw_button(screen, del_button) # Button neu zeichnen
elif drawing and from_pos and to_pos and from_pos != to_pos:
draw_line(screen, from_pos, to_pos)
from_pos = to_pos
pygame.display.flip()
clock.tick(60)
pygame.quit()
Wir erläutern die Zeilen der Funktion, die dem Malprogramm hinzugefügt wurden, um den Lösch-Button zu implementieren.
| Zeile | Erläuterung |
|---|---|
| 5 | Der Löschbutton wird erzeugt |
| 6 | Der Löschbutton wird gezeichnet. |
| 15 | Es wird geprüft ob handle-events einen Button zurückgegeben hat und nicht None ist. |
| 17 | Wenn der Button zurückgegeben wurde, wird die Zeichenfläche gelöscht. |
| 18 | Der Button wird neue gezeichnet. |
Jetzt erweitern wir das Programm noch um drei Buttons, mit denen wir die Malfarbe(rot, grün, blau) wählen können. Diese speichern wir in der Liste buttons. Die aktuelle Malfarbe speichern wir in draw_color und zeigen sie durch einen kleinen Kreis chosen_color,rechts unten in der Zeichenfläche an.
draw_color = RED
chosen_color = get_circle_dict(WIDTH-15, HEIGHT - 15, 10, RED)
buttons = []
buttons.append(get_button(5, HEIGHT - 35, 100, 30, BLUE, "Löschen"))
buttons.append(get_button(115, HEIGHT - 35, 50, 30, DARKGREEN, ""))
buttons.append(get_button(175, HEIGHT - 35, 50, 30, BLUE, ""))
buttons.append(get_button(235, HEIGHT - 35, 50, 30, RED, ""))
draw_buttons(screen, buttons)
Der handle_events-Funktion übergeben wir als Parameter jetzt die Liste mit den Buttons. Wenn ein Button gedrückt wurde, gibt die Event-Funktion den geklickten Button zurück.
def handle_events(drawing, from_pos, buttons):
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1: # Linke Maustaste
# Prüfe zuerst, ob Button geklickt wurde
for button in buttons:
if is_button_clicked(event.pos, button):
drawing = False
returnTrue, drawing, None, None, button
else:
# No.rmales Zeichnen
drawing = True
from_pos = event.pos
elif event.button == 3: # Rechte Maustaste
returnFalse, None, None, None, None
elif event.type == pygame.MOUSEBUTTONUP:
if event.button == 1:
drawing = False
from_pos = None # Wichtig: Reset beim Stoppen
returnTrue, drawing, from_pos, None, None
elif event.type == pygame.MOUSEMOTION and drawing:
# Aktuelle Position für das Zeichnen zurückgeben
returnTrue, drawing, from_pos, event.pos, None
return True, drawing, from_pos, None, None
Die Verarbeitung in der main-Funktion findest du im folgenden Programmausschnitt.
while running:
# Events verarbeiten
running, drawing, from_pos, to_pos, button = handle_events(drawing, from_pos, buttons)
draw_circle(screen, chosen_color)
if button:
# Button wurde geklickt - Bildschirm löschen
if button["text"]=="": #Es ist ein Farbbutton
draw_color = get_color(button["color"])
chosen_color["color"]=button["color"]
else:
clear_screen(screen)
draw_buttons(screen, buttons) # Button neu zeichnen
drawing = False
from_pos = None
elif drawing and from_pos and to_pos and from_pos != to_pos:
draw_line(screen, from_pos, to_pos, draw_color)
from_pos = to_pos
pygame.display.flip(
clock.tick(60))
Wenn du jetzt noch die Funktion draw_buttons hinzufügst, hast du das Programm fertiggestellt.
|
Versuche nun, das Mal-Programm mit Lösch- und Farbbuttons mithilfe der erläuterten Erweiterungen selbstständig in WTP zu implementieren. |
import pygame
# Konstanten
WIDTH = 650
HEIGHT = 500 # Höher für Button-Bereich
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
LIGHTGREY = (240, 240, 240)
BLACK = (0, 0, 0)
BLUE = (0, 0, 255)
DARKGREEN = (34, 139, 34)
def init():
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
screen.fill(LIGHTGREY)
pygame.display.flip()
return screen
def get_button(x, y, w, h, c, t):
return {"rect":pygame.Rect(x, y, w, h),"color":c, "text":t}
def get_circle_dict(x, y, r, col):
return {"x":x, "y":y, "radius": r, "color":col}
def draw_circle(screen,c):
pygame.draw.circle(screen, c["color"], ( c["x"],c["y"]), c["radius"])
def draw_buttons(screen, buttons):
"""Zeichnet den Löschen-Button"""
# Button-Rechteck zeichnen
for button in buttons:
pygame.draw.rect(screen, button["color"], button["rect"])
# Button-Text zeichnen mit expliziter weißer Farbe
font = pygame.font.Font(None, 24)
text_color = (255, 255, 255) # Explizit weiß
text = font.render(button["text"], True, text_color)
# Text-Position berechnen
text_rect = text.get_rect()
text_rect.center = button["rect"].center
# Text zeichnen
screen.blit(text, text_rect)
def is_button_clicked(pos, button):
#Prüft, ob der Button geklickt wurde
return button["rect"].collidepoint(pos)
def handle_events(drawing, from_pos, buttons):
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1: # Linke Maustaste
# Prüfe zuerst, ob Button geklickt wurde
for button in buttons:
if is_button_clicked(event.pos, button):
drawing = False
return True, drawing, None, None, button
else:
# Normales Zeichnen
drawing = True
from_pos = event.pos
elif event.button == 3: # Rechte Maustaste
return False, None, None, None, None
elif event.type == pygame.MOUSEBUTTONUP:
if event.button == 1:
drawing = False
from_pos = None # Wichtig: Reset beim Stoppen
return True, drawing, from_pos, None, None
elif event.type == pygame.MOUSEMOTION and drawing:
# Aktuelle Position für das Zeichnen zurückgeben
return True, drawing, from_pos, event.pos, None
return True, drawing, from_pos, None, None
def draw_line(screen, start_pos, end_pos, color):
pygame.draw.line(screen, color, start_pos, end_pos, 3)
def clear_screen(screen):
screen.fill(LIGHTGREY)
def get_color(color):
return color
def main():
clock = pygame.time.Clock()
screen = init()
draw_color = RED
chosen_color = get_circle_dict(WIDTH-15, HEIGHT - 15, 10, RED)
buttons = []
buttons.append(get_button(5, HEIGHT - 35, 100, 30, BLUE, "Löschen"))
buttons.append(get_button(115, HEIGHT - 35, 50, 30, DARKGREEN, ""))
buttons.append(get_button(175, HEIGHT - 35, 50, 30, BLUE, ""))
buttons.append(get_button(235, HEIGHT - 35, 50, 30, RED, ""))
draw_buttons(screen, buttons)
drawing = False
from_pos = None
to_pos = None
running = True
pygame.display.flip()
while running:
# Events verarbeiten
running, drawing, from_pos, to_pos, button = handle_events(drawing, from_pos, buttons)
draw_circle(screen, chosen_color)
if button:
# Button wurde geklickt - Bildschirm löschen
if button["text"]=="":
draw_color = get_color(button["color"])
chosen_color["color"]=button["color"]
else:
clear_screen(screen)
draw_buttons(screen, buttons) # Button neu zeichnen
drawing = False
from_pos = None
elif drawing and from_pos and to_pos and from_pos != to_pos:
draw_line(screen, from_pos, to_pos, draw_color)
from_pos = to_pos
pygame.display.flip()
clock.tick(60)
pygame.quit()
main()
|
Bearbeite jetzt die Aufgaben 4 bis 6 von Aufgaben 2 |
Wichtiger Hinweis: Wenn du WebTigerPython auf einem Tablet nutzt, kannst du die folgenden Programme nur dann ausführen, wenn du eine externe Tastatur angeschlossen hast. Mit der virtuellen Tastatur funkioniert es nicht.
KeyEvents sind Ereignisse, die auftreten, wenn der Benutzer eine Taste auf der Tastatur drückt oder loslässt. In Pygame können wir diese Events abfangen und darauf reagieren.
Wichtig in Pygame sind Key-Events bei Spielen, die mit der Tastatur bedient werden. Wenn event.type den Wert pygame.KEYDOWN (Tastegedrückt) oder pygame.KEYUP (Taste losgelassen) hat, ist ein Tastaturevent eingetreten. Mit event.key kann man die Taste abfragen, die gedrückt wurde.
Beispiel 1
def handle_events():
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
return False # Programm beenden
return True
Erläuterung
Das Pygame-Programm wird beendet,wenn auf dem PC die Escape-Taste gedrückt wurde.
Tastencodes
| Taste | Code |
|---|---|
| Navigationstasten | |
| → | pygame.K_RIGHT |
| ← | pygame.K_LEFT |
| ↑ | pygame.KEY_UP |
| ↓ | pygame.KEY_DOWN |
| Buchstaben, Ziffern | |
| a ... z | pygame.KEY_a ... pygame.KEY_z |
| 0 ... 9 | pygame.K_0 ... pygame.K_9 |
| Funktionstasten | |
| esc | pygame.K_ESCAPE |
| ↵ | pygame.K_RETURN |
| " " Leerzeichen |
pygame.K_SPACE |
Beispiel 2
Mit folgendem Programm kann man einen Kreis mit den Pfeiltasten auf dem Blidschirm bewegen und mit den Tasten r, g , b die Farbe des Kreises in rot, grün oder blau ändern.
import pygame
WIDTH = 650
HEIGHT = 450
WHITE = (255, 255, 255)
BLACK = (0, 0, 0, 0)
RED = (255, 0, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
LIGHTGREY = (240, 240, 240)
def init():
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
screen.fill(LIGHTGREY) # Schwarzer Hintergrund
pygame.display.flip()
return screen
def draw_circle(screen,x, y, r, color):
pygame.draw.circle(screen, color, (x,y), r)
def edge_left_right(x, r):
if x + r > WIDTH or x - r < 0:
r return True
return False
def handle_events():
for event in pygame.event.get():
g if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 3: # Rechte Maustaste
return False, None
if event.type == pygame.KEYDOWN:
return True, event.key
return True, None
b
def main():
clock = pygame.time.Clock()
screen = init()
x, y = 200, 300
radius = 30
color = RED
dx = 5
dy = 5
running = True
while running:
screen.fill(LIGHTGREY)
draw_circle(screen,x, y, radius, color)
running,key = handle_events()
if key == pygame.K_RIGHT:
x = x + dx
elif key == pygame.K_LEFT:
x = x - dx
elif key == pygame.K_UP:
y = y - dy
elif key == pygame.K_DOWN:
y = y + dy
elif key == pygame.K_r:
color = RED
elif key == pygame.K_g:
color = GREEN
elif key == pygame.K_b:
color = BLUE
pygame.display.flip()
clock.tick(60)
pygame.quit()
main()
Wenn du das Programm testest, merkst du schnell: Es ist nervig! 🥴
Du kannst den Ball zwar mit den Pfeiltasten bewegen, aber du musst ständig die Tasten drücken und loslassen.
Wir erweitern das Programm jetzt so, dass der Ball sich automatisch weiterbewegt, solange du eine Taste gedrückt hältst - wie bei einem Auto mit eingeschaltetem Motor! 🏎️
Um das zu erreichen müssen wir wissen, ob eine Pfeiltaste gedrückt ist. Dies speichern wir in einer Variablen. Sobald die Taste wieder losgelassen wird, wird der Wert der Variablen auf False gesetzt. Dies gilt für alle Tasten, die wir im Programm benutzen. Wir verwenden dahe ein Dictionary mit den verwendeten Tasten als key und True oder False als Wert. In der main-Funktion initialisieren wir die Variable key_pressed folgendermaßen:
keys_pressed = {pygame.K_RIGHT:False, pygame.K_LEFT:False, pygame.K_UP:False,pygame.K_DOWN:False, pygame.K_r:False,pygame.K_b:False, pygame.K_g:False}
Wir setzen alle Werte auf False, weil beim Programmstart noch keine Taste gedrückt wurde. In der Funktion handle_events, wird der Wert der gedrückten Taste auf True gesetzt und beim Loslassen wieder auf False. Die Variable keys_pressed müssen wir an handle_events übergeben und auch wieder zurückgeben, weil sich der Wert verändern kann.
Damit ergibt sich folgende Funktion:
def handle_events(keys_pressed):
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 3: # Rechte Maustaste
return False, keys_pressed
elif event.type == pygame.KEYDOWN:
keys_pressed[event.key] = True
return True, keys_pressed
elif event.type == pygame.KEYUP:
keys_pressed[event.key] = False
return True, keys_pressed
return True, keys_pressed
Für die Bewegung des Kreises schreiben wir eine eigene Funktion:
def move_circle(keys_pressed,x, y, dx, dy):
if keys_pressed[pygame.K_RIGHT]:
x = x + dx
elif keys_pressed[pygame.K_LEFT]:
x = x - dx
elif keys_pressed[pygame.K_UP]:
y = y - dy
elif keys_pressed[pygame.K_DOWN]:
y = y + dy
return x, y
|
Vervollständige das Programm unter Verwendung des erarbeiteten Codes. |
import pygame
WIDTH = 650
HEIGHT = 450
WHITE = (255, 255, 255)
BLACK = (0, 0, 0, 0)
RED = (255, 0, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
LIGHTGREY = (240, 240, 240)
def init():
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
screen.fill(LIGHTGREY) # Schwarzer Hintergrund
pygame.display.flip()
pygame.key.set_repeat()
return screen
def draw_circle(screen,x, y, r, color):
pygame.draw.circle(screen, color, (x,y), r)
def handle_events(keys_pressed):
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 3: # Rechte Maustaste
return False, keys_pressed
if event.type == pygame.KEYDOWN:
keys_pressed[event.key] = True
return True, keys_pressed
if event.type == pygame.KEYUP:
keys_pressed[event.key] = False
return True, keys_pressed
return True, keys_pressed
def move_circle(keys_pressed,x, y, dx, dy):
if keys_pressed[pygame.K_RIGHT]:
x = x + dx
elif keys_pressed[pygame.K_LEFT]:
x = x - dx
elif keys_pressed[pygame.K_UP]:
y = y - dy
elif keys_pressed[pygame.K_DOWN]:
y = y + dy
return x, y
def main():
clock = pygame.time.Clock()
screen = init()
x, y = 200, 300
radius = 30
color = RED
dx = 5
dy = 5
running = True
keys_pressed = {pygame.K_RIGHT:False, pygame.K_LEFT:False, pygame.K_UP:False,pygame.K_DOWN:False, pygame.K_r:False,pygame.K_b:False, pygame.K_g:False}
while running:
screen.fill(LIGHTGREY)
draw_circle(screen,x, y, radius, color)
running,keys_pressed = handle_events(keys_pressed)
x, y = move_circle(keys_pressed, x, y, dx, dy)
if keys_pressed[pygame.K_r]:
color = RED
elif keys_pressed[pygame.K_g]:
color = GREEN
elif keys_pressed[pygame.K_b]:
color = BLUE
pygame.display.flip()
clock.tick(60)
pygame.quit()
main()
Lösung