unsauberer Spritecounter (python2.5, pygame1.8.1)

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
Benutzeravatar
HorstJENS
User
Beiträge: 123
Registriert: Donnerstag 9. Februar 2006, 21:41
Wohnort: Wien, Österreich
Kontaktdaten:

Liebe pygame-gurus, bitte um Hilfe:

Habe versucht einen Spritecounter (classenvariable blogcounter) mit pygame zu erstellen, dabei tritt aber manchmal folgende ignorierte Fehlermeldung auf:
Exception exceptions.AttributeError: "'NoneType' object has no attribute 'blobcounter'" in <bound method Blob.__del__ of <Blob sprite(in 0 groups)>> ignored
und der Spritezähler der Classenvariable (blogcounter) liefert andere (falsche) Werte als der zähler der Spritegruppe len(spritegroup).

Was muss ich machen um die Anzahl der Sprites per Klassenvariable fehlerfrei anzeigen zu können ?

Das Programm dazu schaut so aus, bitte öfter auf "+" oder "-" drücken um die Diskrepanz zwischen len(spritegroup) und blogcounter zu sehen.

Code: Alles auswählen

# -*- coding: utf-8 -*-
"""
problem3.py
problem counting the sprites in a group
python 2.5, pygame 1.8.1
author: Horst JENS, www.spielend-programmieren.at
email: pygamebook@gmail.com
press + and - (several times) to see the effect.
len(spritegroup) gives (after a while) different result than blobcounter (class variable)
"""
import pygame
import random

pygame.init()
screen=pygame.display.set_mode((640,480))
pygame.display.set_caption("press Esc to exit")

background = pygame.Surface(screen.get_size()) # background
background.fill((255,255,255))     #fill the background white

class Blob(pygame.sprite.Sprite):
    """a small independent moving Circle"""
    
    blobcounter = 0 # class variable, not an object variable, therfore no "self."
    
    def __init__(self, border = False):
        self.crash = False
        self.border = border
        pygame.sprite.Sprite.__init__(self)
        mycircle = pygame.Surface((50,50))
        pygame.draw.circle(mycircle, (0,255,0), (25,25), 25, 0)
        mycircle.set_colorkey((0,0,0))
        self.imageN = mycircle
        self.imageN.convert()
        self.imageB = self.imageN.copy()
        pygame.draw.rect(self.imageB, (255,0,255), (0,0,50,50),1)
        self.imageB.convert()
        self.image = self.imageN
        self.rect = self.image.get_rect()
        self.TrueX = random.randint(0,screen.get_width()) + 0.0
        self.TrueY = random.randint(0,screen.get_height())+ 0.0
        self.rect.centerx = round(self.TrueX,0)
        self.rect.centery = round(self.TrueY,0)
        self.speedx = 0.0
        self.speedy = 0.0
        self.newspeed()
        Blob.blobcounter += 1 # change Class-variable, no self !
        
    def __del__(self):
        print "i leave" # the Spritemethod kill() calls __del__ and remove the sprite from all groups
        print "Blobs before:", Blob.blobcounter
        Blob.blobcounter -= 1
        print "Blobs after:", Blob.blobcounter
        if Blob.blobcounter >0:
           print "len(blobsprites):",len(blobsprites)
        
    def newspeed(self):
        self.speedx = random.random() * 4 -2 # a float between -2 and +2
        self.speedy = random.random() * 4 -2
        
    def avoid(self, othercenterx, othercentery):
        if othercenterx > self.TrueX:
            self.TrueX -= 1.0
        else:
            self.TrueX += 1.0
        if othercentery > self.TrueY:
            self.TrueY -= 1.0
        else:
            self.TrueY += 1.0
        self.crash = True

    def update(self, border = False):
        if border:
            self.image = self.imageB      # routine to draw border if necessary
        else:
            self.image = self.imageN

        if not self.crash:
            self.TrueX += self.speedx         #move
            self.TrueY += self.speedy
        else:
            self.crash = False

        if self.TrueX > screen.get_width():
            self.TrueX = screen.get_width() #bounce
            self.newspeed()
        if self.TrueX < 0:
            self.TrueX = 0
            self.newspeed()
        if self.TrueY > screen.get_height():
            self.TrueY = screen.get_height()
            self.newspeed()
        if self.TrueY < 0:
            self.TrueY = 0
            self.newspeed()
        # calculate new position
        self.rect.centerx = round(self.TrueX,0) # make integer out of float
        self.rect.centery = round(self.TrueY,0)


blobsprites = pygame.sprite.Group()
blobsprites.add(Blob(),Blob(), Blob(), Blob(),Blob()) # add five Blobs into the world
allsprites = pygame.sprite.LayeredUpdates(blobsprites) # later, put other sprites here

showBorder = False
myFont = pygame.font.SysFont("None",20)
textsurface1 = myFont.render("pres + or - (several times) to add or remove sprites. pres b to toggle border", True, (0,0,255))
textsurface2 = myFont.render("press ESC or q to quit", True, (0,0,255))
textsurface3 = myFont.render("why does the len(spritegroup) does not always sync with blobcounter ?", True, (255,0,0))
background.blit(textsurface1, (100, screen.get_height()/2))
background.blit(textsurface2, (100, screen.get_height()/2+20))
background.blit(textsurface3, (100, screen.get_height()/2+40))
screen.blit(background, (0,0)) # blit the  backgroundsurface on the screen
clock = pygame.time.Clock()
playmainloop = True


#main loop
while playmainloop:
    #deltatime = clock.tick(30)
    clock.tick(30)

    #event handler
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            playmainloop = False #user close pygame window with mouse
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE or event.key == pygame.K_q:
                playmainloop = False #quit
            elif event.key == pygame.K_b:
                showBorder = not showBorder # toggle between True and False
            elif event.key == pygame.K_PLUS:
                blobsprites.add(Blob()) # create a new Blob
                allsprites = pygame.sprite.LayeredUpdates(blobsprites) # recalculate the allsprites group
                screen.blit(background,(0,0)) #redraw the background to remove artefachts
            elif event.key == pygame.K_MINUS:
                for killblob in blobsprites: 
                    killblob.kill() # spritemethod, remove the sprite from all groups
                    break       # only kill one sprite at this time
                
    # collision detection
    
    #Blob crash into Blob ?
    for blob in blobsprites:
        othergroup = blobsprites.copy()
        othergroup.remove(blob) # a copy of the group without the current penguin
        if pygame.sprite.spritecollideany(blob, othergroup):
            # there was a collision, but with whom ?
            for crashblob in pygame.sprite.spritecollide(blob, othergroup, False):
                blob.avoid(crashblob.rect.centerx, crashblob.rect.centery)
            #blob.newspeed()


    # decorate pygame window
    pygame.display.set_caption("Border: %s , Blobs (len spritegroup): %i ,blobcounter (class Variable) %i "
                               %  (str(showBorder), len(blobsprites), Blob.blobcounter))

    allsprites.clear(screen, background)
    if showBorder:
        allsprites.update(True)
    else:
        allsprites.update(False)
    allsprites.draw(screen)
    pygame.display.flip()

#--end of loop
pygame.quit() #idle-friendly quit




[/code]
http://spielend-programmieren.at
Benutzeravatar
HorstJENS
User
Beiträge: 123
Registriert: Donnerstag 9. Februar 2006, 21:41
Wohnort: Wien, Österreich
Kontaktdaten:

sorry fürs posten, habe soeben die Lösung gezeigt bekommen:

Code: Alles auswählen

    #def __del__(self):
    #    print "i leave" # the Spritemethod kill() calls __del__ and remove the sprite from all groups
    #    print "Blobs before:", Blob.blobcounter
    #    Blob.blobcounter -= 1
    #    print "Blobs after:", Blob.blobcounter
    #    if Blob.blobcounter >0:
    #       print "len(blobsprites):",len(blobsprites)
    
    def kill(self):
        Blob.blobcounter-=1
        pygame.sprite.Sprite.kill(self)
http://spielend-programmieren.at
BlackJack

Ich wollte noch einmal darauf hinweisen, dass der Kommentar in `__del__()` falsch ist -- die Methode wird nicht von einer anderen Methode aufgerufen, sondern *vielleicht*, und wenn dann *irgend wann einmal*, vom Interpreter. Und zu dem Zeitpunkt kann es dann sein, dass der Interpeter am Programmende mitten beim "runterfahren" ist, und nicht mehr alles im Speicher vorhanden ist, auf das in der `__del__()` zugegriffen wird. Und wenn dann die Klasse `Blob` schon "abgeräumt" wude, kommt es zu der gezeigten Fehlermeldung wenn man auf `blobcounter` zugreifen möchte.

Fazit: `__del__()` ist nicht wirklich zu gebrauchen.
Benutzeravatar
name
User
Beiträge: 254
Registriert: Dienstag 5. September 2006, 16:35
Wohnort: Wien
Kontaktdaten:

BlackJack hat geschrieben:Fazit: `__del__()` ist nicht wirklich zu gebrauchen.
Amen. Die Dokumentation sollte das irgendwie bisschen expliziter sagen.
Ohloh | Mein Blog | Jabber: segfaulthunter@swissjabber.eu | asynchia – asynchrone Netzwerkbibliothek

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.
Antworten