Ошибка с Tkinter и асинхроном
Пишу прогу смеха ради:
from tkinter import *
from tkinter import ttk
import asyncio
root = Tk()
root.title("Управление кораблём")
root.geometry("200x100")
PlanetSelection = ttk.Combobox(values=["---------------------------","Sol-1(Меркурий)", "Sol-2(Венера)", "Sol-3 (Земля)", "Sol-4(Марс)", "Sol-5(Юпитер)", "Sol-6(Сатурн)", "Sol-9(Плутон)", "---------------------------"])
console = ttk.Label(text="")
async def startCom():
console.config(text="5")
await asyncio.sleep(1)
console.config(text="4")
await asyncio.sleep(1)
console.config(text="3")
await asyncio.sleep(1)
console.config(text="2")
await asyncio.sleep(1)
console.config(text="1")
await asyncio.sleep(0.2)
console.config(text="Запуск!")
start = Button(text="Запуск!", command=startCom)
PlanetSelection.pack()
start.pack()
console.pack()
root.mainloop()
Когда я нажимаю на кнопку "Запуск!" появляется эта ошибка:
C:\Python311\Lib\tkinter\__init__.py:1485: RuntimeWarning: coroutine 'startCom' was never awaited
self.tk.mainloop(n)
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
Скорее всего проблема в том что функция асинхронная.
Ответы (1 шт):
tkinter из коробки не умеет работать с асинхронностью. Для использования асинхронности в tkinter можно использовать, например, мою библиотеку async-tkinter-loop
(pip install async-tkinter-loop):
from tkinter import *
from tkinter import ttk
import asyncio
from async_tkinter_loop import async_handler, async_mainloop
root = Tk()
root.title("Управление кораблём")
root.geometry("200x100")
PlanetSelection = ttk.Combobox(values=[
"---------------------------",
"Sol-1(Меркурий)",
"Sol-2(Венера)",
"Sol-3 (Земля)",
"Sol-4(Марс)",
"Sol-5(Юпитер)",
"Sol-6(Сатурн)",
"Sol-9(Плутон)",
"---------------------------",
])
console = ttk.Label(text="")
@async_handler # <--- Добавить декоратор
async def startCom():
console.config(text="5")
await asyncio.sleep(1)
console.config(text="4")
await asyncio.sleep(1)
console.config(text="3")
await asyncio.sleep(1)
console.config(text="2")
await asyncio.sleep(1)
console.config(text="1")
await asyncio.sleep(0.2)
console.config(text="Запуск!")
start = Button(text="Запуск!", command=startCom)
PlanetSelection.pack()
start.pack()
console.pack()
async_mainloop(root) # <--- запуск асинхронного цикла обработки событий
В целом, обычно в tkinter для таймера используют метод .after:
def startCom(n=5):
if n > 0:
console.config(text=n)
t = 1000 if n > 1 else 200
root.after(t, startCom, n-1)
else:
console.config(text="Запуск!")
start = Button(text="Запуск!", command=startCom)
Тут с помощью root.after планируем следующий запуск функции через указанное количество миллисекунд. Третьим параметром передаем аргумент, который при следующем запуске будет передан в функцию, в данном случае — следующее значение счетчика. Когда счетчик становится равным 0, просто выводим сообщение без планирования повторного запуска.