Seite 1 von 2

simpler Kopfrechner mit Pygame

Verfasst: Donnerstag 26. November 2020, 18:09
von Sam123
Hallo zusammen,

ich habe diesmal versucht, einen einfachen Kopfrechner mit Pygame darzustellen.
Hab schon folgendes Grundgerüst.

time.sleep(3) habe ich eingebaut, damit er nach der Eingabe und Bestätigung mit ENTER nicht sofort zur nächsten Aufgabe springt und man etwas länger die Ausgabe "richtig!" oder "falsch!" sehen kann. Das Problem ist, während er "schläft" nimmt er die Zeichen von der Tastatur auf, die dann bei der nächsten Aufgabe angezeigt werden. Woher kommt das? Ich dachte, die Zeichen von der Tastatur würden erst beim nächsten Schleifendurchlauf (der inneren while-Schleife) aufgenommen?

Vielen Dank im Voraus!

Code: Alles auswählen

import pygame, sys
from pygame.locals import *
import random
import time

pygame.init()
clock = pygame.time.Clock()

DISPLAYSURF = pygame.display.set_mode((1000, 300))
pygame.display.set_caption("Kopfrechner")

WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
BLUE = (0, 0, 128)

fontObj = pygame.font.Font('freesansbold.ttf', 50)

while True:

    zahl_1 = random.randint(1, 101)
    zahl_2 = random.randint(1, 101)
    ergebnis = zahl_1 + zahl_2
    eingabe = ""
    eingabe_abgeschlossen = False

    while eingabe_abgeschlossen != True:
        DISPLAYSURF.fill(WHITE)

        anzeige = "{0} + {1} = ".format(zahl_1, zahl_2)

        SurfaceObj_anzeige = fontObj.render(anzeige, True, GREEN, BLUE)
        DISPLAYSURF.blit(SurfaceObj_anzeige, (200, 50))

        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RETURN:
                    eingabe_abgeschlossen = True
                    if ergebnis == int(eingabe):
                        anzeige_richtig_falsch = fontObj.render("richtig!", True, GREEN, BLUE)
                        DISPLAYSURF.blit(anzeige_richtig_falsch, (600, 50))
                    else:
                        anzeige_richtig_falsch = fontObj.render("falsch!", True, GREEN, BLUE)
                        DISPLAYSURF.blit(anzeige_richtig_falsch, (600, 50))
                elif event.key == pygame.K_BACKSPACE:
                    eingabe = eingabe[:-1]
                else:
                    eingabe = eingabe + event.unicode

        anzeige_eingabe = fontObj.render(eingabe, True, GREEN, BLUE)
        DISPLAYSURF.blit(anzeige_eingabe, (450, 50))

        pygame.display.flip()
        clock.tick(30)

    time.sleep(5)

Re: simpler Kopfrechner mit Pygame

Verfasst: Donnerstag 26. November 2020, 19:38
von poldi
Ich kenne Pygame nicht so genau aber meistens läuft die Peripherie wie Maus, Tastatur etc. im eigenen Thread.

Re: simpler Kopfrechner mit Pygame

Verfasst: Donnerstag 26. November 2020, 19:50
von __deets__
poldi hat geschrieben: Donnerstag 26. November 2020, 19:38 Ich kenne Pygame nicht so genau aber meistens läuft die Peripherie wie Maus, Tastatur etc. im eigenen Thread.
Steile These, und genau andersherum verhält es sich. Ereignisse kommen üblicherweise nur im main Thread an. In dem auch die GUI läuft.

@sam123: pygame hat eine Event-queue, und darin laufen Ereignisse auf, während dein Programm was anderes macht. Es wäre ja sonst auch dramatisch wenn zb auf ein key press das key release nicht empfangen würde, weil du gerade etwas anderes machst. Wenn du Ereignisse für eine gewisse Zeit ignorieren willst, dann musst du sie so lange einfach wegwerfen, bis der Zeitpunkt gekommen ist, ab dem du sie wieder verarbeiten willst.

Re: simpler Kopfrechner mit Pygame

Verfasst: Donnerstag 26. November 2020, 20:37
von Sirius3
@Sam123: benutze keine *-Importe. Soweit ich das sehe, benutzt Du die auch gar nicht.
Nur Konstanten werden komplett gross geschrieben, DISPLAYSURF ist aber keine Konstante.
Variablennamen werden komplett klein geschrieben. Bei fontObj ist das Obj auch total nichtssagend, weil alles ein Objekt ist.
Definiere Funktionen. sys.exit hat in einem sauberen Programm nichts verloren, vor allem nicht so tief verschachtelt. Im Eventloop wird auch viel zu viel gemacht.
Statt einem time.sleep solltest Du einen event-Loop weiter laufen lassen, vor allem um auf QUIT reagieren zu können.

Code: Alles auswählen

import pygame
import random
import time

WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
BLUE = (0, 0, 128)


def event_loop(eingabe):
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            return eingabe, pygame.QUIT
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RETURN:
                return eingabe, pygame.K_RETURN
            elif event.key == pygame.K_BACKSPACE:
                eingabe = eingabe[:-1]
            else:
                eingabe = eingabe + event.unicode
    return eingabe, None


def play_round(display):
    zahl_1 = random.randint(1, 101)
    zahl_2 = random.randint(1, 101)
    ergebnis = zahl_1 + zahl_2
    aufgabe = "{0} + {1} = ".format(zahl_1, zahl_2)
    eingabe = ""
    while True:
        display.fill(WHITE)
        text = font.render(aufgabe, True, GREEN, BLUE)
        display.blit(text, (200, 50))

        eingabe, status = event_loop(eingabe)
        text = font.render(eingabe, True, GREEN, BLUE)
        display.blit(text, (450, 50))

        if status == pygame.K_RETURN:
            text = "richtig!" if ergebnis == int(eingabe) else "falsch!"
            text = font.render(text, True, GREEN, BLUE)
            display.blit(text, (600, 50))

        pygame.display.flip()
        if status is not None:
            return status
        clock.tick(30)

def pause():
    end = time.monotonic() + 5
    while time.monotonic() < end:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return pygame.QUIT
        clock.tick(30)
    return None


def main():
    pygame.init()
    pygame.display.set_caption("Kopfrechner")
    font = pygame.font.Font('freesansbold.ttf', 50)
    clock = pygame.time.Clock()

    display = pygame.display.set_mode((1000, 300))
    while True:
        status = play_round(display)
        if status == pygame.QUIT:
            break
        status = plause()
        if status == pygame.QUIT:
            break
    pygame.quit()

if __name__ == '__main__':
    main()

Re: simpler Kopfrechner mit Pygame

Verfasst: Dienstag 1. Dezember 2020, 18:39
von Sam123
danke für die Tips und den ausführlichen Code!

Ich musste bei den Funktionen play_round() und pause() die Variablen font und clock mit übergeben. Sonst lief das Programm bei mir nicht.
Ich muss ehrlich sagen, das Verständnis für das Zusammenspiel ist noch nicht wirklich da. Bin nunmal Anfänger. Muss noch viel üben :-)

Re: simpler Kopfrechner mit Pygame

Verfasst: Mittwoch 2. Dezember 2020, 20:41
von Sam123
Hallo zusammen.
Ich versuche mit sehr einfachen Beispielen den obigen Code zu verstehen.
Das Beispiel unten läuft auch, aber das Problem ist, ich kann das Fenster nicht schließen, zum Beenden.
Die Zeile mit pygame.quit() wird bei Pycharm auch mit anderer Farbe hinterlegt angezeigt und Pycharm sagt auch "this code is unreachable".
Woran liegt das?

Code: Alles auswählen

import pygame
import random

WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
BLUE = (0, 0, 128)

def schreibe_zahl(font, display, clock):
    x_Koord_text = random.randint(50, 100)
    y_Koord_text = random.randint(50, 100)
    zufallszahl = random.randint(1, 10)

    display.fill(WHITE)
    pygame.draw.rect(display, BLUE, (50, 50, 100, 100), 3)
    text_zufallszahl = font.render(str(zufallszahl), True, GREEN, BLUE)
    display.blit(text_zufallszahl, (x_Koord_text, y_Koord_text))
    pygame.display.flip()
    clock.tick(5)

def main():
    pygame.init()
    pygame.display.set_caption("Zufallszahl")
    font = pygame.font.Font('freesansbold.ttf', 50)
    clock = pygame.time.Clock()
    display = pygame.display.set_mode((1000, 300))

    while True:

        schreibe_zahl(font, display, clock)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                break

    pygame.quit()

if __name__ == '__main__':
    main()

Re: simpler Kopfrechner mit Pygame

Verfasst: Mittwoch 2. Dezember 2020, 20:56
von __blackjack__
@Sam123: Mit ``break`` wird die Schleife abgebrochen in der das steht. Das ist die ``for``-Schleife, nicht die ``while``-Schleife.

Re: simpler Kopfrechner mit Pygame

Verfasst: Donnerstag 3. Dezember 2020, 11:51
von Sam123
Mist, war ja total offensichtlich :(

Re: simpler Kopfrechner mit Pygame

Verfasst: Donnerstag 3. Dezember 2020, 13:58
von Sam123
Wenn ich die beiden Funktionen schreibe_zahl und zeichne_kreis im unteren Beispiel einzeln aufrufe, d.h. die andere jeweils auskommentiere, laufen die "Animationen" unterschiedlich schnell ab. Wenn beide Funktionen da sind, dann laufen beide Animationen langsam. Ist auch irgedwie veständlich, dass die langsamere Funktion die schnellere abbremst. Aber geht es irgendwie gleichzeitig mit unterschiedlichen Geschwindigkeiten?

Code: Alles auswählen

import pygame
import random

WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
BLUE = (0, 0, 128)
RED = (255,0,0)
YELLOW = (255,255,0)

def schreibe_zahl(font, display, clock):
    x_Koord = random.randint(50, 100)
    y_Koord = random.randint(50, 100)
    zufallszahl = random.randint(1, 10)

    pygame.draw.rect(display, WHITE, (50, 50, 100, 100))
    pygame.draw.rect(display, BLUE, (50, 50, 100, 100),1)
    text_zufallszahl = font.render(str(zufallszahl), True, GREEN, BLUE)
    display.blit(text_zufallszahl, (x_Koord, y_Koord))
    pygame.display.flip()
    clock.tick(5)

def zeichne_kreis(display, clock):
    x_Koord = random.randint(200, 300)
    y_Koord = random.randint(50, 150)

    farbenliste = [GREEN, BLUE, RED, YELLOW]
    zufallzahl_farbe = random.randint(0,3)
    farbe = farbenliste[zufallzahl_farbe]

    pygame.draw.circle(display, farbe, (x_Koord, y_Koord), 10)
    pygame.display.flip()
    clock.tick(300)

def main():
    pygame.init()
    pygame.display.set_caption("Zufallszahl")
    font = pygame.font.Font('freesansbold.ttf', 40)
    clock = pygame.time.Clock()
    display = pygame.display.set_mode((1000, 300))
    display.fill(WHITE)

    status = None
    while status != pygame.QUIT:

        schreibe_zahl(font, display, clock)
        zeichne_kreis(display,clock)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                status = pygame.QUIT

    pygame.quit()

if __name__ == '__main__':
    main()
Noch eine Frage: Warum färbt er den Code jezt nur zweifarbig ein?

Re: simpler Kopfrechner mit Pygame

Verfasst: Donnerstag 3. Dezember 2020, 20:29
von Sirius3
Natürlich darf es pro while-Schleifendurchgang nur ein display.flip und nur ein clock.tick geben.
Und wenn man etwas langsameres haben will, dann darf man die eine Sache nur alle 60 Durchläufe aktualisieren.

Re: simpler Kopfrechner mit Pygame

Verfasst: Donnerstag 3. Dezember 2020, 20:31
von Domroon
@Sam123: ich bin mir unsicher ob ich Deine Frage richtig verstanden habe. Ich würde die Framerate fest vorgeben. Das heißt clock.tick(zahl) muss aus den beiden Funktionen gelöscht werden und stattdessen in der while-Schleife von der main - Funktion eingesetzt werden. Damit z.B. die Zahlen langsamer als die Kreise gezeichnet werden, gebe ich hier vor, dass die Zahlen nur jedes 20. Frame gezeichnet werden sollen:

Code: Alles auswählen

import pygame
import random

WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
BLUE = (0, 0, 128)
RED = (255,0,0)
YELLOW = (255,255,0)

def schreibe_zahl(font, display, clock):
    x_Koord = random.randint(50, 100)
    y_Koord = random.randint(50, 100)
    zufallszahl = random.randint(1, 10)

    pygame.draw.rect(display, WHITE, (50, 50, 100, 100))
    pygame.draw.rect(display, BLUE, (50, 50, 100, 100),1)
    text_zufallszahl = font.render(str(zufallszahl), True, GREEN, BLUE)
    display.blit(text_zufallszahl, (x_Koord, y_Koord))
    pygame.display.flip()


def zeichne_kreis(display, clock):
    x_Koord = random.randint(200, 300)
    y_Koord = random.randint(50, 150)

    farbenliste = [GREEN, BLUE, RED, YELLOW]
    zufallzahl_farbe = random.randint(0,3)
    farbe = farbenliste[zufallzahl_farbe]

    pygame.draw.circle(display, farbe, (x_Koord, y_Koord), 10)
    pygame.display.flip()


def main():
    pygame.init()
    pygame.display.set_caption("Zufallszahl")
    font = pygame.font.Font('freesansbold.ttf', 40)
    clock = pygame.time.Clock()
    display = pygame.display.set_mode((1000, 300))
    display.fill(WHITE)

    speed = 60
    zaehler = 0
    status = None
    while status != pygame.QUIT:
        clock.tick(speed)
	
        if zaehler == (20 or 40 or 60):
            schreibe_zahl(font, display, clock)
        
        zeichne_kreis(display,clock)

        zaehler += 1
        if zaehler == 60:
            zaehler = 0
	
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                status = pygame.QUIT

    pygame.quit()

if __name__ == '__main__':
    main()

Re: simpler Kopfrechner mit Pygame

Verfasst: Donnerstag 3. Dezember 2020, 20:32
von Domroon
ja genau, das display.flip() sollte auch in die main-funktion wandern ;) habe ich oben jetzt nicht berücksichtigt

Re: simpler Kopfrechner mit Pygame

Verfasst: Donnerstag 3. Dezember 2020, 21:49
von Sirius3
@Domroon: or funktioniert nicht so, wie Du hoffst. Hast Du Dir mal das Ergebnis des Ausdrucks (20 or 40 or 60) angeschaut? Dafür gibt es den in-Operator. Zudem wird zaehler bei der Abfrage niemals 60.

Re: simpler Kopfrechner mit Pygame

Verfasst: Freitag 4. Dezember 2020, 06:04
von Domroon
@Sirius3: ich habe den von mit modifizierten Code vor dem posten getestet und er funktioniert wie beschrieben ;)
Es geht sicherlich deutlich eleganter, aber das ist die Änderung die mir eingefallen ist.
Der Zähler wird 60. Bei jedem Durchlauf der while-Schleife erhöhe ich "zaehler" um einen:

Code: Alles auswählen

while status != pygame.QUIT:
        clock.tick(speed)
	
        if zaehler == (20 or 40 or 60):
            schreibe_zahl(font, display, clock)
        
        zeichne_kreis(display,clock)

        zaehler += 1
        if zaehler == 60:
            zaehler = 0

Re: simpler Kopfrechner mit Pygame

Verfasst: Freitag 4. Dezember 2020, 07:40
von Sirius3
Beim Programmierern gibt es so oft den Fall, dass etwas scheinbar funktioniert und trotzdem falsch ist. Lass dir mal zaehler ausgeben:

Code: Alles auswählen

while status != pygame.QUIT:
        clock.tick(speed)
	
        if zaehler == (20 or 40 or 60):
            print(zaehler)
            schreibe_zahl(font, display, clock)
        
        zeichne_kreis(display,clock)

        zaehler += 1
        if zaehler == 60:
            zaehler = 0

Re: simpler Kopfrechner mit Pygame

Verfasst: Freitag 4. Dezember 2020, 11:41
von Sam123
Super, danke! Läuft jetzt genau wie ich wollte.

Code: Alles auswählen

while_zaehler = 0
while status != pygame.QUIT:
        clock.tick(300)

        zeichne_kreis(display, clock)

        while_zaehler += 1

        if while_zaehler in [10, 20, 100]:
            print(while_zaehler)
            schreibe_zahl(font, display, clock)

        if while_zaehler == 100:
            while_zaehler = 0

Re: simpler Kopfrechner mit Pygame

Verfasst: Freitag 4. Dezember 2020, 11:44
von Domroon
@Sirius3: Okay Fehler erkannt ;) "Or" hat wirklich nicht funktioniert und 60 wurde auch nie erreicht. Folgende main Funktion habe ich nun korrigiert:

Code: Alles auswählen

def main():
    pygame.init()
    pygame.display.set_caption("Zufallszahl")
    font = pygame.font.Font('freesansbold.ttf', 40)
    clock = pygame.time.Clock()
    display = pygame.display.set_mode((1000, 300))
    display.fill(WHITE)

    speed = 60
    zaehler = 0
    status = None
    while status != pygame.QUIT:
        clock.tick(speed)
        
        zwanzigste = [20, 40, 60]
        if zaehler in zwanzigste:
            print(zaehler)
            schreibe_zahl(font, display, clock)
        
        zeichne_kreis(display,clock)

        zaehler += 1
        if zaehler > 60:
            zaehler = 0
	
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                status = pygame.QUIT

    pygame.quit()

if __name__ == '__main__':
    main()
Danke Sirius3 ;)

Re: simpler Kopfrechner mit Pygame

Verfasst: Freitag 4. Dezember 2020, 11:45
von Domroon
@Sam123: Super freut mich ;)

Re: simpler Kopfrechner mit Pygame

Verfasst: Freitag 4. Dezember 2020, 11:57
von Sirius3
@Domroon: damit läuft jetzt der Zähler jetzt unrund, zwischen Frame 20 und 40, bzw. 40 und 60 liegen jeweils 20 Frames, zwischen 60 und 20 aber 21.

Re: simpler Kopfrechner mit Pygame

Verfasst: Freitag 4. Dezember 2020, 14:00
von Domroon
@Sirius: Ach stimmt :D Okay dann müsste ich doch einfach nur den Zähler anstatt auf 0 dann auf 1 setzen. Denn bislang habe ich die Null mitgezählt :D

Code: Alles auswählen

    speed = 60
    zaehler = 1
    status = None
    while status != pygame.QUIT:
        clock.tick(speed)
        print(zaehler)
        zwanzigste = [20, 40, 60]
        if zaehler in zwanzigste:
            schreibe_zahl(font, display, clock)
        
        zeichne_kreis(display,clock)

        zaehler += 1
        if zaehler > 60:
            zaehler = 1