循环时用户输入数据的最佳方法是什么?

时间:2019-07-15 20:17:26

标签: python

我有一个运行(嵌套)循环的Python程序,该循环将运行相当长的时间,我希望用户只需按 p 即可暂停和/或中止它分别是 c

我正在IPython控制台中运行它,所以我实际上没有访问msvctr.getch的权限,我想想要使其保持平台独立。

很显然,input()块是我不想要的。因此,我尝试了线程化,该线程可按预期使用,但按 CTRL C 时,线程不会停止。这很可能是因为线程阻塞导致未执行任何停止线程的合法方法(atexit,全局变量或lambda stop_thread)。

import threading
import queue
q = queue.SimpleQueue()
stop_thread = False

def handle_input(q, stopped):
    s = ''
    while not stopped():
        s = input()
        q.put(s)

thread = threading.Thread(target=handle_input,
                          args=[q, lambda: stop_thread])
thread.start()
for i in range(very_long_time):
    #Do something time consuming
    if not q.empty():
        s = q.get_nowait()
        if 'p' in s:
            print('Paused...', end='\r')
            s = s.replace('p', '')
            while True:
                if not q.empty():
                s += q.get_nowait()
                if 'p' in s or 'c' in s:
                    s = s.replace('p', '')
                    break
                    time.sleep(0.5)

         if 'c' in s:
             print('\rAborted training loop...' + ' '*50, end='\r')
             s = s.replace('c', '')
             stop_thread = True
             # Another method of stopping the thread
             # thread.__getattribute__('_tstate_lock').release()
             # thread._stop()
             # thread.join()
             break

这原则上可行,但在中断时会中断。

该线程似乎没有停止,这在同一个控制台中再次运行该线程时会造成问题,因为它甚至不要求用户输入。

此外,这会打印我的'c'或'p'和换行符,因为IPython不允许所有ANSI转义,所以我无法摆脱它。

我的方法是否可以解决,或更完善的替代方法?

1 个答案:

答案 0 :(得分:1)

您可以尝试使用keyboard module,它(除其他外)使您可以将事件挂钩绑定到键盘按下。

在这种情况下,我将创建一组全局变量/标志(例如pausedabort),最初设置为False,然后为 p 和 c 分别切换它们:

paused = False
abort = False

def toggle_paused():
    global paused
    paused = not paused

def trigger_abort():
    abort = True

keyboard.add_hotkey('p', toggle_paused())
keyboard.add_hotkey('c', trigger_abort())

然后更改循环以在每次迭代中检查pausedabort(假设每次迭代都相当快)。您已经在做的工作或多或少会起作用-只需删除已经设置的队列和线程处理的东西(无论如何,IIRC keyboard的事件都会在自己的线程上运行),将{ {1}}条件,并将条件分别更改为ifif paused:

您还可以在代码的其余部分加上寻找if abort:pause标志的内容,以便您的程序可以在方便的时候优雅地暂停或退出。您还可以扩展aborttoggle_paused()来完成您需要的任何操作(例如让trigger_abort()打印trigger_abort()或其他内容。


尽管,正如@Tomerikoo在评论中建议的那样,如果可以使用程序的设计方式,则使用"Trying to abort program (kill me if I'm not done in 5 seconds)"选项创建威胁是最佳答案。如果这是您的所有程序都,那么使用守护程序线程将不起作用,因为您的程序会立即退出,但是如果这是后台操作,则可以使用守护程序线程来放入,它不会在后台影响用户的其他体验。