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

Update traitlet values before initial render of widget #4956

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

marthacryan
Copy link
Collaborator

Fixes #4933.

Source of the problem

The issue was that there were updates happening (for example, add_layout_image) before the initial call to _repr_mimebundle_, which were triggering the traitlet events like _py2js_relayout or _py2js_addTraces. These events were designed with the idea of a queue that would exist through traitlets, but anywidget (and ipywidgets in general, although for some reason this was working before the anywidget changes) expects the traitlet to be more of a single source of truth for a value, not something that has ordered changes. So by the time that the widget is initialized on the frontend, it loses all of the events that were triggered before the call to fig.show().

Solution

This updates the _repr_mimebundle_ method to set the value of the layout and data traitlets before the initial render, so that the changes that were lost in the events queue are still included.

This also changes the name of the traitlets for layout and data to _widget_layout and _widget_data because changes were being made to those fields along the way that prevented traitlets from registering a change when they're set in _repr_mimebundle.

More info on changing the traitlet names Some parts of the plotly code change specific properties of the layout traitlets rather than reassigning the entire traitlet. This meant that when we _did_ reassign the value of the traitlets, ipywidgets went to check if there were any changes and ended up deciding that there weren't because it still pointed to the object that had specific fields changed. This seems like a bug but keeping these traitlets separate from the internals of plotly and always reassigning the full value seems like a more straightforward solution.
Context on other approaches considered We considered removing the events system and simplifying the code. See #4945 for what I tried (which did work). However, this would entail a sacrifice in performance because the entire layout or data object would be sent over for any change to the traitlets. This would be much slower than sending only the deltas as we are now.

@@ -729,6 +730,8 @@ def _repr_mimebundle_(self, include=None, exclude=None, validate=True, **kwargs)
Return mimebundle corresponding to default renderer.
"""
display_jupyter_version_warnings()
self._widget_layout = deepcopy(self._layout_obj._props)
Copy link
Contributor

Choose a reason for hiding this comment

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

@marthacryan Can you add a comment explaining why these are here?

if (_.isNil(layout) || _.isNil(layout.width)) {
// @ts-ignore
Plotly.Plots.resize(that.el).then(function () {
var layout_edit_id = that.model.get("_last_layout_edit_id");
var layout_edit_id = that.model.get("_last_widget_layout_edit_id");
Copy link
Contributor

Choose a reason for hiding this comment

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

@marthacryan Shouldn't the name of this traitlet be updated on the Python side as well?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
fix fixes something broken P1 needed for current cycle
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Erratic behavior when working with multiple FigureWidget instances (after adoption of anywidget?)
3 participants