问题描述
让我们假设以下问题:我们有一个Ipywidget按钮和一个进度栏。单击该按钮后,将执行一个函数work(),该函数仅填充进度条直到完成为止,然后反转该过程并将其清空。就目前而言,这种功能是连续运行的。以下代码段提供了相应的MWE:
# importing packages.
from IPython.display import display
import ipywidgets as widgets
import time
import functools
# setting 'progress','start_button' and 'HBox' variables.
progress = widgets.FloatProgress(value=0.0,min=0.0,max=1.0)
start_button = widgets.Button(description="start fill")
HBox = widgets.HBox(children=[start_button,progress])
# defining 'on_button_clicked_start()' function; executes 'work()' function.
def on_button_clicked_start(b,start_button,progress):
work(progress)
# call to 'on_button_clicked_start()' function when clicking the button.
start_button.on_click(functools.partial(on_button_clicked_start,start_button=start_button,progress=progress))
# defining 'work()' function.
def work(progress):
total = 100
i = 0
# while roop for continuous run.
while True:
# while loop for filling the progress bar.
while progress.value < 1.0:
time.sleep(0.01)
i += 1
progress.value = float(i)/total
# while loop for emptying the progress bar.
while progress.value > 0.0:
time.sleep(0.01)
i -= 1
progress.value = float(i)/total
# display statement.
display(HBox)
目标是包括“停止”和“恢复”按钮,以便每次单击第一个循环时都会中断while循环,并在按下第二个循环时恢复执行。可以在不使用线程,多处理或异步的情况下做到这一点吗?
解决方法
这是我通过线程包得出的答案,它基于https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Asynchronous.html中给出的background-working-widget示例。它当然没有经过优化,并且可能不符合良好做法。欢迎提供任何更好答案的人。
# importing packages.
import threading
from IPython.display import display
import ipywidgets as widgets
import time
# defining progress bar 'progress',start,stop and resume buttons
# 'start_button','stop_button' and 'resume_button',and horizontal
# box 'Hbox'.
progress = widgets.FloatProgress(value=0.0,min=0.0,max=1.0)
start_button = widgets.Button(description="start fill")
stop_button = widgets.Button(description="stop fill/empty")
resume_button = widgets.Button(description="resume fill/empty")
Hbox = widgets.HBox(children=[start_button,stop_button,resume_button,progress])
# defining boolean flags 'pause' and 'resume'.
pause = False
restart = False
# defining 'on_button_clicked_start()' function.
def on_button_clicked_start(b):
# setting global variables.
global pause
global thread
global restart
# conditinoal for checking whether the thread is alive;
# if it isn't,then start it.
if not thread.is_alive():
thread.start()
# else,pause and set 'restart' to True for setting
# progress bar values to 0.
else:
pause = True
restart = True
time.sleep(0.1)
restart = False
# conditional for changing boolean flag 'pause'.
if pause:
pause = not pause
# defining 'on_button_clicked_stop()' function.
def on_button_clicked_stop(b):
# defining global variables.
global pause
# conditional for changing boolean flag 'pause'.
if not pause:
pause = not pause
# defining 'on_button_clicked_resume()' function.
def on_button_clicked_resume(b):
# defining global variables.
global pause
global restart
# conditional for changing boolean flags 'pause' and 'restart'
# if necessary.
if pause:
if restart:
restart = False
pause = not pause
# call to 'on_button_clicked_start()' function when clicking the button.
start_button.on_click(on_button_clicked_start)
# call to 'on_button_clicked_stop()' function when clicking the button.
stop_button.on_click(on_button_clicked_stop)
# call to 'on_button_clicked_resume()' function when clicking the button.
resume_button.on_click(on_button_clicked_resume)
# defining the 'work()' function.
def work(progress):
# setting global variables.
global pause
i = 0
i_m1 = 0
# setting 'total' variable.
total = 100
# infinite loop.
while True:
# stop/resume conditional.
if not pause:
# filling the progress bar.
if (i == 0) or i > i_m1 and not pause:
time.sleep(0.1)
if i == i_m1:
pass
else:
i_m1 = i
i += 1
progress.value = float(i)/total
# emptying the progress bar.
if (i == 101) or i < i_m1 and not pause:
time.sleep(0.1)
if i == i_m1:
pass
else:
i_m1 = i
i -= 1
progress.value = float(i)/total
else:
if restart:
i = 0
i_m1 = 0
# setting the thread.
thread = threading.Thread(target=work,args=(progress,))
# displaying statement.
display(Hbox)