pythonarraystkintertkinter-canvasdestroy

Is there a way to destroy a Tkinter object that is stored in a list?


I'm trying to display a lot of appearing and disappearing object in Tkinter. I store those in a normal list for easy access and to be easily able to loop through them. I also want some of the objects to disappear and not come back (and thus be entirely deleted).

I've tried to use .destroy() because that should work to destroy an object in Tkinter:

loc = 0
list_with_objects.append(create_oval(1, 2, 3, 4)
list_with_objects[loc].destroy()

However, this doesn't work and only gives me the following error message: AttributeError: 'int' object has no attribute 'destroy' Any thoughts on how I could do this would be really much appreciated. Here is my full code:

from random import choices, randint
import tkinter


class World(tkinter.Canvas):
    def __init__(self, master, size):

        self.xLim = size[0]
        self.yLim = size[1]

        super().__init__(master, width=self.xLim, height=self.yLim, bg='#27c945')
        self.pack()

        self.organisms = []
        self.organTags = []

        self.run()


    def run(self):

        for organism, organTag in zip(self.organisms, self.organTags):
            organism.movement()
            self.move(organTag, organism.xDis, organism.yDis)
            
            if organism.x >= self.xLim - 10 or organism.x <= 10:
                organism.xDis *= -1
            if organism.y >= self.yLim - 10 or organism.y <= 10:
                organism.yDis *= -1
                
        toKill = []
        for loc in range(len(self.organisms)):
            if choices([0, 1], [90, 10])[0] == 1:
                toKill.append(loc)

        self.killOrganism(*toKill)
        self.newOrganism()

        self.after(25, self.run)


    def newOrganism(self):
        new = organism(self.xLim, self.yLim)
        self.organisms.append(new)
        self.organTags.append(self.create_oval(new.x - 10, new.y - 10, new.x + 10, new.y + 10, fill='black'))


    def killOrganism(self, *args):
        for loc in sorted(args, reverse=True):
            self.organTags[loc].destroy()
            self.organTags.pop(loc)
            self.organisms.pop(loc)
            

class organism():
    def __init__(self, xLim, yLim):
        self.x = randint(10, xLim - 10)
        self.y = randint(10, yLim - 10)

        self.xLim = xLim - 10
        self.yLim = yLim - 10

        self.xDis = randint(-1, 1)
        self.yDis = randint(-1, 1)


    def movement(self):
        if choices((0, 1), [99, 1]) == [1]:
            self.xDis = randint(-3, 3)
            self.yDis = randint(-2, 2)

        self.x += self.xDis
        self.y += self.yDis


worldWidth = 1280
worldLenght = 960
Map = tkinter.Tk()
Map.geometry(str(worldWidth) + 'x' + str(worldLenght))
field = World(Map, (worldWidth, worldLenght))
field.mainloop()

Solution

  • create_oval returns an int which doesn't have a destroy method. Luckily, this int is the reference of the shape in your canvas. You can delete it with your_canvas.delete(ref). So in your code:

    def killOrganism(self, *args):
        for loc in sorted(args, reverse=True):
            self.delete(self.organTags[loc])
            self.organTags.pop(loc)
            self.organisms.pop(loc)