I have a function which records data and takes a while to complete. While the data is being recorded, I would like to have a button which, upon being pressed, displays how much time has elapsed since the data acquisition has started.
Is this possible to do in Jupyter?
I'm having issues as the data acquisition blocks the widget from activating, and if I try and run the widget in the background, it doesn't receive the on_click
event until the data acquisition has finished. On the other hand, if I send the data acquisition to the background, then data is lost once the background job finishes.
Background Job
from IPython.lib.backgroundjobs import BackgroundJobManager
from IPython.core.magic import register_line_magic
from IPython import get_ipython
jobs = BackgroundJobManager()
@register_line_magic
def background_job(line):
ip = get_ipython()
jobs.new(line, ip.user_global_ns)
Timer Button
import ipywidgets as widgets
import time
def button_timer():
t0 = 0
button = widgets.Button(description="Measure time")
def action(b):
time_elapsed = time.perf_counter() - t0
display("Time elapsed: {}".format(time_elapsed))
button.on_click(action)
display(button)
t0 = time.perf_counter()
Data Acquisition
import numpy as np
import pandas as pd
import time
def acquire(a=None):
time.sleep(10)
print("Done")
if a is None:
return np.linspace(0, 10, 10)
else:
a = np.linspace(0, 10, 10)
## Implementation 1
# This fails because the `on_click` event for the button only runs after the data has been acquired.
button_timer()
data = pd.DataFrame()
data['x'] = np.linspace(0, 10, 10)
data['y'] = acquire()
## Implementation 2
# As before, the `on_click` event isn't activated until after the data has been acquired.
%background_job button_timer()
data = pd.DataFrame()
data['x'] = np.linspace(0, 10, 10)
data['y'] = acquire()
## Implementation 3
# This one fails as the data isn't actually updated
button_timer()
data = pd.DataFrame()
data['x'] = np.linspace(0, 10, 10)
# I can't use the equal sign as that isn't supported by the background job.
# %background_job data['y'] = acquire()
# If I pass in the data I want, the DataFrame isn't updated (even after I wait for the background job to finish)
%background_job acquire(data['y'])
display(data)
If all else fails, I guess one option would be to have a Javascript-only timer which runs entirely within the browser.
Still, I'm wondering if there's a way of doing this in Python (and if possible, have it such that the (last) measured time is programmatically accessible in the rest of the notebook).