pygame.QUIT wird nicht erkannt

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
stef5i
User
Beiträge: 8
Registriert: Dienstag 31. März 2020, 13:47

Hallo,
ich bin neu hier und habe auch erst letzte Woche mit Python angefangen, daher habt bitte ein wenig Geduld mit mir ^^
Auf YouTube habe ich ein Tutorial verfolgt für eine Spieleprogrammierung und bin damit in pygame eingestiegen. Soweit so gut, allerdings bekomme ich nun immer folgende Fehlermeldung:
Module 'pygame' has no 'QUIT' member
Module 'pygame' has no 'quit' member
Module 'pygame' has no 'K_LEFT' member

Ich hab mir daraufhin sehr viele Codesnippets angeschaut und weiß langsam echt nicht mehr woran es liegen könnte. Das Modul habe ich mehrmals deinstalliert und wieder frisch geholt über pip install.
Im Folgenden gebe ich euch mal den besagte Codeausschnitt. Wenn ihr mehr braucht sagt einfach Bescheid :)

Code: Alles auswählen

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

            keys = pygame.key.get_pressed()

            for key in keys:
                if keys[pygame.K_LEFT]:
                    self.dirnx = -1
                    self.dirny = 0
                    self.turns[self.head.pos[:]] = [self.dirnx, self.dirny]
__deets__
User
Beiträge: 14536
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist leider kein vollstaendiges Programm, das waere gut damit man das mal testen kann. Also auch importe etc.
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@stef5i: Ist es denn ausführbar? Also ist das nur Dein Editor oder Deine IDE die das behauptet? Dann ignorier es einfach. Mit statischer Analyse kommt man bei dynamischen Programmiersprachen halt nicht weit. Insbesondere wenn es eine Erweiterung in C ist und damit gar kein Quelltext zur Analyse bereit steht.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Dieser Teil wird nicht wie gewünscht funktionieren.

Code: Alles auswählen

            for key in keys:
                if keys[pygame.K_LEFT]:
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
stef5i
User
Beiträge: 8
Registriert: Dienstag 31. März 2020, 13:47

__blackjack__ hat geschrieben: Dienstag 31. März 2020, 15:02 @stef5i: Ist es denn ausführbar? Also ist das nur Dein Editor oder Deine IDE die das behauptet? Dann ignorier es einfach. Mit statischer Analyse kommt man bei dynamischen Programmiersprachen halt nicht weit. Insbesondere wenn es eine Erweiterung in C ist und damit gar kein Quelltext zur Analyse bereit steht.
Der Code ist leider nicht ausführbar. Ich rufe das Programm über die Konsole auf, die mir allerdings keine Fehlermeldung, sondern nur einen Hinweis auf die Website von pygame ausgibt. Die Website war zumindest gestern nicht erreichbar :/

Das hier ist die Konsolenausgabe:
pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
Zuletzt geändert von stef5i am Mittwoch 1. April 2020, 06:10, insgesamt 1-mal geändert.
stef5i
User
Beiträge: 8
Registriert: Dienstag 31. März 2020, 13:47

__deets__ hat geschrieben: Dienstag 31. März 2020, 14:47 Das ist leider kein vollstaendiges Programm, das waere gut damit man das mal testen kann. Also auch importe etc.
Ok. Kein Problem. Hier das vollständige Programm:

Code: Alles auswählen

import pygame
import math
import random
import tkinter as tk
from tkinter import messagebox

class cube(object):
    rows = 20
    w = 500
    def __init__(self, start, dirnx=1, dirny=0, color='red'):
        self.pos = start
        self.dirnx = 1
        self.dirny = 0
        self.color = color

    def move(self, dirnx, dirny):
        self.dirnx = dirnx
        self.dirny = dirny
        self.pos = (self.pos[0] + self.dirnx, self.pos[1] + self.dirny)

    def draw(self, surface, eyes=False):
        dis = self.w // self.rows
        i = self.pos[0]
        j = self.pos[1]

        pygame.draw.rect(surface, self.color, (i*dis+1, j*dis+1, dis-2, dis-2)) # '+1' und '-2' sorgt dafür, dass man nachwievor das Gitter sehen kann

        if eyes:
            centre = dis//2
            radius = 3
            circleMiddle = (i*dis+centre-radius, j*dis+8)
            circleMiddle2 = (i*dis+dis-radius*2, j*dis+8)
            pygame.draw.circle(surface, 'black', circleMiddle, radius)
            pygame.draw.circle(surface, 'black', circleMiddle2, radius)


class snake(object):
    body = []
    turns = {}
    def __init__(self, color, pos):
        self.color = color
        self.head = cube(pos)
        self.body.append(self.head)
        self.dirnx = 0
        self.dirny = 1

    def move(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()

            keys = pygame.key.get_pressed()

            for key in keys:
                if keys[pygame.K_LEFT]:
                    self.dirnx = -1
                    self.dirny = 0
                    self.turns[self.head.pos[:]] = [self.dirnx, self.dirny]

                elif keys[pygame.K_RIGHT]:
                    self.dirnx = 1
                    self.dirny = 0
                    self.turns[self.head.pos[:]] = [self.dirnx, self.dirny]

                elif keys[pygame.K_UP]:
                    self.dirnx = 0
                    self.dirny = -1
                    self.turns[self.head.pos[:]] = [self.dirnx, self.dirny]

                elif keys[pygame.K_DOWN]:
                    self.dirnx = 0
                    self.dirny = 1
                    self.turns[self.head.pos[:]] = [self.dirnx, self.dirny]

        for i, c in enumerate(self.body):   # folgender Block sorgt dafür, dass alle Körperteile an der richtige Stelle drehen
            p = c.pos[:]
            if p in self.turns:
                turn = self.turns[p]
                c.move(turn[0],turn[1])
                if i == len(self.body)-1:
                    self.turns.pop(p)
            else:
                if c.dirnx == -1 and c.pos[0] <= 0: c.pos = (c.rows-1, c.pos[1])        # Es wird überprüft, ob Rand erreicht wurde und ggf korrigiert -> schlange taucht auf anderer Seite auf
                elif c.dirnx == 1 and c.pos[0] >= c.rows-1: c.pos = (0, c.pos[1])
                elif c.dirny == 1 and c.pos[1] >= c.rows-1: c.pos = (c.pos[0], 0)
                elif c.dirny == -1 and c.pos[1] <= c.rows: c.pos = (c.pos[0], c.rows-1)
                else: c.move(c.dirnx, c.dirny)

    def reset(self, pos):
        self.head = cube(pos)
        self.body = []
        self.body.append(self.head)
        self.turns = {}
        self.dirnx = 0
        self.dirny = 1

    def addCube(self):
        tail = self.body[-1]
        dx, dy = tail.dirny, tail.dirny

        if dx == 1 and dy == 0:
            self.body.append(cube((tail.pos[0]-1, tail.pos[1])))
        elif dx == -1 and dy == 0:
            self.body.append(cube((tail.pos[0]+1, tail.pos[1])))
        elif dx == 0 and dy == 1:
            self.body.append(cube((tail.pos[0], tail.pos[1]-1)))
        elif dx == 0 and dy == -1:
            self.body.append(cube((tail.pos[0], tail.pos[1]+1)))

        self.body[-1].dirnx = dx
        self.body[-1].dirny = dy

    def draw(self, surface):
        for i, c in enumerate(self.body):
            if i == 0:
                c.draw(surface, True)
            else:
                c.draw(surface)




def drawGrid(w, rows, surface):
    sizeBtwn = w // rows

    x = 0
    y = 0
    for l in range(rows):
        x = x+ sizeBtwn
        y = y + sizeBtwn

        pygame.draw.line(surface, (255,255,255), (x,0), (x,w))      # zeichnet vertikale Linie
        pygame.draw.line(surface, (255,255,255), (0,y), (w,y))      # zeichnet horizontale Linie


def redrawWindow(surface):
    global rows, width, s, snack
    surface.fill((0,0,0))
    s.draw(surface)
    snack.draw(surface)
    drawGrid(width, rows, surface)
    pygame.display.update()

def randomSnack(rows, items):
    positions = items.body

    while True:
        x = random.randrange(rows)
        y = random.randrange(rows)
        if len(list(filter(lambda z:z.pos == (x,y), positions))) > 0:
            continue
        else:
            break

    return (x,y)

def message_box(subject, content):
    root = tk.Tk()
    root.attributes("-topmost", True)
    root.withdraw()
    messagebox.showinfo(subject, content)
    try:
        root.destroy()
    except:
        pass

def main ():
    global width, rows, s, snack
    width = 500
    rows = 20
    surface = pygame.display.set_mode((width, width))
    s = snake((255,0,0), (10,10))      # (255,0,0) Farbcode für Rot, (10,10) ist Position
    snack = cube(randomSnack(rows, s), color='green')
    flag = True

    clock = pygame.time.Clock()

    while flag:
        pygame.time.delay(50)       # sorgt für angeneheme Spielegeschwindigkeit!
        clock.tick(10)
        s.move()
        if s.body[0].pos == snack.pos:
            s.addCube()
            snack = cube(randomSnack(rows, s), color='green')

        for x in range (len(s.body)):
            if s.body[x].pos in list(map(lambda  z: z.pos, s.body[x+1:])):
                print('Score: ',len(s.body))
                message_box('Du hast leider verloren!','Nochmal Spielen')
                s.reset((10,10))
                break

        redrawWindow(surface)
Vll weitere nützliche Informationen: Ich programmiere in Visual Studio Code mit Python 3.7.0. Betriebssystem ist Windows10.
Zuletzt geändert von stef5i am Mittwoch 1. April 2020, 06:18, insgesamt 1-mal geändert.
stef5i
User
Beiträge: 8
Registriert: Dienstag 31. März 2020, 13:47

ThomasL hat geschrieben: Dienstag 31. März 2020, 15:04 Dieser Teil wird nicht wie gewünscht funktionieren.

Code: Alles auswählen

            for key in keys:
                if keys[pygame.K_LEFT]:
Erstmal danke für die Antwort :) Aber wieso wird das nicht funktionieren? Wie gesagt ich bin noch Neuling :wink:
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@stef5i: Doch der Code *ist* ausführbar und der macht auch genau das was man erwartet. Er importiert Module. Unter anderem `pygame`. Bei dessen Import entsteht die Ausgabe auf der Konsole. Dann werden Funktionen definiert. Und das Programm endet. Natürlich ohne das noch irgend was passiert, denn genau das steht da ja. Falls Du möchtest das `main()` ausgeführt wird, dann musst Du das *aufrufen*. Also am besten unten noch folgendes hinzufügen auf Modulebene:

Code: Alles auswählen

if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
stef5i
User
Beiträge: 8
Registriert: Dienstag 31. März 2020, 13:47

Achherrje... Da hab ich wohl den Wald vor lauter Bäumen nicht gesehen ^^

Vielen Dank für die Hilfe :)
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@stef5i: Noch ein paar Anmerkungen zum Code:

`math` wird importiert, aber nirgends benutzt.

Namen schreibt man in Python klein_mit_unterstrichen. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).

Man sollte nicht mehrere Anweisungen in eine Zeile quetschen. Das macht den Code nur unnötig schwerer lesbar.

Einbuchstabige Namen sind in aller Regel keine guten Namen. `s` sollte beispielsweise `snake` heissen. Da die Klasse ja `Snake` heissen sollte, ist das auch kein Problem. Auch kryptische Abkürzungen und Nummerierungen sollte man nicht machen. Bei `sizeBtwn` musste ich ganz schön raten bis ich mir halbwegs sicher war, dass das wohl `size_between` heissen soll. Soll es?

``global`` hat in einem sauberen Programm nichts zu suchen. Alles was Funktionen und Methoden ausser Konstanten benötigen, bekommen sie als Argument(e) übergeben.

`width` ist nicht nur die Breite sondern auch die Höhe. Da wäre `size` besser als Name. Und `display_size` noch besser, weil man dann nicht raten muss was für eine Grösse das eigentlich ist. Ähnliches gilt für `rows` was ja gleichzeitig auch `columns` ist, beziehungsweise sein muss, weil sonst andere Stellen im Code nicht richtig funktionieren. `cells_per_axis` oder `grid_cells_per_axis` wären da passendere Namen. Beides wären gute Kandidaten für Konstanten.

Das die Werte obwohl sie die gleiche Bedeutung haben auf `Cube` noch mal als Klassenattribute `w` und `rows` definiert sind, macht keinen Sinn und macht das ganze nur unnötig fehleranfällig.

Beim erstellen von `Snake` wird eine Farbe übergeben die nirgends verwendet wird. Entweder weg damit oder tatsächlich verwenden.

`dirnx` und `dirny` sind schlechte Namen und als Argumente von `Cube` werden die weder übergeben, noch benutzt → weg damit.

Bei `random_snack()` die Schlange `items` zu nennen, wobei die noch nicht einmal iterierbar ist, ist keine gute Idee. In der Funktion ist `positions` dann falsch, denn das sind ja keine Positionen sondern `Cube`-Objekte. Man sollte da tatsächlich die Positionen der `Cube`-Objekte ab `positions` binden, am besten als `set()`. Dann wird der furchtbar kompliziert ausgedrückte Test (``len(list(filter(lambda z: z.pos == (x, y), positions))) > 0``) einfach zu einem ``(x, y) in positions``.

`pygame.time.delay()` *und* `Clock.tick()` zu verwenden ist unsinnig. `Clock.tick()` ist zu bevorzugen.

`flag` wird nur einmal auf `True` gesetzt und nur an einer Stelle verwendet. Da kann man an dieser Stelle dann auch gleich `True` schreiben. `flag` ist als Name auch ein bisschen nichtssagend.

Bei Snake dürfen `body` und `turns` keine Klassenattribute sein, die gehören in die `__init__()`.

In `Snake.move()` ist die ``for``-Schleife über die `keys` total unsinnig.

Das kopieren von Tupeln per slicing (``[:]``) ist überall überflüssig.

`dirnx` und `dirny` bei Snake ist redundant, denn das ist ja immer die Ausrichtung vom Kopf die schon im Kopf gespeichert wird. in sofern ist dann `turns` auch redundant und unnötig, weil die Ausrichtung der Teile ja bereits in den Teilen gespeichert ist. Wenn sich die Schlange weiterbewegt, dann kann man die Ausrichtung vom Vorgänger nehmen, beziehungsweise würde man statt einer Liste und dem anpassen der Positionen aller Teile besser eine `collections.deque` nehmen und das fortbewegen lösen in dem man einen ”neuen Kopf” vorne anhängt und das letzte Teil entfernt. Dann braucht man die Ausrichtung auch nicht in allen Teilen, sondern nur noch die des Kopfes in der Schlange.

Es wird in der Hauptfunktion auf ``snake.body[0]`` zugegriffen obwohl es ``snake.head`` gibt. Und ``snake.head`` sollte nicht als eigenständiges Attribut bestehen wenn das immer den gleichen Wert wie ``snake.body[0]`` hat. Dafür gibt es `property()`.

Der Test auf Kollision ist auch extrem überkompliziert. Man muss nur schauen ob der Kopf mit einem der anderen Körperelemente kollidiert. Körperelemente können nicht mit anderen Körperelementen kollidieren ohne das vorher der Kopf das mal gemacht hätte.

Eine `reset()`-Methode sollte man nicht verwenden wenn man einfach ein neues Objekt erstellen kann. Das ist weniger fehleranfällig.

Die `main()`-Funktion könnte dann am Ende so aussehen:

Code: Alles auswählen

def main():
    surface = pygame.display.set_mode((DISPLAY_SIZE, DISPLAY_SIZE))
    snake = Snake(SNAKE_START_POSITION, "red")
    snack = Cube(random_snack_position(GRID_CELLS_PER_AXIS, snake), "green")
    clock = pygame.time.Clock()
    while True:
        clock.tick(10)
        snake.move()
        if snake.head.position == snack.position:
            snake.grow()
            snack = Cube(
                random_snack_position(GRID_CELLS_PER_AXIS, snake), "green"
            )

        parts = iter(snake.body)
        head = next(parts)
        if head.position in (part.position for part in parts):
            print("Score: ", len(snake.body))
            message_box("Du hast leider verloren!", "Nochmal Spielen")
            snake = Snake(SNAKE_START_POSITION, "green")
            break

        redraw_window(surface)
Es gäbe noch mehr zu sagen, aber das ist ja erst einmal genug Stoff zum überarbeiten. :-)
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
stef5i
User
Beiträge: 8
Registriert: Dienstag 31. März 2020, 13:47

Uff, ja das ist ganz schön viel ^^ Danke für die Anmerkungen. Ich werde versuchen das mit der Namensgebung in Zukunft auch zu beachten :) Hier habe ich einfach mal die Namen aus dem Tutorial genommen, um besser vergleichen zu können.

Den restlichen Code werde ich mal nach deinen Vorschlägen versuchen zu optimieren :)
Antworten