Обработка одновременного нажатия двух клавиш

Я кажется немного понял на что python ругается, но до сих пор ломаю голову.

RuntimeWarning: coroutine 'move_ka' was never awaited self.tk.mainloop(n)

сам код небольшой, признаюсь(я еще никогда не работал с этой библиотекой)

Почему она мне понадобилась?

Потому что когда я нажимаю две клавиши то ничего не происходит, так как работает только если одну клаву нажму (из кода сразу видно)

from tkinter import Tk, Canvas, PhotoImage

root = Tk()
root.geometry('700x500')
root.resizable(False,False)

canvas = Canvas(root, width=300, height=400)
canvas.pack()


my_obj = PhotoImage(file= здесь моя картинка)
id_img = canvas.create_image(50,50,anchor='c',image=my_obj) #Создаем картинку

async def move_ka(event):
    if event.keysym == 'Left':
        canvas.move(id_img, -4, 0)
    
async def move_ka2(event):
    if event.keysym == 'Right':
        canvas.move(id_img, 4, 0)

canvas.bind_all('<KeyPress-Left>', move_ka) #При нажатии вызываем функцию
canvas.bind_all('<KeyPress-Right>', move_ka2)

root.mainloop()

Сразу скажу что я думаю это происходит из-за root.mainloop() подскажите пожалуйста, НО другие ссылки прошу не кидать, я гуглил более часа!


Ответы (1 шт):

Автор решения: insolor

Почему она мне понадобилась?

Потому что когда я нажимаю две клавиши то ничего не происходит, так как работает только если одну клаву нажму (из кода сразу видно)

Во-первых, Tkinter сам по себе не умеет работать с асинхронными функциями, но просто замена обычных обработчиков на асинхронные тут и не поможет. В лучшем случае при одновременном зажатии кнопок доска будет "дергаться" вправо-влево, в худшем - последняя зажатая клавиша будет постоянно генерировать повторяющиеся события нажатия и доска будет двигаться по последней нажатой клавише.

Чтобы решить проблему, нужно отслеживать отдельно события нажатия и отпускания клавиш (т.е., если две кнопки были нажаты, но не были отпущены, значит в данный момент они нажаты вместе).

В данном случае можно в глобальных переменных хранить состояния клавиш, периодически их проверять, от их состояния менять скорость движения, например, при нажатой кнопке "влево" вычитать из скорости 4, при нажатой кнопке "вправо" - прибавлять 4, потом перемещать доску на полученное в сумме значение. В итоге, при одновременном зажатии кнопок "вправо" и "влево" скорость будет равна нулю, доска никуда не будет двигаться.

from tkinter import Tk, Canvas, PhotoImage


root = Tk()
root.geometry('700x500')
root.resizable(False,False)

canvas = Canvas(root, width=300, height=400)
canvas.pack()


# my_obj = PhotoImage(file= здесь моя картинка)
# id_img = canvas.create_image(50,50,anchor='c',image=my_obj) #Создаем картинку
id_img = canvas.create_rectangle(50, 50, 100, 60)

# Клавиши изначально не нажаты
left = False
right = False


def left_pressed(event):
    global left
    left = True


def left_released(event):
    global left
    left = False


def right_pressed(event):
    global right
    right = True


def right_released(event):
    global right
    right = False


def move():
    # Периодически перемещаем доску на vel (скорость)
    vel = -4 * left + 4 * right  # (нажатая клавиша "Влево" добавляет к скорости -4, "Вправо" - +4)
    canvas.move(id_img, vel, 0)
    root.after(50, move)  # Повторить перемещение через 50 мс


move()

canvas.bind_all('<KeyPress-Left>', left_pressed)
canvas.bind_all('<KeyRelease-Left>', left_released)
canvas.bind_all('<KeyPress-Right>', right_pressed)
canvas.bind_all('<KeyRelease-Right>', right_released)

root.mainloop()

Более универсально - запоминать вообще все нажатые или отпущенные клавиши в множестве: при нажатии добавлять в множество, при отпускании - удалять из него. Скорость вычисляем через наличие "Left" или "Right" в этом множестве.

...

# Клавиши изначально не нажаты
pressed_keys = set()


def key_pressed(event):
    pressed_keys.add(event.keysym)


def key_released(event):
    pressed_keys.remove(event.keysym)


def move():
    vel = -4 * ("Left" in pressed_keys) + 4 * ("Right" in pressed_keys)
    canvas.move(id_img, vel, 0)
    root.after(50, move)  # Повторить перемещение через 50 мс


move()

canvas.bind_all('<KeyPress>', key_pressed)
canvas.bind_all('<KeyRelease>', key_released)

root.mainloop()
→ Ссылка