max
max

Reputation: 41

Tkinter removing an object from the canvas using delete

I am having difficulty understanding how to delete an object from the canvas. A rocket object is created from Class Rocket and is just a simple oval. After an event, I'm trying to use the canvas.delete() method with the argument set to rocket in order to remove this object from the canvas but it doesn't seem to work.

My goal is to create multiple rockets in a list and delete some during the animation.

I have include just main() function. The Class Rocket just creates an oval on the canvas and works fine.

file: constants.py

CANVAS_WIDTH = 600      # Width of drawing canvas in pixels
CANVAS_HEIGHT = 600     # Height of drawing canvas in pixels
ROCKET_SIZE = 30

file: test.py

import tkinter
import time
import random
from constants import *
from Rocket import Rocket

def main():
    canvas = make_canvas(CANVAS_WIDTH, CANVAS_HEIGHT, 'Fireworks')

    #put a rocket a bottom of canvas
    rocket = Rocket(canvas)
    canvas.update()
    time.sleep(50/50.)  #pause to see the rocket

    # some event occurs (not important for this example)

    canvas.delete(rocket)  #this doesn't make the rocket disappear!
    canvas.update()
    time.sleep(50/50.)

def make_canvas(width, height, title):

    top = tkinter.Tk()
    top.minsize(width=width, height=height)
    top.title(title)
    canvas = tkinter.Canvas(top, width=width + 1, height=height + 1)
    canvas.pack()
    return canvas

if __name__ == '__main__':
    main()

file: Rocket.py

import random
from constants import *

class Rocket:
    '''
    This is the blueprint for a new variable type called "Rocket"
    Every rocket has three things: an oval, a change_x and a change_y.
    Every rocket supports the "update" method which will move the rocket one step.
    '''
    def __init__(self, canvas):
        # Starting point - screen bottom with slight angle
        x_1 = random.randint(CANVAS_WIDTH/2 - 50, CANVAS_WIDTH/2 + 50)
        y_1 = CANVAS_HEIGHT - ROCKET_SIZE
        x_2 = x_1 + ROCKET_SIZE
        y_2 = CANVAS_HEIGHT
        self.fill = random.choice(['blue', 'green', 'orange', 'purple', 'red', 'lime'])
        self.oval = canvas.create_oval(x_1, y_1, x_2, y_2, fill=self.fill, outline=self.fill)
        self.change_x = random.randint(-3, 3)
        self.change_y = random.randint(-15, -5)

    # again, I pass in canvas.
    def update(self, canvas):
        # update a single rocket instance (the one given by self)
        canvas.move(self.oval, self.change_x, self.change_y)

Upvotes: 2

Views: 4831

Answers (2)

max
max

Reputation: 41

After helpful feedback and more research, here is my first solution;

add to Class Rocket the following -

    # method to get canvas object id (already assigned to self.oval in constructor)
    def get_id(self):
       return self.oval

change the following line:

canvas.delete(rocket)

in main() to:

canvas.delete(rocket.get_id())

This also worked for multiple rockets created in a list.

I haven't tried deleting the rocket within the Class Rocket as a method, but this seems like the most elegant solution. Either way, the object ID is the required argument for canvas.delete.

Upvotes: 1

Bryan Oakley
Bryan Oakley

Reputation: 385830

When you call canvas.delete(rocket), tkinter will convert rocket to a string before passing it off to the actual delete method. So, you can modify your rocket class to return the canvas object id from the __str__ method:

class Rocket():
    ...
    def __str__(self):
        return str(self.oval)

The downside to this is that it only allows you to have a single canvas element for drawing the rocket. If you have more than just a single object, you can instead have __str__ return an id, such as "rocket#1", and associate that id with every object that makes up the rocket.

For example, this uses both an oval and square for the rocket:

def __init__(self, canvas)
    ...
    self.oval = canvas.create_oval(x_1, y_1, x_2, y_2, fill=self.fill, outline=self.fill)
    # the id of the oval to create a tag for all rocket pieces
    self.tag = f"rocket#{self.oval}"
    self.canvas.itemconfigure(self.oval, tags=(self.tag,))
    self.square = canvas.create_rectangle(x_1, y_1, x_2, y_1-20, fill="black", tags=(self.tag,))

def __str__(self):
    return self.tag

Perhaps a better way would be to give your rocket a delete method (or distruct!) so that it is responsible for deleting itself.

This requires saving the canvas, and then having the rocket call self.canvas.delete(self.oval) when you want the rocket to be destroyed.

class Rocket:
    def __init__(self, canvas):
        self.canvas = canvas
        ...

    def destruct(self):
        self.canvas.delete(self.oval)

...
def main():
    ...
    # some event occurs (not important for this example)
    rocket.destruct()

Upvotes: 1

Related Questions