11

I'm running some data analysis in ipython notebook. A separate machine collects some data and saves them to a server folder, and my notebook scans this server periodically for new files, and analyzes them.

I do this in a while loop that checks every second for new files. Currently I have set it up to terminate when some number of new files are analyzed. However, I want to instead terminate upon a keypress.

I have tried try-catching a keyboard interrupt, as suggested here: How to kill a while loop with a keystroke?

but it doesn't seem to work with ipython notebook (I am using Windows).

Using openCV's keywait does work for me, but I was wondering if there are alternative methods without having to import opencv.

I have also tried implementing a button widget that interrupts the loop, as such:

from ipywidgets import widgets 
import time
%pylab inline

button = widgets.Button(description='Press to stop')
display(button)

class Mode():
    def __init__(self):
        self.value='running'

mode=Mode()

def on_button_clicked(b):
    mode.value='stopped'

button.on_click(on_button_clicked)

while True:
    time.sleep(1)
    if mode.value=='stopped':
        break

But I see that the loop basically ignores the button presses.

1
  • 4
    For reference, the reason this doesn't work is that the kernel doesn't process the button click until after it's finished running the cell. Commented May 17, 2016 at 17:05

2 Answers 2

19

You can trigger a KeyboardInterrupt in a Notebook via the menu "Kernel --> Interrupt".

So use this:

try:
    while True:
        do_something()
except KeyboardInterrupt:
    pass

as suggested here and click this menu entry.

Sign up to request clarification or add additional context in comments.

1 Comment

KeyboardInterrupt is how the stop button works, though.
0

We need to explicitly poll the UI events

There's a neat little library called jupyter-ui-poll which handles exactly this use case! The rest of the button setup you have can stay the same. We just need to wrap a pollable scope around the loop like so:

from jupyter_ui_poll import ui_events
...
with ui_events() as poll:
    while True:
        time.sleep(1)
        poll(10) # poll queued UI events including button
        if mode.value=='stopped':
            break

The issue is that the IPython kernel executes a queue of events, but there's one queue for both UI event callbacks and cell execution. So once it's busy working on a cell, UI events don't get processed. jupyter-ui-poll temporarily (inside the scope) adjusts this queueing behaviour to allow for explicit polling.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.