Deleting an image from a tkinter canvas based on its coordinates

959 views Asked by At

I am working on a game where the player has to collect coins by landing on chests. However, if they land on the same chest three times, it will delete the chest that is there and will insert a bandit in its place - which will reset their coins back to zero (this part is not included in the code provided as it runs correctly).

try:
    import tkinter as tk 
    from tkinter import ttk    
except ImportError: 
    import Tkinter as tk
    from Tkinter import ttk

import random
from collections import Counter

CELL_WIDTH = 50 
CELL_HEIGHT = 50
rows = 8 
columns = 8 
bandit_number = 5
chest_number = 10
moves = 0
coins = 0
chests_visited = ()

def create_grid():
    game_frame.pack()
    game_grid.pack()
    data = {}
    for col in range(columns): 
        for row in range(rows): 
            x1 = col * CELL_WIDTH 
            y1 = row * CELL_HEIGHT
            x2 = x1 + CELL_WIDTH 
            y2 = y1 + CELL_HEIGHT
            data[row, col] = game_grid.create_rectangle(x1, y1, x2, y2,
                                                       fill="green",
                                                        tags="rect")
    return data
def create_chests(chest_image):
    global chest_dict
    chest_dict = {}
    for i in range(chest_number):

        while True:
            row = random.randint(0, rows-1)
            col = random.randint(0, columns-1)
            if (row,col) not in chest_dict: 
                break 
        x1 = col * CELL_WIDTH + 24 
        y1 = row * CELL_HEIGHT - 26 
        x2 = x1 + CELL_WIDTH
        y2 = y1 + CELL_HEIGHT
        chest_dict[row,col] = game_grid.create_image(x1, y1, image=chest_image,
                                                     tags="chest")
    return chest_dict 

def create_bandits(bandit_image):
    global bandit_dict
    bandit_dict = {}
    for i in range(bandit_number): 
        while True: 
            row = random.randint(0, rows-1)
            col = random.randint(0, columns-1)
            if (row,col) not in bandit_dict:
                break            
        x = col * CELL_WIDTH + 22 
        y = row * CELL_HEIGHT - 22
        x2 = x + CELL_WIDTH
        y2 = y + CELL_HEIGHT
        bandit_dict[row,col] = game_grid.create_image(x, y, image=bandit_image)
    return bandit_dict 
def position_player(player_image):
    global arrow
    arrow = game_grid.create_image(26, 375, image=player_image)
    display_widgets()
    return arrow 
def display_widgets():
    global move_entry_x, move_entry_y, help_lbl
    help_lbl = tk.Label(game_grid, text="Enter the x value in the first entry"+
                        " and the y value in the second." + '\n' +
                        "Use negatives to move left and down.")
    game_grid.create_window(200, 420, window=help_lbl) 
    move_entry_x = tk.Entry(game_grid)
    game_grid.create_window(70, 450, window=move_entry_x)
    move_entry_y = tk.Entry(game_grid)
    game_grid.create_window(200, 450, window=move_entry_y)
    enter_btn = ttk.Button(game_grid, text="Enter", command=check_move)
    game_grid.create_window(305, 450, window=enter_btn)

def check_move():
    global help_lbl
    if (
        move_entry_x.get()[0] == "-" or
        move_entry_y.get()[0] == "-"
        ): 
        try:
            if (
                int(move_entry_x.get()[1])*CELL_WIDTH < 26 or
                int(move_entry_x.get()[1])*CELL_WIDTH > int(rows)*CELL_WIDTH
                ):
                print("Illegal move! Enter a different value")
            elif (
                int(move_entry_y.get()[1])*CELL_WIDTH < 26 or
                int(move_entry_y.get()[1])*CELL_WIDTH > int(rows)*CELL_HEIGHT
                ):
                print("Illegal move! Enter a different value")
            else:
                move_player(arrow)
        except ValueError:
            print("Please enter a number!") 
    else: 
        try:
            if (
                int(move_entry_x.get())*CELL_WIDTH < 26 or
                int(move_entry_x.get())*CELL_WIDTH > int(rows)*CELL_WIDTH
                ): 
                print("Illegal move! Enter a different value")
            elif (
                int(move_entry_y.get())*CELL_WIDTH < 26 or
                int(move_entry_y.get())*CELL_WIDTH > int(rows)*CELL_HEIGHT
                ): 
                print("Illegal move! Enter a different value")
            else: 
                move_player(arrow) 
        except ValueError:
            print("Please enter a number!")

def move_player(arrow):
    global  move_entry_x, move_entry_y, help_lbl, moves
    x_move = move_entry_x.get() 
    y_move = move_entry_y.get()
    x = int(x_move)*CELL_WIDTH 
    y = int(y_move)*CELL_HEIGHT 
    game_grid.move(arrow, x, -y) 
    moves += 1 
    print("Moves = "+str(moves))
    check_position(arrow, chest_dict)

def check_position(arrow, chest_dict):
    global coins, arrow_coords, chests_visited
    arrow_coords = game_grid.coords(arrow)
    for i in chest_dict:
        chest_coords = game_grid.coords(chest_dict[i])
        if (
            int(arrow_coords[0])-2 in chest_coords and
            int(arrow_coords[1])-1 in chest_coords
        ):
            coins += 10
            chests_visited += tuple(arrow_coords)
            print("Chests visited: "+str(chests_visited))
    check_chests()
    return arrow, chest_dict
def check_chests():
    global chests_visited, chest_dict, bandit_dict
    cnt = Counter(chests_visited)
    if (
        [k for k, v in cnt.items() if v == 3]
        ):
        game_grid.create_image(arrow_coords[0],arrow_coords[1],
                               image=bandit_image)
        print("bandit_time")    
window = tk.Tk()

game_frame = tk.Frame(window)
game_grid = tk.Canvas(game_frame, width=500, height=500, borderwidth=0,
                      highlightthickness=0)  
game_grid.itemconfig("rect", fill="green")

bandit_image = tk.PhotoImage(file="Bandit.png") 
chest_image  = tk.PhotoImage(file="Treasure Chest.png")
player_image = tk.PhotoImage(file="Arrow.png")

rects = create_grid()
bandits = create_bandits(bandit_image) 
chests = create_chests(chest_image) 
player = position_player(player_image)
window.mainloop() 

I know you can use canvas.delete(item_id) to remove an object, given that it has been defined but my problem is that since I created my objects with a dictionary, they do not have specific names which I could use and I would like to know how I can delete an object from a canvas based on what it's coordinates are rather than it's name.

Also, as a side note, since I am using images, according to answers I have found on , the format has to be GIF, but I am able to use the PNG format and it still works fine but when I try my game on a different device, I get the expected error. Is there a reason for this?

1

There are 1 answers

0
AudioBubble On BEST ANSWER

With the help of furas, I have found a solution:

Since each chest has its own unique row and column, you can refer to an individual chest using its row and column, meaning we must find out what that row and column could be.

Because x1 = col * CELL_WIDTH + 24 and y1 = row * CELL_HEIGHT - 26 where x1 is the x coordinate and y1 is the y coordinate, the equations for the row and column must be (x1 - 24) / CELL_WIDTH = col and (y1 + 26) / CELL_HEIGHT = row.

You would then substitute for the x and y coordinates using arrow_coords[0] and arrow_coords[1]. And to get the specific row and column from the chest_dict, you would then round it to the nearest whole number and it will delete that specific chest from the canvas:

game_grid.delete(chest_dict[int(round(int(arrow_coords[0]-24)/50, 0)),
                            int(round(int(arrow_coords[1]+26)/50, 0))])