[英]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程序,该循环将运行相当长的时间,我希望用户能够分别分别按p和c来暂停和/或中止它。
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: 在这种情况下,我将创建一组全局变量/标志(例如,
paused
和abort
),最初设置为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())
And then change your loop to check for paused
and abort
on every iteration (assuming, that is, that each iteration is fairly quick). 然后更改循环,以检查每次迭代是否
paused
和abort
(假设每次迭代都相当快)。 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. 您还可以在代码的其余部分加上寻找
pause
或abort
标志的内容,以便程序可以在方便的时候优雅地暂停或退出。 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.