pygame.error: Couldn't open ship_rocket.png

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
headhunter1978
User
Beiträge: 20
Registriert: Sonntag 18. November 2018, 11:15

Hallo zusammen,

vielleicht könnt ihr mir mit folgender Frage helfen:

Ich möchte ein das Image ship_rocket.png laden, wenn ich die Klasse rocket aufrufe. Leider bekomme ich die Fehlermeldung

pygame.error: Couldn't open ship_rocket.png.

Die Python-Modul rocket.py und das Bild ship_rocket.png liegen im selben Verzeichnis. Habt ihr eine Idee was falsch laufen könnte?

Verzeichnisstruktur

Code: Alles auswählen

├── modul
│   ├── __init__.py
│   ├── rocket.py
│   └── ship_rocket.png
├── space_rocket_game.py
Klasse space_rocket_game

Code: Alles auswählen

import pygame
from pygame.locals import *

import random

from modul.rocket import Rocket

class App:
    '''
    Main class for Application. This class creates the window for the game play.
    The window will be closed when self._running changes its value to False
    '''

    #resolution for gaming window
    windowWidth = 800
    windowHeight = 600

    def __init__(self):
        self.on_init()

    def on_init(self):
        pygame.init()

        #pygame module to work with the keyboard
        #control how held keys are repeated
        pygame.key.set_repeat(1, 1)

        #pygame module to control the display window and screen
        #Initialize a window or screen for display
        self._gameDisplay = pygame.display.set_mode((self.windowWidth, self.windowHeight))
        
        #Title for window
        pygame.display.set_caption('Space Rocket')

        #create an object to help track time
        self._clock = pygame.time.Clock()

        self._running = True
        
        #Initialize additional classes
        self.rocket = Rocket(self._gameDisplay)

        self.game_loop()

    def on_event(self):
        #on_event() reacts on key events

        while self._running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self._running = False
        
            self.rocket.draw_line()

            pygame.display.update()
            self._clock=60

    def game_loop(self):
        self.on_event()
        

  
if __name__== '__main__':
    theApp = App()
Klasse rocket

Code: Alles auswählen

import pygame 

class Rocket():
    
    def __init__(self, window):
        pygame.init
        
        self._window = window
        self._rocket_image = pygame.image.load('ship_rocket.png')
        
    def draw_line(self, x=(20,100), y=(20,200)):

        print('draw_line')
        pygame.draw.line(self._window, (255,0,0), x, y, 5)

    def draw_rocket(self, x=(20,100), y=(20,200)):

        print('draw_rocket')

headhunter1978
User
Beiträge: 20
Registriert: Sonntag 18. November 2018, 11:15

Ich glaube, ich habe den Fehler gefunden. Das Working Directory ist falsch.
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nicht ganz. Das du relative Pfade verwendest, die sich auf das working-directory verlassen, ist falsch. Benutz absolute Pfade unter Zuhilfenahme von __file__ um den Pfad relativ zum laufenden Skript zu bekommen. Das ist eine sehr robuste Loesung.

Code: Alles auswählen

import os

BASE = os.path.dirname(__file__) # Pfad zum Verzeichnis dieses Skriptes.
IMAGE = os.path.join(BASE, "mein-super-bild.png") # muss relativ zu diesem Skript liegen, sonst entsprechend Pfad basteln
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@headhunter1978: Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). Also beispielsweise `WINDOW_WIDTH` und `WINDOW_HEIGHT` statt `windowWidth` und `windowHeight`.

`modul` ist ein ganz blöder Name für ein Package. Das Package sollte `space_rocket_game` heissen und die `space_rocket_game.py`-Datei sollte da *drin* liegen und `__main__.py` heissen.

Falls das da ein Modul pro Klasse werden soll: Nein! Das ist Python und nicht Java. Module sind dazu da zusammengehörige Konstanten, Funktionen, und Klassen in einem Namensraum zusammenzufassen. Wenn man in jedes Modul nur eine Klasse steckt, nutzt man Module überhaupt nicht, was falsch ist.

Die `on_init()` ist überflüssig, das gehört alles in die `__init__()`. Was da nicht reingehört ist der Aufruf von `game_loop()`. Beim erstellen eines Objekts sollte wirklich nur das Objekt erstellt werden. An der Stelle wo das Objekt erstellt wurde sollte danach ein benutzbares Objekt existieren und *der* Code entscheidet dann was damit weiter passiert. Zum Beispiel kann der dann die `game_loop()`-Methode aufrufen.

Warum besteht `game_loop()` genau wie die `__init__()` sinnloserweise nur aus dem Aufruf einer anderen Methode ohne selbst etwas zu tun? Genau wie bei `__init__()` gehört der Inhalt der aufgerufenen Methode direkt in `game_loop()`. Denn `on_event()` ist als Name für den Inhalt der Methode falsch. Bei `on_event()` erwartet der Leser das diese Methode bei *einem* Event mit diesem Event als Argument aufgerufen wird. Nicht dass das eine Methode ist die alle Events selbst abfragt und verarbeitet.

``self._clock=60`` ist sicher nicht das was Du machen willst wenn `self._clock` ursprünglich mal mit einem `Clock()`-Objekt initialisiert wurde.

``pygame.init`` in `Rocket.__init__()` hat keinen Effekt. Ein Aufruf würde dort auch gar nicht hingehören.

Das Fenster (was eigentlich ein `Surface` ist und keinem Fenster entsprechen muss), würde ich nicht an `Rocket`-Objekte binden, sondern bei `draw()`-Methoden als Argument übergeben. Es lohnt sich vielleicht auch ein Blick auf das `sprites`-Modul von `pygame` bevor man anfängt da Funktionalität selbst zu schreiben, die es bereits gibt.

Die Koordinaten einer Rakete dagegen würde ich ja eher an die Rakte binden statt die irgendwo ausserhalb zu verwalten. Und wenn man das Bild beim erstellen übergibt, kann man a) das gleiche Bild in mehreren Raketen wiederverwenden ohne es für jede Rakete einzeln zu laden und im Speicher zu halten, und b) mit der gleichen `Rocket`-Klasse Raketen mit unterschiedlichem Aussehen erstellen.

Zur Ausnahme: Relative Dateinamen werden relativ zum aktuellen Arbeitsverzeichnis gesucht, nicht relativ zu dem Modul in dem man Code schreibt der die Datei von irgendeinem anderen Modul öffnen lässt. An den Dateinamen vom Modul kommt man über die Modulvariable `__file__` heran, wo man dann mit `pathlib.Path` den Pfad zum Modul ermitteln kann und darüber dann Dateinamen relativ zum Modul. Ungetestet ``Path(__file__).absolute().parent / "ship_rocket.png"``.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
headhunter1978
User
Beiträge: 20
Registriert: Sonntag 18. November 2018, 11:15

@__blackjack__ - Danke für die Infos
Antworten