I used the ipywidgets to define a customed component in jupyter notebook, but when I adding interaction to my component, it can't work.The component class is:
from __future__ import print_functionimport ipywidgets as widgets# from traitlets import Unicode, validateimport traitletsclass VerticalSliderModel(widgets.DOMWidget): _view_name = traitlets.Unicode('VerticalSliderView').tag(sync=True) _view_module = traitlets.Unicode('vertical_slider').tag(sync=True) value0 = traitlets.Float(250, min=0, max=1000).tag(sync=True) color0 = traitlets.Unicode('#0000FF').tag(sync=True) value1 = traitlets.Float(600, min=0, max=1000).tag(sync=True) color1 = traitlets.Unicode('#0000FF').tag(sync=True) value2 = traitlets.Float(750, min=0, max=1000).tag(sync=True) color2 = traitlets.Unicode('#0000FF').tag(sync=True) _min = traitlets.Float(0.0).tag(sync=True) _max = traitlets.Float(1000.0).tag(sync=True) value = 2000
And the View coded by js is (The important part is 'update the value2'):
%%javascriptrequire.undef('vertical_slider');define('vertical_slider', ["@jupyter-widgets/base"], function(widgets) { var VerticalSliderView = widgets.DOMWidgetView.extend({ initialize: function() { this.axis = null; this.sliders = []; this.valueLabels = []; this.colorPickers = []; this.isDragging = []; this.startY = []; this.colorPickers = []; for (var i = 0; i < this.nSliders; i++) { this.isDragging.push(false); } }, render: function() { this.axis = document.createElement('div'); this.axis.className = 'line'; this.el.appendChild(this.axis); this.nSliders = 3; for (var i = 0; i < this.nSliders; i++) { this.createSlider(i, i*200); } createSlider: function(index, initialValue) { console.log("initialValue:", initialValue); var slider = document.createElement('div'); slider.id = 'slider'+ index; slider.className = 'slider'; console.log("slider:this.axis.offsetHeight:", this.axis.offsetHeight); console.log("slider.offsetHeight:", slider.offsetHeight); slider.style.top = (initialValue / 1000) * 200 +'px'; console.log("slider.style.top:", slider.style.top); this.axis.appendChild(slider); var valueLabel = document.createElement('div'); valueLabel.className = 'value-label'; valueLabel.id = 'value'+ index; valueLabel.textContent = initialValue; slider.appendChild(valueLabel); var colorPicker = document.createElement('input'); colorPicker.type = 'color'; colorPicker.style.display = 'none'; this.el.appendChild(colorPicker); this.sliders[index] = slider; this.valueLabels[index] = valueLabel; this.colorPickers[index] = colorPicker; slider.addEventListener('mousedown', this.startDrag.bind(this, index)); slider.addEventListener('click', this.changeColor.bind(this, index)); }, startDrag: function(index, e) { console.log(this.sliders); console.log("index:", index); console.log("e:", e); this.isDragging[index] = true; this.startY[index] = e.clientY - this.sliders[index].offsetTop; document.addEventListener("mousemove", this.move.bind(this, index)); document.addEventListener("mouseup", this.up.bind(this, index)); }, move: function(index, e) { if (!this.isDragging[index]) return; const minPos = index === 0 ? 0 : this.sliders[index-1].offsetTop + 10; const maxPos = index === this.sliders.length - 1 ? this.sliders[index].parentNode.offsetHeight - this.sliders[index].offsetHeight : this.sliders[index + 1].offsetTop - 10; if(pos < minPos) pos = minPos; if(pos > maxPos) pos = maxPos; let minValue = 0; let maxValue = 1000; let valuePerPixel = (maxValue - minValue) / 200; let newValue = Math.round(minValue + pos * valuePerPixel); if(newValue > 1000) newValue = 1000; if(newValue < 0) newValue = 0; this.sliders[index].style.top = Math.min(Math.max(pos, minPos), maxPos) +'px'; //----------------update the value2-------------------------------- this.model.set('value2', newValue); console.log("this.model.value+:", this.model); //----------------------------------------------------------------- this.valueLabels[index].textContent = newValue; this.touch(); }, up: function(index, e) { document.removeEventListener("mousemove", this.move.bind(this, index)); document.removeEventListener("mouseup", this.up.bind(this, index)); this.isDragging[index] = false; this.colorPickers[index].style.display = 'none'; }, changeColor: function(index) { event.preventDefault(); event.stopPropagation(); if (this.isDragging[index]) return; this.colorPickers[index].style.display = "inline"; this.colorPickers[index].value = this.sliders[index].style.backgroundColor; console.log("colorPicker.value:", this.colorPickers[index].value); this.colorPickers[index].addEventListener("change", this.applyColor.bind(this, index)); }, applyColor: function(index) { this.model.set('color'+ index, this.colorPickers[index].value); console.log('color'+ index, this.colorPickers[index].value); this.colorPickers[index].style.display = "none"; this.sliders[index].style.backgroundColor = this.colorPickers[index].value; this.touch(); }, }); return { VerticalSliderView: VerticalSliderView };});
Then I adding interaction using 'observe'. What I want is, as the value2 changes, the 'update_plot' function will be excuted, so the plot will be updated. However, when value2 changes(see the js code, update the value2), the plot won't update. When I print, I find the value2 in update_plot is no change.
# out = widgets.Output()def update_plot(change):# with out: plt.clf() value = change['new'] print(value) fig, ax = plt.subplots() x = np.linspace(0, value * np.pi) line, = ax.plot(x, np.sin(x)) line.set_ydata(np.sin(2 * x))# plt.draw() vertical_slider = VerticalSliderModel()vertical_slider.observe(update_plot, names='value2')display(vertical_slider)update_plot({'new': vertical_slider.value2})
I want the plot will be updated when i change the value2.