NiceGUI with async classes
All notes in this series:
- NiceGUI: Always show main scrollbar
- NiceGUI: Show a confirmation popup
- NiceGUI: File upload and download
- FastAPI: Pretty print JSON
- NiceGUI with Click, Poetry, auto-reload and classes
- NiceGUI: tkinter error when updating pyplot
- NiceGUI: Bind visibility to arbitrary value
- NiceGUI: Change threshold for binding propagation warning
- NiceGUI with async classes
These notes build upon the class based NiceGUI app written about here, and modify it to be async. It may help to read those notes first.
Why use async for NiceGUI? §
The NiceGUI FAQ has some good answers.
Async init for Python classes §
Typically the init for a Python class look as follows:
class MyClass:
def __init__(self):
await self.do_something() # WRONG: Cannot await because __init__ is not async
async def do_something(self):
pass
my_obj = MyClass()
However, if there are async methods that need to be called during init, we cannot place them in the __init__
method because it is not async.
Instead, we can use the factory pattern to add an async create
class method:
class MyClass:
@classmethod
async def create(cls):
self = cls()
await self.do_something()
return self
async def do_something(self):
pass
my_obj = await MyClass.create()
Then it is permissable to use await
inside the create
method.
Using async classes with NiceGUI §
Applying the above to our async class, we can do the following:
import click
from nicegui import ui
# Very basic example GUI in a class.
class MyGUI:
@classmethod
async def create(cls):
self = cls()
ui.label("Hello, world!")
return self
# Explicit index page.
@ui.page("/")
async def index():
await MyGUI.create()
# Wrapper around the call to `ui.run`.
def my_run(port: int, reload: bool):
ui.run(port=port, reload=reload, title="My GUI")
# Entrypoint for when GUI is launched by the CLI.
# e.g.: poetry run my-cli
@click.command()
@click.option(
"--port",
default=8081,
help="Port for GUI",
)
def main(port):
print("GUI launched by CLI")
my_run(port=port, reload=False)
# Entrypoint for when GUI is launched by the CLI.
# e.g.: python my_app/my_cli.py
if __name__ in {"__main__", "__mp_main__"}:
if __name__ == "__mp_main__":
print("GUI launched by auto-reload")
my_run(port=8081, reload=True)
This enables us to use async methods within the create
method of MyGUI
, e.g.:
class MyGUI:
@classmethod
async def create(cls):
self = cls()
await self.hello_world()
return self
async def hello_world(self):
ui.label("Hello, world!")