Résumé
Voir l'exemple de l'Azure notebook hébergé à ce lien . Le bloc-notes peut être cloné et exécuté, ou téléchargé et exécuté localement, à partir de là, mais tout le code est également ci-dessous pour plus de commodité.
Lorsque toutes les cellules sont exécutées, la console javascript signale ces erreurs (abrégées) dans la dernière cellule, et la dernière ligne de sortie attendue ne s'affiche pas :
Error: Could not create a view for model id 91700d0eb745433eaee98bca2d9f3fc8
at promiseRejection (utils.js:119)
Error: Could not create view
at promiseRejection (utils.js:119)
Uncaught (in promise) TypeError: Cannot read property 'then' of undefined
Uncaught (in promise) TypeError: Cannot read property 'then' of undefined
Je ne sais pas où je me trompe.
UPDATE :
Tel qu'il existe actuellement, le code envoie une instance de chaîne (plutôt qu'une DOMWidgetModel
) à la create_child_view
méthode. La chaîne de caractères contient "IPY_MODEL_"
complété par l'identifiant du modèle. Cela semble être la racine du problème. Cette instance de chaîne est reçue par le client depuis le Backbone côté serveur. children
éléments du tableau du modèle ( this.model.get('children')
).
Je me demande si le problème n'est pas lié à la [dé]sérialisation des widgets dont il est question dans l'avis de la Commission européenne. tutoriel sur les widgets de bas niveau . Mais je ne suis pas sûr de savoir comment l'utiliser pour résoudre ce problème, puisque j'ai besoin d'accéder au modèle du sous-widget lui-même et pas seulement à un attribut. Et je pense que je passe correctement le **widgets.widget_serialization
comme le précise le tutoriel.
Détails
Le carnet de notes contient du code python et javascript, et utilise l'application ipywidgets
qui s'appuie largement sur Backbone. Le code dorsal (python, cellule #1) crée un fichier ipywidgets.DOMWidget
sous-classe widget, Test
(un modèle Backbone reflété dans le front-end). Le code frontal (javascript, cellule #2) crée un fichier ipywidgets.DOMWidgetView
sous-classe, TestView
qui est instancié par le widget lorsqu'il est rendu dans la page.
El Test
Le widget de modèle a un children
composé de plusieurs "sous-widgets" (qui sont aussi des modèles). Ces widgets sont des instances de la classe python Sub
. Lorsqu'une vue de Test
est rendu, je veux instancier et rendre les vues des widgets enfants et les attacher à la vue du parent Test
(note : cette dernière partie n'a pas encore été implémentée ci-dessous).
Le problème est que lorsque j'essaye de suivre les ipywidgets
pour créer des vues enfants, en alimentant le ViewList
en instanciant les vues enfants à l'aide de la fonction create_child_view
sur chaque modèle enfant ne fonctionne pas.
L'API pour ce genre de choses n'est pas particulièrement bien documentée, donc je fais de mon mieux pour suivre divers exemples similaires sur la façon d'instancier des sous-vues en utilisant des modèles enfants à partir d'une vue parent, comme les widgets parents. sur ipywidgets
lui-même et sur ipyleaflet
. Mais rien ne me permet de faire fonctionner la création de vues d'enfants.
Notez que je suis en mesure de rendre une vue de chaque Sub
widget individuellement sans aucun problème. Ce n'est que lorsque j'essaie d'utiliser le create_child_view
pour créer une vue à partir du parent Test
widget que nous rencontrons des problèmes.
Code
Cellule 1 (côté serveur jupyter python noyau)
import ipywidgets.widgets as widgets
from traitlets import Unicode, List, Instance
from IPython.display import display
class Sub(widgets.DOMWidget):
"""Widget intended to be part of the view of another widget."""
_view_name = Unicode('SubView').tag(sync=True)
_view_module = Unicode('test').tag(sync=True)
_view_module_version = Unicode('0.1.0').tag(sync=True)
class Test(widgets.DOMWidget):
"""A parent widget intended to be made up of child widgets."""
_view_name = Unicode('TestView').tag(sync=True)
_view_module = Unicode('test').tag(sync=True)
_view_module_version = Unicode('0.1.0').tag(sync=True)
children = List(Instance(widgets.Widget)).tag(sync=True,
**widgets.widget_serialization)
def __init__(self, subs):
super().__init__()
self.children = list(subs)
Cellule 2 (code frontal du carnet de notes de Jupyter)
%%javascript
require.undef('test');
define('test', ["@jupyter-widgets/base"], function(widgets) {
var SubView = widgets.DOMWidgetView.extend({
initialize: function() {
console.log('init SubView');
SubView.__super__.initialize.apply(this, arguments);
},
render: function() {
this.el.textContent = "subview rendering";
},
});
var TestView = widgets.DOMWidgetView.extend({
initialize: function() {
console.log('init TestView');
TestView.__super__.initialize.apply(this, arguments);
this.views = new widgets.ViewList(this.add_view, null, this);
this.listenTo(this.model, 'change:children', function(model, value) {
this.views.update(value);
}, this);
console.log('init TestView complete');
},
add_view: function (child_model) {
// error occurs on this line:
return this.create_child_view(child_model);
},
render: function() {
this.views.update(this.model.get('children'));
this.el.textContent = 'rendered test_view';
},
});
return {
SubView : SubView,
TestView : TestView,
};
});
Cellule 3 (code python pour les tests)
models=[Sub() for _ in range(4)]
for m in models:
# view each Sub object individually
display(m) # output: 'subview rendering'
t=Test(models)
t # output: 'rendered test_view' <-- broken; see console log
Sortie
Sortie de courant :
subview rendering
subview rendering
subview rendering
subview rendering
Sortie attendue :
subview rendering
subview rendering
subview rendering
subview rendering
rendered test_view
Des informations plus spécifiques sur le projet sur lequel je travaille sont disponibles à l'adresse suivante ce problème github si quelqu'un est intéressé.