How to make start and stop buttons?

Hi everyone,

I want to implement two buttons that can start and stop a process.

Here is an example of what I want to do:

from ipywidgets import Output, Button
from IPython.display import display
import time

start = Button(description="Start")
stop = Button(description="Stop")
stop.stop = False
output = Output()

def work(start, stop, output):
    for i in range(10):
        # Sync values here
        time.sleep(0.5)
        with output:
            print(f"\rRound {i}", end="")
        if stop.stop:
            with output:
                print("\nProcess stopped")
            break
    with output:
        print("\nProcess finished")

def click_start(b):
    with output:
        print("Process started")
    work(start, stop, output)

def click_stop(b):
    stop.stop = True
        
start.on_click(click_start)
stop.on_click(click_stop)

display(start, stop, output)

In this example, the effect of clicking the stop button happens only after the process has finished.
This seems to be because the backend and frontend do not ‘touch’ [1] until the process is finished. Is there a way to sync values during the loop?

I found a way to do this with threads (see here). This only works in the classic Jupyter Notebook, but not in Jupyter Lab.

Thanks in advance for any kind of advice!

[1] The cookiecutter widget explains this as follows: “Any property tagged with sync=True is automatically synced to the frontend any time it changes in Python. It is synced back to Python from the frontend any time the model is touched.”

In case it helps…
When researching for an entirely different purpose, I came across examples of widget buttons used to start and stop. See links here and therein, plus my earlier post in that thread.
I don’t know if they work in JupyterLab. I suspect they should soon, if not already, as they are using basic python and widget abilities. JupyterLab 3 is still fairly new and that would be what you should be using if referencing JupyterLab as that is the focus of development now.

1 Like

Thanks for the suggestion! Unfortunately, I don’t understand how I can relate the content of that thread to my problem. Could you elaborate, maybe with a suggestion of how you would adapt my code example?

I just tested the timer notebook and the timer button stopping & starting does work in JupyterLab 3.0. Currently launching as described there gives JupyterLab 3.0 if you switch to it in the running session, as described here.

If I understood the workcode bock in your original post, I’m close to the solution at close to solution to widget_example_finish_indicator · GitHub .
What I failed to do was trigger Process finished to print to self.output even though I get the status text below buttons to update at the start and if you press Stop. I tried placing what I thought would do it several places. No luck. I should add that I only tested the behavior in JupyterLab as that is what was mentioned.

I should add that there is also multiprocessing that Python has. I actually have more experience with that than threading personally. If you look at the Module of the week postings for muttiprocessing and threading, you’ll see the coverage is much the same because a lot of the concepts really parallel each other. Importantly, this may tie to the odd behavior of the status because I recalled @glyg had said that the ending the process via threading was behaving weird in the notebook, see here. And so maybe @glyg’s example there would be better adapted for you, sort of what I did from the timer example.

1 Like

Thank you very much, these examples are very helpful! So threading does the job, apart from not being able to print to the output from within the thread. But since I am working with widgets anyway, I might just print the output with javascript in the front-end.

Hi,

As for printing the output, would putting the print in a finally block not work?

Hmm maybe! You would need to make sure that finally is called only after the thread has finished calculating, though, and not called from within the thread.

I think I tried that in a sense by putting it after the for loop in timeit, but it failed to update the output status text. I think it may be a scope, or possibly timing thing like @xaphod suggests. There’s a lot going on with the timer part that I didn’t get into and should have probably just removed at the start. I probably should have adapted yours as a backbone. However, I think it demonstrated what it needed to for now.