i am trying to make a game in the console and want to have scrolling text. i want to be able to hit a key/type enter and skip the scrolling and print the rest. so far i tried using pygame (out of the picture due to having to have a display surface active), asyncio with sys.stdin.read(1)(blocked the run on cmd and didnt query user in async based ide's).
this was my latest attempts at this.
import asyncio,time,sys
global skip
immutablesleep = 0.04
mutablesleep = [immutablesleep]
async def aprintl(string,sep="",end="\n",sleep=mutablesleep):
global skip
for letter in string+end:
print(letter+sep,end="",flush=True)
await asyncio.sleep(sleep[0])
skip = True
async def break_print():
global skip
while not skip:
ch = sys.stdin.read(1)
if len(ch)>0:
mutablesleep[0]=0
skip = True
await asyncio.sleep(0.1)
def printl(*args):
global skip
skip = False
mutablesleep[0] = immutablesleep
asyncio.gather(aprintl(*args),break_print())
keep in mind when suggesting modules that i want both os system independant code, and something that can be easly hooked into when freezing modules into exe.
currently this functions fairly well in terms of interrupting the slow print, but two issues persist:
1:
the interruption by pressing enter is cutting through the printed line, making it unreadable
2:
thread is still waiting for enter even after the print finished.
async def break_print():
global skip, ch
thread = Thread(target=t)
thread.start()
thread.join(timeout=0.1)
while not skip:
if len(ch) > 0:
mutablesleep[0]=0
skip = True
ch = ''
await asyncio.sleep(0.1)
def t():
"""Needed to read from stdin async"""
global ch
ch = sys.stdin.readline()
I believe your problem has to do with the last line
asyncio.gather(aprintl(*args),break_print())
Looking at the docs, the function signature looks like this: awaitable asyncio.gather(*aws, loop=None, return_exceptions=False)
. The .gather
call is likely not working as expected because you are not passing a list of callables, you're instead passing aprintl(*args)
to *aws
and break_print()
is being passed to the loop
argument
Change the line to the below, and see if it works as you're expecting.
asyncio.gather([aprintl(*args),break_print()])
I got your code to work, with some caveats
import asyncio
import sys
from threading import Thread
global skip
ch = ''
immutablesleep = 0.04
mutablesleep = [immutablesleep]
async def aprintl(string,sep="",end="\n",sleep=mutablesleep):
global skip
for letter in string+[end]:
if not skip:
print(letter+sep,end="",flush=True)
await asyncio.sleep(sleep[0])
skip = True
async def break_print():
global skip, ch
while not skip:
thread = Thread(target=t)
thread.start()
thread.join(timeout=.1)
if len(ch) > 0:
mutablesleep[0]=0
skip = True
ch = ''
await asyncio.sleep(0.1)
def t():
"""Needed to read from stdin async"""
global ch
ch = sys.stdin.readline()
async def printl(*args):
global skip
skip = False
mutablesleep[0] = immutablesleep
await asyncio.gather(aprintl(*args), break_print())
if __name__ == '__main__':
x = ['asdf ', 'asdf']*5000
asyncio.run(printl(x))
What was changed
t()
which runs in a Thread for .1 seconds every time break_print
runs -- this was required as I believe the reason your initial code isn't running is because it's hanging at the sys.stdin.read(1)
lineprintl()
via asyncio.run()
if not skip:
check in aprintl()
, otherwise it will print the entire input once skippedCaveats
.read()
you must hit enter. I use readline()
because it will return any characters input before the enter key is hit (meaning, you could check to make sure the user input some character before hitting enter: len(ch.strip() > 0): do ...
skip == True
-- this is likely because break_print()
doesn't exit when skip == True
, it will simply continue to loop.I realize this may not work in your use case, but I hope it at least gives you some ideas.