简体   繁体   English

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

[英]What is the best way to user-input data while in a loop?

I have a Python program running a (nested) loop which will run for a fairly long time, and I want the user to be able to pause and/or abort it with just pressing p and c , respectively. 我有一个运行(嵌套)循环的Python程序,该循环将运行相当长的时间,我希望用户能够分别分别按pc来暂停和/或中止它。

I am running this in an IPython console, so I don't really have access to msvctr.getch and I kinda want to keep it platform independent. 我正在IPython控制台中运行它,因此我实际上没有访问msvctr.getch ,我有点使它独立于平台。

Obviously, input() blocks, which is exactly what I do not want. 显然, input()阻塞,这正是我所不想要的。 So I tried threading, which works when used as intended, but when hitting CTRL C the thread does not stop. 因此,我尝试了线程化,该线程可按预期使用,但按CTRL C时线程不会停止。 This is likely because any legitimate method to stop the thread (atexit, global variable or lambda stop_thread ) isn't executed because the thread blocks. 这可能是因为线程阻塞导致未执行任何停止线程的合法方法(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

This works in principle, but breaks when interrupting. 原则上这是可行的,但在中断时会中断。

The thread does not seem to stop, which poses a problem when running this again in the same console, because it does not even ask for user input then. 线程似乎没有停止,这在同一个控制台中再次运行该线程时会造成问题,因为那时它甚至不要求用户输入。

Additionally, this prints my 'c' or 'p' and a newline, which I can't get rid of, because IPython doesn't allow all ANSI escapes. 另外,这会打印我的'c'或'p'和换行符,这是我无法摆脱的,因为IPython不允许所有ANSI转义。

Is there a fix to my method, or even better, a cleaner alternative? 我的方法是否有解决方法,或更完善的替代方法?

You can try using the keyboard module , which (among other things) lets you bind event hooks to keyboard presses. 您可以尝试使用keyboard模块 ,该模块 (除其他外)使您可以将事件挂钩绑定到键盘按下。

In this case, I would create a set of global variables/flags (say, paused and abort ), initially set to False , and then make some hotkeys for p and c respectively to toggle them: 在这种情况下,我将创建一组全局变量/标志(例如, pausedabort ),最初设置为False ,然后分别为pc设置一些热键来切换它们:

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

And then change your loop to check for paused and abort on every iteration (assuming, that is, that each iteration is fairly quick). 然后更改循环,以检查每次迭代是否pausedabort (假设每次迭代都相当快)。 What you're already doing would more-or-less work - just remove the queues and threading stuff you've already set up (IIRC keyboard 's events run on their own threads anyway), de-indent the if conditions, and change the conditions to if paused: and if abort: respectively. 您已经在做的工作或多或少会起作用-只需删除已经设置的队列和线程化的东西(IIRC keyboard的事件始终在其自己的线程上运行),取消缩进if条件并更改if paused:的条件if paused:if abort:的条件。

You can also lace the rest of your code with things that look for pause or abort flags, so that your program can gracefully pause or exit at a convenient time for it. 您还可以在代码的其余部分加上寻找pauseabort标志的内容,以便程序可以在方便的时候优雅地暂停或退出。 You can also extend the toggle_paused() and trigger_abort() to do whatever you need them to (eg have trigger_abort() print "Trying to abort program (kill me if I'm not done in 5 seconds)" or something. 您还可以扩展toggle_paused()trigger_abort()来执行您需要它们执行的任何操作(例如,让trigger_abort()打印"Trying to abort program (kill me if I'm not done in 5 seconds)"或其他方法。


Although, as @Tomerikoo suggested in a comment, creating the threat with the daemon=True option is the best answer, if it's possible with the way your program is designed. 尽管,正如@Tomerikoo在评论中所建议的那样,如果可以使用程序的设计方式,则使用daemon=True选项创建威胁是最佳答案。 If this is all your program does then using daemon threads wouldn't work, because your program would just quit immediately, but if this is a background operation then you can use a daemon thread to put it in the background where it won't obstruct the rest of the user's experience. 如果这是您的程序所做的全部,那么使用守护程序线程将不起作用,因为您的程序会立即退出,但是如果这是后台操作,则可以使用守护程序线程其置于不会阻塞的后台其余的用户体验。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM