Skip to content

Instantly share code, notes, and snippets.

@davesteele
Last active February 6, 2024 17:26
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davesteele/8838f03e0594ef11c89f77a7bca91206 to your computer and use it in GitHub Desktop.
Save davesteele/8838f03e0594ef11c89f77a7bca91206 to your computer and use it in GitHub Desktop.
A Python asyncio curses template
#
# This file supports http://davesteele.github.io/development/2021/08/29/python-asyncio-curses/
#
import asyncio
from abc import ABC, abstractmethod
from curses import ERR, KEY_RESIZE, curs_set, wrapper
import _curses
class Display(ABC):
def __init__(self, stdscr: "_curses._CursesWindow"):
self.stdscr = stdscr
self.done: bool = False
@abstractmethod
def make_display(self) -> None:
pass
@abstractmethod
def handle_char(self, char: int) -> None:
pass
def set_exit(self) -> None:
self.done = True
async def run(self) -> None:
curs_set(0)
self.stdscr.nodelay(True)
self.make_display()
while not self.done:
char = self.stdscr.getch()
if char == ERR:
await asyncio.sleep(0.1)
elif char == KEY_RESIZE:
self.make_display()
else:
self.handle_char(char)
class MyDisplay(Display):
def make_display(self) -> None:
msg1 = "Resize at will"
msg2 = "Press 'q' to exit"
maxy, maxx = self.stdscr.getmaxyx()
self.stdscr.erase()
self.stdscr.box()
self.stdscr.addstr(
int(maxy / 2) - 1, int((maxx - len(msg1)) / 2), msg1
)
self.stdscr.addstr(
int(maxy / 2) + 1, int((maxx - len(msg2)) / 2), msg2
)
self.stdscr.refresh()
def handle_char(self, char: int) -> None:
if chr(char) == "q":
self.set_exit()
async def display_main(stdscr):
display = MyDisplay(stdscr)
await display.run()
def main(stdscr) -> None:
return asyncio.run(display_main(stdscr))
if __name__ == "__main__":
wrapper(main)
@davesteele
Copy link
Author

davesteele commented Aug 29, 2021

The nodelay() polling of getch() is necessary. Otherwise, e.g. KEY_RESIZE is never returned from the blocking executor call.

@john-
Copy link

john- commented Oct 27, 2023

This was very helpful....thanks!

@meejah
Copy link

meejah commented Feb 5, 2024

Note also that the asyncio.sleep(..) call is vital here too: without that, basically 100% of the time is spent in curses and the reactor / proactor won't have any chance to "do network stuff"

@davesteele
Copy link
Author

"without that"

@meejah
Copy link

meejah commented Feb 6, 2024

👍 thanks (edited)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment