AaronWatters / jp_proxy_widget

Generic Jupyter/IPython widget implementation that will support many types of javascript libraries and interactions.
BSD 2-Clause "Simplified" License
61 stars 12 forks source link

feature request: W.renderNow() or similar #2

Closed magland closed 5 years ago

magland commented 6 years ago

The following example shows real time update of a jp_proxy_widget showing the status of a computation

# First define the widget and display it
import jp_proxy_widget
W=jp_proxy_widget.JSProxyWidget()
W.js_init("""
    element.empty()
    element.append('<h3></h3>')
    element.setText=function(txt) {
        element.find('h3').html(txt)
    }
""")
W.check_jquery()
W.element.setText('hello')
display(W)

# Note that the following is needed in order for the the real-time update to work
import IPython, time
time.sleep(0.1) # It is unclear how long one needs to sleep... e.g., 0.001 does not seem to work
IPython.get_ipython().kernel.do_one_iteration()

# Note: If the following is in a separate cell (or called in a separate thread after the a delay), then the real-time update works without the above hack
def run_calc():
    a=0
    for j in range(1,501):
        W.element.setText('Progress: {}/{}'.format(j,500))
        for k in range(5000):
            a=a+j*k
run_calc()

Suggestion: It would be great to have some API call on the widget that does the above... it could be something like

W.renderNow()

As indicated in the code above, yes, I could just do this in a separate call, but for my use case, this is not a good option.

I'm happy to try implementing this if that would be helpful.

magland commented 6 years ago

However, there is a problem with this, I am finding...

in jupyterlab, if I use the above hack to render the widget prior to the calculation, everything works, EXCEPT, the cell does not get marked as executed at the end. It still has the [*] indicator. This must have to do with the IPython.get_ipython().kernel.do_one_iteration() call. I wonder if there is a way around this, or if there is a different way to force rendering.

magland commented 6 years ago

Another, more serious problem with the above "solution": the jupyterlab stop button does not work on the calculation once do_one_iteration() is called! Therefore, not a viable solution.

AaronWatters commented 6 years ago

In my experience, unfortunately, sleeps and do_one_iteration just make async anomolies worse.

You cannot really "sync" between cell evaluation and widgets. This is because the IPython kernel sends messages to the widgets, but never waits for any replies (and there is no mechanism to make it wait).

However widgets are guaranteed to be initialized in order, so you can be guaranteed that the sequentially last widget will initialize last. I use that to implement the test helpers, for example:

https://github.com/AaronWatters/jp_proxy_widget/blob/master/notebooks/notebook_tests/basic%20tests.ipynb

https://github.com/AaronWatters/jp_proxy_widget/blob/master/jp_proxy_widget/notebook_test_helpers.py

Sadly this does not result in fully automated tests -- you still have to manually open the notebook and "run all".

magland commented 6 years ago

Yeah, I read more of your tutorial, and can see why this is a necessary limitation. My workaround right now is to create the widget in one cell and then do the computation in another cell. It's working okay. You can close this issue if you want.

AaronWatters commented 6 years ago

Watch out for "cell->run all" -- the widget in cell (1) will not execute until the computation in cell (2) finishes. This is a critical design flaw with Jupyter widgets in my opinion. Jupyter was not designed with widgets in mind.

You could hack around it by mediating all computation using widgets, I think. But that would be kind of weird.

AaronWatters commented 5 years ago

This would be nice, but it's not possible in the current Jupyter infrastructure.