insolor / async-tkinter-loop

Asynchronous mainloop implementation for tkinter. Makes it possible to use async functions as event handlers and widget commands.
https://insolor.github.io/async-tkinter-loop/
MIT License
61 stars 3 forks source link

Invalid command name while executing in customtkinter project #90

Open 4e6anenk0 opened 3 months ago

4e6anenk0 commented 3 months ago

I tried to set async-tkinter-loop for my project on customtkinter. I haven't added any async method yet. The project runs in VS Code while debugging, everything seems to work, but when I close the application, the following messages appear in the console:

Something like this:

invalid command name "139925939756800update"
    while executing
"139925939756800update"
    ("after" script)
invalid command name "140180485790400check_dpi_scaling"
    while executing
"140180485790400check_dpi_scaling"
    ("after" script)

or one of them:

invalid command name "139925939756800update"
    while executing
"139925939756800update"
    ("after" script)

What could be the problem? How critical is it?

Here is parts of my project:

main.py ```py if __name__ == "__main__": settings = get_settings() app = App(settings=settings) app.async_mainloop() ```
app.py ```py class App(ct.CTk, AsyncCTk): def __init__(self, settings: Settings): super().__init__() self.__settings = settings self.geometry(f"{1100}x{580}") self.title("App") self.minsize(700, 300) self.mainframe = MainFrame(self, settings=settings) self.mainframe.pack_configure(fill="both", expand=True) def update_all(self): updated_frame = MainFrame(self, settings=self.__settings) self.mainframe.destroy() self.mainframe = updated_frame self.mainframe.pack_configure(fill="both", expand=True) @property def settings(self) -> Settings: return self.__settings ```
mainframe.py ```py class AppProtocol(Protocol): def update(): pass class MainFrame(Page): def __init__(self, master: AppProtocol, settings: Settings, **kwargs): super().__init__(master, settings, **kwargs) self.master = master self.__pages: Dict[str, Page] = {} self.__default_page: str = None self.__current_page: str = None self.grid_rowconfigure(0, weight=1) self.grid_columnconfigure(1, weight=1) self.create_sidebar().grid_configure(row=0, column=0, sticky='wns') self.init_pages() def init_pages(self): self.add_page(SettingsPage(self, settings=self.settings)) self.add_page(HomePage(self, settings=self.settings)) self.set_default_page('SettingsPage') self.show_default_page() def create_sidebar(self) -> CTkFrame: self.sidebar = Sidebar(self, settings=self.settings, menu_btn_callback=self.on_click_menu_btn, new_macros_callback=self.show_screenshot_action) return self.sidebar def show_screenshot_action(self): self.action = ScreenshotAction(self.master) self.action.bind('', lambda event: self.action.destroy()) self.action.mainloop() def show_default_page(self): print(self.__default_page) self.__pages[self.__default_page].grid_configure(row=0, column=1, sticky='wsne') self.__current_page = self.__default_page def show_page(self, page_name: str): if self.__current_page == None: self.__pages[page_name].grid_configure(row=0, column=1, sticky='wsne') self.__current_page = page_name elif self.__current_page != page_name: self.__pages[self.__current_page].grid_remove() self.__pages[page_name].grid_configure(row=0, column=1, sticky='wsne') self.__current_page = page_name def hide_page(self, page_name: str): if self.__current_page == page_name: self.__pages[page_name].grid_remove() def add_page(self, page: Page) -> Page: self.__pages[page.__class__.__name__] = page def set_default_page(self, page_name: str): self.__default_page = page_name def on_click_menu_btn(self, page_name: str): self.show_page(page_name) ```
page.py ```py class Page(CTkFrame): def __init__(self, master: Any, settings: Settings, **kwargs): super().__init__(master, **kwargs) self.__settings = settings self.frame: CTkFrame = None self.__master = master @property def settings(self): return self.__settings def update_all(self): self.__master.update_all() ```
insolor commented 3 months ago

Probably there's nothing critical, but I'll check it later anyway

geonu01 commented 2 months ago

I solved the problem like this.

@async_handler
    async def on_close(self):
        self.save_config()
        await asyncio.sleep(0.5)

         # 활성화된 모든 비동기 작업을 안전하게 종료
        tasks = [task for task in asyncio.all_tasks() if task is not asyncio.current_task()]
        for task in tasks:
            task.cancel()
            try:
                await task
            except asyncio.CancelledError:
                pass  # 취소된 태스크에 대한 처리를 무시

        self.destroy()  # App 종료

import asyncio
from App.Src.main import App

if __name__ == "__main__":
    app = App()
    try:
        app.async_mainloop()
    except asyncio.CancelledError:
        pass  # 비동기 작업이 취소되었을 때 발생하는 예외를 처리