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 13 forks source link

Detachable / floating jp_proxy_widget example? #8

Open psychemedia opened 4 years ago

psychemedia commented 4 years ago

Do you know of any examples of detaching a jp_proxy_widget instance so that it can be detached from the output area of the cell that displays it so that it can float or be dragged around the notebook? (This makes the widget more useful it it is accessed / referenced from multiple other cells in a notebook.)

An example in this ipywidgets issue using the following approach to display a floating panel at the top of the notebook:

display(Javascript("""$('div.job_widget')
        .detach()
        .appendTo($('#header'))
        .css({
            'z-index': 999,
             'position': 'fixed',
            'box-shadow': '5px 5px 5px -3px black',
            'opacity': 0.95,
            'float': 'left,'
        })
        """))

It's easy enough to add the css attributes to the widget, but is there a simple way of detaching it and appending it to the notebook $('#header')?

AaronWatters commented 4 years ago

Look here

https://github.com/AaronWatters/jp_doodle/blob/master/notebooks/workshop/3%20-%20Silly%20example.ipynb

demo.in_dialog()

Here is the implementation:

https://github.com/AaronWatters/jp_doodle/blob/master/jp_doodle/dual_canvas.py#L563

I think this should work with other proxy widgets. Let me know if this is not what you had in mind.

psychemedia commented 4 years ago

Ah, that demo looks like it could be exactly what I need. Will give it a go. Thanks..

PS ..element.dialog() does the job nicely.... Thanks:-)

psychemedia commented 4 years ago

Just following on from that, I'm now wondering if there's any easy way to also pull in some of the jquery.dialogextend.js features, such as the ability to launch full screen view, or collapsed view?

eg it'd be nice to be able to say something like:

widget.element.dialog().dialogExtend({"closable":True,
"maximizable" :True,
"minimizable" :True,
"collapsable" :True})

etc.?

AaronWatters commented 4 years ago

Hi Tony,

Sorry for the delayed reply. I added this as a demo to the jp_doodle repository (I don't like to clutter up jp_proxy_widgets, so I put miscellaneous demos in jp_doodle).

Please look here

http://localhost:8889/notebooks/repos/jp_doodle/notebooks/misc/JQueryUI%20dialogextend%20plugin%20demo.ipynb

And let me know what you think.

psychemedia commented 4 years ago

@Aaron-Watters that looks like just what I need.... thanks...

My current active tinkering is in this context... Will give the extension a go next time I'm back at the computer (bank holiday tomorrow, and I'm trying to get into the habit of avoiding screens on holidays and weekends... Though I am really itching to try this... :-)

I'd overlooked the misc dir before. Will also have a good look through those other examples to see if they help me figure out a couple of outstanding things I'd like to be able to do (my model of how py and js interact is still hazy...)

Thanks again..

AaronWatters commented 4 years ago

very cool! Thanks for the link.

psychemedia commented 4 years ago

I seem to have the same issue as you that calling from py doesn't seem to work...

I also get really erratic behaviour... when I try things in a brand new notebook, things work fine. But if I am working in a legacy notebook, or sometimes close the widget then reopen it (which works normally), I get a new error message: TypeError: element.dialog(...).dialogExtend is not a function error. There is something I fundamentally don't understand about require.js, how packages are loaded into a browser, and what scope they have.

PS Ah, okay, so it seems I have to run the loader cell, and display the loader widget, and then in a new cell run the code to instantiate the widget I actually want to run the .dialogExtend() on...

AaronWatters commented 4 years ago

I suspect the problem is this:

The code that installs the jquery plugin needs to re-run every time you open a new javascript context. This is one of the confusing things about the notebook interfaces -- it's not always clear what has been run in the current context and what was run at some previous time and stored away in the notebook document.

psychemedia commented 4 years ago

Yeah, I reckon that must be it... it's a real faff.... is there a way I could dig deeper into the jp_proxy_widget code hack a requirement that the dialogExtend package is loaded when I try to pop out the widget? (So I guess require dialogExtend is available before the widget is created?)

I guess a similar issue is if I try to simply call a function in js widget from py magic? I can't get my head round the contexts properly, because the examples I tried resulted in an error that the js function wasn't defined even though it's there and being used in the widget (I guess I need to create my own set of really simple recipes that I can refer to... the context I'm working in is getting more and more complex!)

(Just as a by the by, I don't suppose you (know if) anyone hacked the tensorflow playground or any other simple NN demos into into an ipywidget using jp_proxy_widget? Also, do you have a demo of a widget js function using a py function to do some work, so eg js function calls a py function, then py function returns a result to the js widget?)

psychemedia commented 4 years ago

I started wondering whether there's a route to load an arbitrary required js package in before rendering the widget, but I guess this needs an async load success callback and I couldnlt spot one offhand.

eg this recipe:

roboSim = eds.Ev3DevWidget()
def show_robosim():
    display(roboSim)
    roboSim.element.dialog().dialogExtend({
        "maximizable" : True,
        "dblclick" : "maximize",
        "icons" : { "maximize" : "ui-icon-arrow-4-diag" }});

roboSim.check_jquery(force=True, onsuccess=show_robosim(),
                     code_fn=eds.get_file_path('js/jquery.dialogextend.js'))

where Ev3DevWidge extenda 'jp_proxy_widget.JSProxyWidget, renders the widget and then a few moments later displays

new error message: timeout awaiting /srv/conda/envs/notebook/lib/python3.7/site-packages/nbev3devsim/js/jquery.dialogextend.js

So the onsuccess callback in proxy_widget.py is presumably success at queuing up the async load, not success in completing the load?

AaronWatters commented 4 years ago

Sorry for the delay.

First observation: you probably don't want to call show_robosim in check_jquery because it should be called when the check is complete, not before: check_jquery(force=True, onsuccess=show_robosim,...

Aside from that I'm not sure what is going on here. My initial reaction would be to open the Dev Tools in Chrome and see if there are any errors in the Javascript console.

psychemedia commented 4 years ago

No probs - I'm working across multiple fronts at the moment!

So how do you call something after the check is complete (onsuccess seems not to mean on the success of the check_jquery() call?

I've actually sort of got round the problem by adding the required js package load into the load procedure of a notebook extension I'm also loading in to support some of widget activities.

psychemedia commented 4 years ago

Note to self - to wait for the package to load, is it simply a case of:

require( [ 'js/jquery.dialogextend.js' ], function () {
         show_robosim();
    });
psychemedia commented 2 years ago

An alternative way of rendering a widget ins a separate panel in JupyterLab is to use the jupyter-widgets/jupyterlab-sidecar extension, which now allows you to display the output of a code cell in a separate, detached panel:

image

Whilst I can get this demo to run in Binder environment launched from this repo (with sidecar then installed and the JupyterLab browser page refreshed), if I try to install the jp_proxy_widget package into the Binder environment launched from the sidecar repo, it fails with an Error displaying widget: model not found error (related issue: https://github.com/AaronWatters/jp_proxy_widget/issues/27).

The jp_proxy_widget extension needs installing...

!jupyter labextension install jp_proxy_widget