I would like to listen to cell events of JupyterLab notebooks (version 3.2.0) in a custom extension. How can I do so?
a) Searching the documentation on "events" did not give helpful information:
https://jupyterlab.readthedocs.io/en/latest/search.html?q=events&check_keywords=yes&area=default
b) Here is some outdated example I could find:
Jupyter.events.on('execute.CodeCell', function(evt, data) {
// data.cell is the cell object
});
https://github.com/jupyter/notebook/issues/2321
c) Here is another outdated code snipped:
require([
"base/js/namespace",
"base/js/events"
],
function(Jupyter, events){
console.log("hello2");
events.on('notebook_loaded.Notebook', function(){
console.log("hello3");
});
events.on('app_initialized.NotebookApp', function(){
console.log("hello4");
});
});
https://github.com/jupyter/notebook/issues/2499
d) I would expect to use something like
app.events
or
var { Events } = require('#jupyterlab/events');
However, those variants did not work.
Edit
I found another code snippet:
panel.notebook.model.cells.changed.connect((list, changed) => {
if (changed.type == 'add') {
each(changed.newValues, cellmodel => {
let cell = panel.notebook.widgets.find(x => x.model.id === cellmodel.id);
// do something with cell widget.
});
}
});
https://github.com/jupyterlab/jupyterlab/issues/4316
Maybe there is no global "event registry" any more, but the events need to be accessed via the model property?
Related:
Where is a docs for Jupyter front-end extensions JavaScript API?
JupyterLab Extensions should listen to application-related events using the signal pattern which is based on lumino Signal implementation.
You can identify the signal of interest in the reference JupyterLab API Reference (which is also linked from the documentation as the last entry), for example, cell execution signal is available as NotebookActions.executed.
Extension Example
https://github.com/jupyterlab/extension-examples/tree/master/signals
Signals of NotebookActions
executed
executionScheduled
seletionExecuted
Signal of observable list
changed
Signals of individual cells
The cell model provides the signals
contentchanged
statechanged
mimetypechanged
Usage example:
function __observeNotebook(app, dependencies){
let notebook = __tryToGetNotebook(app);
if(notebook){
let cellModels = notebook.model.cells
cellModels.changed.connect(__cellsChanged, this);
for(let cellIndex=0; cellIndex < cellModels.length; cellIndex++){
let cellModel = cellModels.get(cellIndex);
__observeCell(cellModel, notebook);
}
let notebookActions = dependencies["NotebookActions"];
notebookActions.executed.connect(__cellExecuted, this); //selectionExecuted, exutionScheduled
}
}
function __observeCell(cellModel, notebook){
cellModel.contentChanged.connect(cellModel => __cellContentChanged(cellModel, notebook), this);
cellModel.stateChanged.connect(__cellStateChanged, this);
}
function __cellsChanged(cellModels, change){
console.log("Cells changed:")
console.log("type: " + change.type);
console.log("oldIndex: " + change.oldIndex);
console.log("newIndex: " + change.newIndex);
console.log("oldValues: " + change.oldValues);
console.log("newValues: " + change.newValues);
if(change.type == "add"){
var newCellModel = cellModels.get(change.newIndex);
__observeCell(newCellModel);
}
}
function __cellContentChanged(cellModel, notebook){
let id = cellModel.id
console.log("Content of cell " + id + " changed");
let currentText = cellModel.value.text;
console.log(currentText);
let cellWidget = notebook.widgets.find(widget=>{
return widget.model.id == id;
});
let outputArea = cellWidget.outputArea;
let children = outputArea.node.children;
if(children.length==1){
let output = children[0];
console.log(output);
}
}
function __cellStateChanged(cellModel, change){
let currentText = cellModel.value.text;
console.log("State of cell " + cellModel.id + " changed:");
console.log("name: " + change.name);
console.log("old value: " + change.oldValue);
console.log("new value: " + change.newValue);
}
function __cellExecuted(any, context){
let {cell, notebook, success, error} = context;
console.log("Executed cell " + cell.model.id);
}
function __tryToGetNotebookCell(app){
var notebook = __tryToGetNotebook(app);
return notebook
?notebook.activeCell
:null;
}
function __tryToGetNotebook(app){
var notebookPanel = __getFirstVisibleNotebookPanel(app);
return notebookPanel
?notebookPanel.content
:null;
}
function __getFirstVisibleNotebookPanel(app){
var mainWidgets = app.shell.widgets('main');
var widget = mainWidgets.next();
while(widget){
var type = widget.sessionContext.type;
if(type == 'notebook'){ //other wigets might be of type DocumentWidget
if (widget.isVisible){
return widget;
}
}
widget = mainWidgets.next();
}
return null;
}
Related
I have a zone related problem, and i can't get it working. i'm new to angular but have some basic javascript understanding.
So what is the problem, i can not get the view updated.
Im using a third party library for ionic BLE, and use this in a provider
providers/communication-controller.ts
set_ble_tolisten(){
//subscribe to receive notification, rx channel
console.log(' set_ble_tolisten' + NgZone.isInAngularZone());
this.ble.startNotification(this.peripheral_object.id,
this.TRANSPARANT_SERVICE, this.TX_CHARACTERISTIC).subscribe(
(data) => this.handlereceiveddata(data),
() => console.log('Unexpected Error', 'Failed to subscribe for
temperature changes')
)
}
ble_communication_controller(port,command,payload,payloadsize,
callback)
{
//create buffer
var BLE_Sendbuffer= new Int8Array(payloadsize+10);
var package_index = 0;
this.generatePacketID();
this.ble_send_que.push({
"packetid" : this.packetid,
"action" : callback
});
//Generate send message
BLE_Sendbuffer[ some data]
//send message
this.ble.write(this.peripheral_object.id,this.TRANSPARANT_SERVICE,
this.RX_CHARACTERISTIC,BLE_Sendbuffer.buffer).then(
() => console.log('ble message send'),
e => console.log('ble oops')
);
}
handlereceiveddata(buffer:ArrayBuffer){
var int8View = new Int8Array(buffer);
var ReceivedPacketID = 0;
console.log('handlereceiveddata ' + NgZone.isInAngularZone());
//first check the header
if( ((int8View[0] * 0x100) + int8View[1]) === this.HEADER)
{
//retrieve packetid from received packet
ReceivedPacketID = ((int8View[2] * 0x100) + int8View[3]);
if(ReceivedPacketID === 0) {
console.log('orgin io or rs232');
} else {
//find function based on packetid, and execute
let index_findresult = this.ble_send_que.findIndex(value =>
value.packetid === ReceivedPacketID);
if(index_findresult != -1)
{
this.ble_send_que[index_findresult].action(int8View);
}
//remove object from array
this.ble_send_que.splice(index_findresult, 1);
}
}
set_ble_tolisten() is called to subcribe on the promise.When data is received handlereceiveddata() is called.
To transmit data ble_communication_controller() is called, this function accepts a callback, which is stored. When the ble device response, the handlereceivedata() is called. This then call the callback which is stored a the ble_communication_controller() call
I created a page, configuration, this contains a ts a html and scss file
the configuration.ts
ionViewDidEnter() {
this.communicationController.set_ble_tolisten();
this.sync_device_gui('IO-1');
}
sync_device_gui(port){
console.log('call in zone ' + NgZone.isInAngularZone());
var io_port = 0;
if(port === ‘IO-1')
{
io_port = 0x05;
}
if(port === 'IO-2')
{
io_port = 0x06;
}
this.communicationController.ble_communication_controller(io_port,
GET_IO_STATE,0x00,0,this.update_state_gui);
}
update_state_gui(data){
//rule below added as explained in first comment (location ngzone.run).
this.zone.run(() => {
console.log('repsonse in zone ' + NgZone.isInAngularZone());
console.log('io port state ' + data);
console.log('repsonse in zone ' + NgZone.isInAngularZone());
if(data[8] == 0){
this.relay_1_toggle = true;
this.relay_2_toggle = true;
console.log('state is false set :'+ this.relay_1_toggle + '
state :' + this.relay_2_toggle );
}
if(data[8] == 1){
this.relay_1_toggle = false;
this.relay_2_toggle = false;
console.log('state is true set :'+ this.relay_1_toggle + '
state :' + this.relay_2_toggle );
}
//rule below added as explained in first comment.(location ngzone.run).
});
//ruke below added as explained in first commen. (location markForCheck())
this.cd.markForCheck();
}
when the page is loaded, ionViewDidEnter is called, the problem is that update_state_gui is called from the provider. the data receives in this function. isInAngularZone, tells me that it is false, out of zone. The
relay_1_toggle and relay_2_toggle are toggle button in html.
And the get not updated, i tried ngzone.run or a timeout and markForCheck(). but it does not work.
Thanks for your help
I have the following function below which does a xmlhttprequest based in two targets which i get from the text of certain html elements.
All I need to do is switch the target values around, but without calling them each time in the if statement. Trying to follow the DRY method.
See below -
if(whichSym === "1"){
document.getElementById('priceSym' + whichSym).innerHTML = target;
target2 = document.getElementById('priceSym2').innerHTML;
dataControl.getItem(function(err, data) {
uiControl.changeInput("1", data);
}, target, target2);
}
else if(whichSym === "2"){
document.getElementById('priceSym' + whichSym).innerHTML = target;
target2 = document.getElementById('priceSym1').innerHTML;
dataControl.getItem(function(err, data) {
uiControl.changeInput("1", data);
}, target2, target);
}
Please let me know if you need more context.
To follow the DRY principle you can simply wrap it in a function. Something like this should work:
function (whichSym) {
const otherSym = whichSym === 1 ? 2 : 1; // assuming values can be only 1 and 2
const target = document.getElementById('priceSym' + whichSym).innerHTML;
const target2 = document.getElementById('priceSym' + otherSym).innerHTML;
dataControl.getItem((err, data) => {
uiControl.changeInput("1", data); // not sure what "1" here means, replace with whichSym if needed
}, target, target2);
}
I have the following function which selects a category from a list of available categories. This function works fine in my first test. But the same function with a different valid category name in my second test fails with the following error.
Error: Index out of bound. Trying to access element at index: 0, but there are only 0 elements that match locator By.cssSelector(".grid-view-builder__category")
this.categoryElements = element.all(by.css('.grid-view-builder__category'));
this.selectCategory = function (categoryName) {
var filteredCategories = this.categoryElements.filter(function (category) {
return category.getText().then(function (text) {
log.info(text);
return text === categoryName;
})
})
filteredCategories.first().click().then(function () {
log.info("Select Category: " + categoryName);
}).then(null, function (err) {
log.error("Category: " + categoryName + " Not Found !!" + err);
});
}
Spec File
var columnSelect = require('pages/grid/columns/columnselector-page')()
it('Add Publisher ID Column to the Grid & Verify', function () {
var columnCountBefore = columnSelect.getColumnCount();
columnSelect.openColumnSelector();
columnSelect.selectCategory('Advanced');
columnSelect.selectColumn('Publisher ID');
columnSelect.apply();
var columnCountAfter = columnSelect.getColumnCount();
expect(columnCountAfter).toBeGreaterThan(columnCountBefore);
});
The problem might be in the way you are defining and using Page Objects. Here is a quick solution to try - if this would help, we'll discuss on why that is happening.
Make the categoryElements a function instead of being a property:
this.getCategoryElements = function () {
return element.all(by.css('.grid-view-builder__category'));
};
this.selectCategory = function (categoryName) {
var filteredCategories = this.getCategoryElements().filter(function (category) {
return category.getText().then(function (text) {
log.info(text);
return text === categoryName;
})
})
filteredCategories.first().click().then(function () {
log.info("Select Category: " + categoryName);
}).then(null, function (err) {
log.error("Category: " + categoryName + " Not Found !!" + err);
});
}
Or, this could be a "timing issue" - let's add an Explicit Wait via browser.wait() to wait for at least a single category to be present:
var EC = protractor.ExpectedConditions;
var category = element(by.css('.grid-view-builder__category'));
browser.wait(EC.presenceOf(category), 5000);
It looks like this has nothing to do with the code posted here, only that the css selector you're using is not finding any elements
Good day.
My question is quite dumb, I guess, but I'm not familiar enough with the termins, to ask it properly (and to get an answer from Google).
So - please help...
Shortly - I'm trying to create some major class, which will be insanitated by instances, which will describe some methods, and some fields.
Major logick will be implemented in parent class.
So, lets say I have a parent
function CRUD_Grid_model(){
//Settings part
this.GridElement = "" ;
this.editModeFlagElement = "" ;
this.newRowElement = "";
//Save logicks all lies here.
this.commit = function (){
alert("PLEASE REDEFINE COMMIT FUNCTION IN YOUR CODE");
}
;
//Settings part
//Some more code.
}
And a way I'll use it
//Das modell
var JobberCRUD = new CRUD_Grid_model();
JobberCRUD.GridElement = $('#jobbers_dg');
JobberCRUD.editModeFlagElement = $('#jobbers_tb_edit');
JobberCRUD.newRowElement = {jobb_name:'Enter new unique name',jobb_status:'Y'};
JobberCRUD.commit = function (){
if (this.endEditing()){
var addrows = this.GridElement.datagrid('getChanges','inserted');
var remrows = this.GridElement.datagrid('getChanges','deleted');
var updrows = this.GridElement.datagrid('getChanges','updated');
console.log(addrows);
console.log(remrows);
console.log(updrows);
//Send changes?
alert("Got total of " +addrows.length + remrows.length + updrows.length + " rows changed.");
//Commit changes at local level
this.GridElement.datagrid('acceptChanges');
}
};
And, what I'd like to do, is smoething like this
I want a parent.commit function to allow me to do this in child
JobberCRUD.commit = function (apdrows,updrows,remrows){
//Send changes?
alert("Got total of " +addrows.length + remrows.length + updrows.length + " rows changed.");
};
So, I have no ideas what shoudl I do to achieve that. Please advice me with some tags, what it is at least :)
Thanks in advance.
There is not exactly what you need in JavaScript, but I would tend to use this pattern :
function CRUD_Grid_model() {
...
this.onCommit = null;
this.commit = function (){
if (this.endEditing()){
var addrows = this.GridElement.datagrid('getChanges','inserted');
var remrows = this.GridElement.datagrid('getChanges','deleted');
var updrows = this.GridElement.datagrid('getChanges','updated');
if(this.onCommit != null) this.onCommit(addrows,updrows,remrows);
this.GridElement.datagrid('acceptChanges');
}
}
...
}
var JobberCRUD = new CRUD_Grid_model();
JobberCRUD.onCommit = function(apdrows,updrows,remrows) {
alert("Got total of " +addrows.length + remrows.length + updrows.length + " rows changed.");
};
JobberCRUD.GridElement = ...
Javascript doesn't have a built in notion of interfaces. A javascript object effectively "implements an interface" by having all the needed methods defined, but there's no language "interface" construct
You could use "extends" like so:
Object.prototype.extends = function(clazz) {
var o = new clazz();
for (var f in o) {
if(f === "extends") {
continue;
}
this[f] = o[f];
}
this.super = o;
}
Now could type something like this:
var JobberCRUD = function() {
this.extends(CRUD_Grid_model);
var privateFunction = function() {
//...
}
// You probably don't want to do that,
// but you could override
this.commit = function() {
//...
}
// ...
}
Hope, that helps.
Edit: forgot, that you may call super.commit() then.
You can not have something similar like C# or Java interface or even class with Javascript.
Javascript is just a dynamic scripting language.
LightSwitch tables in play:
ProductVariant
ProductVariantDetail (many to one ProductVariant)
Code:
myapp.AddEditProduct.Id_render = function (element, contentItem) {
var detailArray = new Array();
//contentItem.screen.getProductVariantDetails().then( function(result) {
// for (var i in result.data) {
// if (result.data[i].ProductVariant.Id == contentItem.value) {
// detailArray.push(result.data[i].InventoryDetail.Title);
// }
// }
// $("<div>" + detailArray.join(" / ") + "</div>").appendTo($(element));
//});
var filter = "ProductVariant eq " + contentItem.value;
myapp.activeDataWorkspace.ApplicationData.ProductVariantDetails.filter(filter).
execute().then(function (result) {
for (var i in result.results) {
detailArray.push(result.results[i].InventoryDetail.Title);
}
}).then(function() {
$("<div>" + detailArray.join(" / ") + "</div>").appendTo($(element));
});
};
The commented out code goes through EVERY ProductVariantDetail and checks if the corresponding ProductVariant matches the custom control. I discovered you can retrieve a query client-side (to save server bandwidth, see uncommented code) but my version never makes it inside filter(filter).execute. I tried "ProductVariant.Id eq" for the filter as well. Does anyone know what I might be doing wrong?
Try using this
var filter = "(ProductVariant.Id eq " + msls._toODataString(contentItem.value,":Int32")+")";