Python Tkinter threading animating two sets of objects with keyboard events

I’m designing the main game area of a game similar to fruit ninja. I’m trying to make the laser animation effects show once he user press the spacebar but disappear once the player let go of it. Basically the laser will only appear while the user is holding the space bar. And the fruit will be destroyed while the score will be plus. But now I’m just working on the laser effect. The create_fruit function and move_fruit function were intended to make the fruits being randomly generated and falling on the screen. They worked pretty smoothly without the laser animation. But once the laser animation was added, the whole thing became extremely leggy and the laser will only be animated for like 0.5 second then freeze. I tried to add it to a different thread but the results are the same. **Error message: **RuntimeError: main thread is not in main loop. Moreover, the keyboard event was basically not working at all due to the extremely leggy effect and the runtime error.

Here is my code:

from tkinter import *
import time
from random import randint
from PIL import Image, ImageTk
from threading import *

#make the fruits appear on the screen
def create_fruits():
    
    global fruits_list

    #choose random fruits with different possibilities
    which_fruit = randint(1,10)

    this_fruit = ImageTk.PhotoImage(Image.open(fruit_dic[which_fruit]))
    #randomly generate which colum (four in total) the fruits will be
    choose_position = randint (1,4)
    
    x_position = x_positions_dic[choose_position]  # choose the position from dict

    my_photo = canvas.create_image(x_position, 80, image = this_fruit, anchor = 's')

    #append it to the list(image_if, x position, image)
    fruits_list.append([my_photo, randint(5,10), this_fruit])

    #repeatedly generate fruit images every 0.25 second (change the density of the fruit)
    root.after(250,create_fruits)
    

def move_fruits():
    #iterate through the fruit list
    for fruit in fruits_list:
        #call the move function, move the fruit
        canvas.move(fruit[0], 0, fruit[1])
        #if the fruit gets outside of the canvas, delete it on the canvas and in the list
        if canvas.bbox(fruit[0])[3] >= canvas.winfo_height()+200:
            canvas.delete(fruit[0])
            fruits_list.remove(fruit)

    root.after(5, move_fruits)  # calls the move_fruits after every 5 seconds (change the speed of the fruit)


def laser_threading(event = None):
    # Call work function
    t1=Thread(target=laser)
    t1.start()
    

def laser():
    # create a list of image objects
    for imagefile in laser_list:
        global laser_photo
        laser_photo = PhotoImage(file=imagefile)
        laser_giflist.append(laser_photo)

    # loop through the gif image objects for a while
    for k in range(0, 1000):
        for gif in laser_giflist:
            #place each frame on the canvas
            canvas.create_image(400, 600, image=gif)
            canvas.update()
            time.sleep(0.1)
            

#create game area
root = Tk ()

canvas = Canvas (root, height = 1200, width = 800, bg = 'black')
canvas.pack()

fruits_list = []

laser_list = ["laser_1.png","laser_2.png","laser_3.png",
             "laser_4.png"]

laser_giflist = []

x_positions_dic = {1: 160, 2: 287.5, 3: 462.5, 4:637.5}
fruit_dic = {
    1: r"bomb.png" ,
    2: r"apple.png" ,
    3: r"pear.png",
    4: r"blueberry.png",
    5: r"banana.png",
    6: r"cherry.png",
    7: r"mango.png",
    8: r"lemon.png",
    9: r"watermelon.png",
    10: r"bomb.png",
    } # images path 

create_fruits()
move_fruits()

root.bind("<space>", laser_threading)

mainloop()

Source: Python Questions

LEAVE A COMMENT