How can I add a delay between keyboard button presses?

The best choice for async events in kivy is to use asyncio. First at all you have to be sure your app runs from asyncio.run() instead of App.run(). To do this, you have to import asyncio and also you have to add a method to your App class. See the example below:

import asyncio

######## MAIN APP ######## 
class ExampleApp(App):
    def build(self):
        #Your  app stuff here

    async def kivyCoro(self):  #This is the method that's gonna launch your kivy app
        await self.async_run(async_lib='asyncio')
        print('Kivy async app finished...')

    # This func will start all the "tasks", in this case the only task is the kivy app
    async def base(self):
        (done, pending) = await asyncio.wait({self.kivyCoro()}, 
    return_when='FIRST_COMPLETED')

if __name__ == '__main__':
    instanceApp = ExampleApp() #You have to instanciate your App class
    asyncio.run(instanciaApp.base()) # Run in async mode

The code above will make your kivyApp to run as a "Task" (a task it's something that's gonna be concurrently executed). A task or coroutine can call another task within itself, so you can have another async task running during the execution of your kivyApp.

# Here you create a coroutine (global scope)
async def delayWithoutFreeze():
    print('Wait 3 segs...')
    await asyncio.sleep(3)
    print('3 segs elapsed...')

Finally you just have to call that async function as a task, to do that from your _on_keyboard_down function:

def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
    j = 0 #This is a counter to know if a delay task is running
    elif keycode[1] == 'spacebar':
        for task in asyncio.all_tasks(): #This loop checks if there's a delay already running
            if task.get_name()=='shootTask':
                j+=1
                print('Theres already {} shootTask'.format(j))
        if j==0: #If j == 0 means that no delay is running so you can press and action again
            # This is how you call the async func
            asyncio.create_task(delayWithoutFreeze(), name='shootTask') 
            self.shoot()
        

NOTE: Idk why u use elif instead if in the _on_keyboard_down. You only have 1 condition. no elif needed

For more info about coroutines and tasks: https://docs.python.org/3/library/asyncio-task.html


Try this:

import time
def _on_keyboard_down(self, keyboard, keycode, text, modifiers,cooldown==3):
  elif keycode[1] == 'spacebar':
      try:
            LastShot
      except:
            LastShot=0
      if time.time()-LastShot>=cooldown:
            self.shoot()
            LastShot=time.time()

Basically, it checks whether the time between the last shot and now is greater or equal to the cooldown, then records the time of the last shot.

time.time() doesn't sleep, so it doesn't stop your program. This makes sure that your program keeps running, even when waiting for the cooldown to finish.