在控制台界面中断滚动文本的一般方法

General way to interrupt scrolling text in console interface

我正在尝试在控制台中制作游戏并且想要滚动文本。我希望能够按 key/type 输入并跳过滚动并打印其余部分。 到目前为止,我尝试使用 pygame(由于必须激活显示表面而在图片之外),使用 sys.stdin.read(1) 的 asyncio(在 cmd 上阻止了 运行 并且没有查询用户基于异步 ide's).

这是我最近的尝试。

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())

在建议模块时请记住我想要 os 系统独立代码,以及在将模块冻结到 exe 时可以轻松连接的东西。

更新:

目前这在中断慢速打印方面功能相当好,但仍然存在两个问题:
1: 按 enter 的中断正在切断打印的行,使其不可读
2: 即使打印完成后线程仍在等待输入。

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()

我相信你的问题与最后一行有关

asyncio.gather(aprintl(*args),break_print())

查看 docs,函数签名如下所示:awaitable asyncio.gather(*aws, loop=None, return_exceptions=False).gather 调用可能无法按预期工作,因为您没有传递可调用项列表,而是将 aprintl(*args) 传递给 *aws 并且 break_print() 传递给loop 参数

将行更改为下面的行,看看它是否如您所期望的那样工作。

asyncio.gather([aprintl(*args),break_print()])

更新

我让你的代码可以工作,但有一些注意事项

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))

改变了什么

  • 添加了 t(),它在每次 break_print 运行时在线程中运行 0.1 秒——这是必需的,因为我相信您的初始代码不是 运行 的原因是因为它挂在 sys.stdin.read(1)
  • 运行 printl() 通过 asyncio.run()
  • aprintl()中添加了一个if not skip:检查,否则它会在跳过后打印整个输入

注意事项

  • 必须按回车键停止打印输出——即使使用.read()必须按回车键。我使用 readline() 因为它会 return 在按下回车键之前输入任何字符(意思是,您可以检查以确保用户在按下回车键之前输入了一些字符:len(ch.strip() > 0): do ...
  • 即使在 skip == True 之后脚本也没有退出 -- 这可能是因为 break_print()skip == True 时没有退出,它只会继续循环。

我知道这可能不适用于您的用例,但我希望它至少能给您一些想法。