使用Button Jupyter Notebook终止循环? [英] Kill a loop with Button Jupyter Notebook?
问题描述
我想:
- 从串口读取(无限循环)
- 按下停止按钮时-->;停止读取并绘制数据
从How to kill a while loop with a keystroke?开始,我举了一个使用键盘中断的例子,可以使用,但我想使用按钮。
键盘中断示例
weights = []
times = []
#open port
ser = serial.Serial('COM3', 9600)
try:
while True: # read infinite loop
#DO STUFF
line = ser.readline() # read a byte string
if line:
weight_ = float(line.decode()) # convert the byte string to a unicode string
time_ = time.time()
weights.append(weight_)
times.append(time_)
print (weight_)
#STOP it by keyboard interup and continue with program
except KeyboardInterrupt:
pass
#Continue with plotting
不过,我希望使用显示的按钮(更便于人们使用)。 我曾尝试制作一个按钮(在Jupiter Notebook中),当按下Break_Cicle=False时,循环不会在按下按钮时中断:
#make a button for stopping the while loop
button = widgets.Button(description="STOP!") #STOP WHEN THIS BUTTON IS PRESSED
output = widgets.Output()
display(button, output)
break_cicle=True
def on_button_clicked(b):
with output:
break_cicle = False # Change break_cicle to False
print(break_cicle)
ser.close()
button.on_click(on_button_clicked)
ser = serial.Serial('COM3', 9600)
try:
while break_cicle:
print (break_cicle)
line = ser.readline() # read a byte string
if line:
weight_ = float(line.decode()) # convert the byte string to a unicode string
time_ = time.time()
weights.append(weight_)
times.append(time_)
print (weight_)
except :
pass
ser.close()
全局不工作的示例
from IPython.display import display
import ipywidgets as widgets
button = widgets.Button(description="STOP!") #STOP WHEN THIS BUTTON IS PRESSED
output = widgets.Output()
display(button, output)
break_cicle=True
def on_button_clicked():
global break_cicle #added global
with output:
break_cicle = False # Change break_cicle to False
print ("Button pressed inside break_cicle", break_cicle)
button.on_click(on_button_clicked)
try:
while break_cicle:
print ("While loop break_cicle:", break_cicle)
time.sleep(1)
except :
pass
print ("done")
尽管我按了几次按钮,但从下图中您可以看到它从未打印过BREAK_CICLE"ON内部按下的&QUTON。
推荐答案
我认为问题就像所有具有长时间运行代码的Python脚本一样--它在一个线程中运行所有代码,当它运行while True
循环(长时间运行代码)时,它无法同时运行其他函数。
您可能必须在单独的线程中运行函数,然后主线程才能执行on_button_clicked
此版本适用于我:
from IPython.display import display
import ipywidgets as widgets
import time
import threading
button = widgets.Button(description="STOP!")
output = widgets.Output()
display(button, output)
break_cicle = True
def on_button_clicked(event):
global break_cicle
break_cicle = False
print("Button pressed: break_cicle:", break_cicle)
button.on_click(on_button_clicked)
def function():
while break_cicle:
print("While loop: break_cicle:", break_cicle)
time.sleep(1)
print("Done")
threading.Thread(target=function).start()
也许Jupyter有其他方法来解决这个问题--即。当您使用async
编写函数时,您可以使用asyncio.sleep()
,它允许在其他函数处于休眠状态时运行该函数。
编辑:
在互联网上搜索(使用谷歌),我在Jyputer论坛上找到了帖子
Interactive widgets while executing long-running cell - JupyterLab - Jupyter Community Forum
并且有到模块jupyter-ui-poll的链接,该模块显示了类似的示例(while
-loop+Button
),它使用events
来实现这一点。当函数pull()
被执行时(在每个循环中),Jupyter可以向窗口小部件发送事件,并且它有时间执行on_click()
。
import time
from ipywidgets import Button
from jupyter_ui_poll import ui_events
# Set up simple GUI, button with on_click callback
# that sets ui_done=True and changes button text
ui_done = False
def on_click(btn):
global ui_done
ui_done = True
btn.description = '👍'
btn = Button(description='Click Me')
btn.on_click(on_click)
display(btn)
# Wait for user to press the button
with ui_events() as poll:
while ui_done is False:
poll(10) # React to UI events (upto 10 at a time)
print('.', end='')
time.sleep(0.1)
print('done')
在source code中,我可以看到它使用asyncio
来执行此操作。
编辑:
进程不共享变量,因此需要Queue
将信息从一个进程发送到另一个进程。
button
发送到function
。如果要将消息从function
发送到button
,则最好使用第二队列。
from IPython.display import display
import ipywidgets as widgets
import time
import multiprocessing
button = widgets.Button(description="STOP!")
output = widgets.Output()
display(button, output)
queue = multiprocessing.Queue()
def on_button_clicked(event):
queue.put('stop')
print("Button pressed")
button.on_click(on_button_clicked)
def function(queue):
while True:
print("While loop")
time.sleep(1)
if not queue.empty():
msg = queue.get()
if msg == 'stop':
break
#if msg == 'other text':
# ...other code...
print("Done")
multiprocessing.Process(target=function, args=(queue,)).start()
或更类似于上一次
def function(queue):
break_cicle = True
while break_cicle:
print("While loop: break_cicle:", break_cicle)
time.sleep(1)
if (not queue.empty()) and (queue.get() == 'stop'):
break_cicle = False
print("Done")
编辑:
asyncio的版本
Jupyter已经在运行asynio event loop
,我将async function
添加到这个循环中。而函数使用await
这样的函数,因此asynio event loop
有时间运行其他函数-但如果函数只能运行标准(不是异步)函数,则它将无法运行。
from IPython.display import display
import ipywidgets as widgets
import asyncio
button = widgets.Button(description="STOP!")
output = widgets.Output()
display(button, output)
break_cicle = True
def on_button_clicked(event):
global break_cicle
break_cicle = False
print("Button pressed: break_cicle:", break_cicle)
button.on_click(on_button_clicked)
async def function(): # it has to be `async`
while break_cicle:
print("While loop: break_cicle:", break_cicle)
await asyncio.sleep(1) # it needs some `await` functions
print("Done")
loop = asyncio.get_event_loop()
t = loop.create_task(function()) # assign to variable if you don't want to see `<Task ...>` in output
这篇关于使用Button Jupyter Notebook终止循环?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!