I have a simple dashboard that allows to select options before running a command (calling a python function). I would like to let the user interrupt the computation (ideally with a confirmation step), maybe with a button.
I’m not familiar enough with javascript & jupyter to extract this from the notebook source.
Has anybody done that already?
Thanks for any hint
I think to do it as gracefully as you describe, where you could then provide feedback to the user, you’d need to tie the function you are calling to a thread, see here.
This example here may help you develop the stop button part. This timer code in a working notebook here that I found in my search attempt on Github illustrates some of the concepts well, too, and may help you integrate what you require. (The first example also works where I suggested running the timer notebook in the gist.)
Thanks a lot @fomightez! I think this is indeed the clean way to do it. Having the time consuming computation in a separate thread can also let the user interact with other parts of the dashboard (without getting bored).
I’ll post a snippet with the implemented solution here when it’s ready.
So here is what I did (not perfect but it serves its purpose). For the curious, the context is here
from multiprocessing import Process
import ipywdgets as widgets
class MyWidget(widgets.VBox):
def __init__(self):
...
self.run_btn = widgets.Button(description="run")
self.run_btn.button_style = "info"
self.run_btn.on_click(self.run)
self.interupt_button = widgets.Button(description="stop")
self.interupt_button.on_click(self.stop)
self.current_process = Process(target=lambda: print("Not set"))
...
def run(self, btn):
if self.current_process.is_alive():
return
self.current_process.close()
with self.output:
if not self.kwargs:
self.run_btn.button_style = "danger"
self.run_btn.description = "errored"
return
self.run_btn.description = "running ..."
self.run_btn.button_style = "warning"
self.current_process = Process(target=a_long_job, name="main")
self.current_process.start()
self.output.clear_output()
return 0
def stop(self, btn):
if not self.current_process.is_alive():
return
self.run_btn.description = "stopping"
self.current_process.terminate()
self.current_process.join()
self.current_process.close()
self.run_btn.description = "stopped"
self.run_btn.button_style = "danger"
I tried +/- the same code with a Thread instead of a Process but could not get signal.pthread_kill to behave like I wanted to.
There is a strange behavior with notebook restart (the kernel crashes when I try to restart the notebook after I stopped the process). I assume it is because there are hanging threads somewhere, but could not find a solution to that.