Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run all archived messages when embedded widget is uninitialized #1064

Merged
merged 5 commits into from
Jun 20, 2023

Conversation

Yoshanuikabundi
Copy link
Contributor

@Yoshanuikabundi Yoshanuikabundi commented Jun 14, 2023

This PR implements the proposal discussed in #1063.

I made two changes that weren't discussed there:

  1. _ngl_is_initialized is set in createStage, not handleEmbed, to ensure it is always set when the JS runs (but also after handleEmbed)
  2. I had to make on_msg async. This is because it is essential that each message completes before the next one is triggered to avoid things like trying to add representations to a missing component. I learnt this the hard way. My understanding is that this won't change behavior - on paths where on_msg doesn't await, execution will never be suspended and thus the function will run synchronously; on the other hand, when the function does await it's the last thing it does, so the only difference is that the on_msg function sticks around on the queue instead of the already async inner function. What it does do is allow on_msg to be awaited so that messages can be executed and completed in order. This may be of use elsewhere, I'm not sure - I feel like NGLView does sometimes "forget" something I told it.

I also added the jupyterlab~=3.0 pin to the Conda environment as discussed.

I have a lot of files that have been generated by NPM (or something), including three that are already checked in - so I've added them in a separate commit in case this is a mistake of some sort.

	modified:   nglview/static/index.js
	modified:   nglview/static/index.js.map
	modified:   nglview/staticlab/package.json

Hope this is useful! I think from what I've learned I can hack something together for my cookbook even if this doesn't get merged, so thanks for your help!

Closes #1063

@hai-schrodinger
Copy link
Contributor

Does this PR actually work as expected?

@Yoshanuikabundi
Copy link
Contributor Author

Yep, seems to! I was surprised too 😂 I made a notebook that creates a widget, clears it's representations and adds new ones. It appears correctly when executed either with jupyter nbconvert --execute --to html notebook.ipynb or in JupyterLab. I did have to manually copy over the compiled JavaScript file but my understanding is thats just how the whole embedded widget packaging thing works. I'll put together a more comprehensive demo today.

@Yoshanuikabundi Yoshanuikabundi changed the title Run all archived messages when embedded widget is unitialized Run all archived messages when embedded widget is uninitialized Jun 15, 2023
@Yoshanuikabundi
Copy link
Contributor Author

Sorry Hai, I'm not going to have time to put that more comprehensive demonstration together this week. Here's the test I was developing off of, along with the compiled JS and the converted HTML file - let me know what more you need to review this!

pr-demo.zip

console.log("No state stored; initializing embedded widget for the first time.");
for (const msg of that.model.get("_ngl_msg_archive")) {
console.log("Running msg " + JSON.stringify(msg));
await that.on_msg(msg);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should update await for other places using on_msg(msg) too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uhmmm possibly. I didn't make this sort of change to avoid changing behaviour. The other places calling on_msg are:

l450 (in handleEmbed()):

        // fire any msg with "fire_embed"
        that.model.get("_ngl_msg_archive").forEach(function(msg){
            if (msg.fire_embed){
                that.on_msg(msg);
            }
        })

This should probably be awaited - it's in an async function already and its possible that _set_representation_from_repr_dict could try to set a representation on a component added here? The comment above _set_representation_from_repr_dict seems to suggest that completing all these messages is a requirement, and completing them out of order could mean assigning the wrong representations. (I will make this change shortly)

l152 (in handleMessage):

        this.model.on("msg:custom", function(msg){
           this.on_msg(msg);
        }, this);

From what I can google, ipywidgets/backbone don't seem to treat async callbacks any different to regular callbacks, so making the callback function async and adding an await here wouldn't do anything.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it. thanks.

@@ -398,6 +399,16 @@ class NGLView extends widgets.DOMWidgetView{
async handleEmbed(){
var that = this;
var ngl_msg_archive = that.model.get("_ngl_msg_archive");

if (!that.model.get("_ngl_is_initialized")) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we check for ngl_stage_params instead?

in another line:
var ngl_stage_params = that.model.get('_ngl_full_stage_parameters');

Basically if the ngl is not initialize, check for empty of something?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or check for both ngl_full_stage_parameters and _camera_orientation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! It looks _ngl_full_stage_parameters object is already modified in createStage, so I've just made this a shallow copy so that changes aren't reflected in the shared state until we deliberately sync them back up. I think this is more robust to future changes than checking if _ngl_full_stage_parameters is the value it is modified to.

@hainm
Copy link
Collaborator

hainm commented Jun 15, 2023

I have a lot of files that have been generated by NPM (or something), including three that are already checked in - so I've added them in a separate commit in case this is a mistake of some sort.

	modified:   nglview/static/index.js
	modified:   nglview/static/index.js.map
	modified:   nglview/staticlab/package.json

yeah, those files are intentional to be included in nglview folder so we don't need npm to install the dev nglview python package.

@Yoshanuikabundi
Copy link
Contributor Author

OK I've addressed those points! One thing I've noticed is that js/package.json still thinks its 3.0.1 - does that need to be updated?

@hainm
Copy link
Collaborator

hainm commented Jun 19, 2023

One thing I've noticed is that js/package.json still thinks its 3.0.1 - does that need to be updated?

I will do when doing release.

js/src/widget_ngl.ts Outdated Show resolved Hide resolved
Yoshanuikabundi added a commit to openforcefield/openff-docs that referenced this pull request Jun 20, 2023
The modified JS file introduced in this commit is from:
nglviewer/nglview#1064
Once that PR makes it into a release, the logic and JavaScript added
in this commit should be replaced with a pin to the appropriate
version of NGLView.
@hainm hainm merged commit 451e82c into nglviewer:master Jun 20, 2023
@hainm
Copy link
Collaborator

hainm commented Jun 20, 2023

Thanks @Yoshanuikabundi very much for your contribution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support nbconvert --execute
3 participants