Jump to next code cell (Jupyter notebook) - javascript

I'd like to move the focus to the next cell of my Jupyter notebook. I've written a Javascript function that is fired when a user presses an HTML button. I added this button to my notebook by making use of functionality from IPython.core.display.HTML. This is the HTML part for the button: button onclick="set_params();" [in html brackets]. In the function - yes, with name set_params - I prepare a command that should be executed by the Python kernel. I establish that this is actually done when the button is pressed. Afterwards I want the cursor to move to the next cell - and that is not happening unfortunately. Here is part of my Javascript function:
var command = 'input_params = [' + names + ']';
IPython.notebook.kernel.execute(command);
i = IPython.notebook.get_selected_index();
IPython.notebook.select(i+1);
I even checked with some extra code whether the cell with index i+1 is really a cell and it is. Why is the focus not moving?

If you import Javascript:
from IPython.display import Javascript
You can do something like:
def goto_cell_two():
display(Javascript("Jupyter.notebook.execute_cells([2])"))
go_to_cell_two()
Cells start with index 0

A few million years later ....
I end up calling 3 javascript functions consequently, namely select_next(), focus_cell(), and execute_selected_cells(). All these calls can be combined in one procedure in the first cell, and then call this function every time you want to jump into the next cell.
def run_next(ev):
display(Javascript('IPython.notebook.select_next()'))
display(Javascript('IPython.notebook.focus_cell()'))
display(Javascript('IPython.notebook.execute_selected_cells()'))
So in the "linear" part of a code without any input from the user, this function is called at the end of each cell
run_next(None)
However, in interface cells, this function should be called only when the user selects/accepts parameters and these parameters are correct.
recacceptbtn = widgets.Button(
description='Accept',
disabled=False,
button_style='success', # 'success', 'info', 'warning', 'danger' or ''
tooltip='Click to accept',
icon='check-square'
)
def rec_check_update_runnext(evn):
# testing parameters
if recfile.value is not None and recprobe.value is not None:
# saving parameters
run_next(evn)
else:
display(Markdown('**You must select file and probe!**'))
recacceptbtn.on_click(rec_check_update_runnext)
I haven't tested this approach extensively, but it works so far and works pretty well.

Related

javascript ordering asyn functions

I'm currently modifying a open source project (link) to fit my need.
my English is bad, maybe it will be clearer if you can click on the link page, it will take half of minute to load, browser may seems to be freeze for a moment.
this page uses slickgrid , when hovering mouse over table rows , it will render details of that row on a fixed-position layer on page(the bottom left). there is only one such detail layer on the page, when you move your mouse to another row, this layer will change to show details of that row. following code is how it achieve this :
this.grid.onMouseEnter.subscribe( function (e) {}) // use slickgrid to set event listeners
// inside the handler function
from event e, get the row number row.rc
// then calls the staticOverlayDetails function do the actual work. it will renders the presentation layer.
window.setTimeout(function(){staticOverlayDetails(rc.row);}, 30);
so this staticOverlayDetails function will render the layer ( a div element ) to show the detail of row number rc.row, which is where mouse cursor is at.
I want to collect all the rendered result layer html code so I can combine them into a single page for easy read. that means, I want to hover my mouse on row one, wait for layer to be rendered, copy and save the div element, then move mouse to the next row , wait for the layer to be rendered with this new row , ...repeat until all row are done.
function staticOverlayDetails pseudo code:
function staticOverlayDetails (rown) {
//step 1:generate html text structure for the row, but leaves an element blank, this element is a text description for the row. the text will be loaded from a txt file
...
...
// html_text contains an `div` for description text which is currently empty, the text need to be fetched from a text file url, the text file name is based on the content of row. for the purpose of when the text is fetched, the callingback function need to know where to insert and avoid insert wrongly ( if the mouse has already moved to another row, the layer is showing details of another row, so the description text mismatch), so here it set a random string as the unique id of the description `div`, so later the `insert` function can use the id string as selector to insert text.
description_selector = random_id
html_text= html_code_generated
//step 2:
// this function fetches a , setting a callback function `insert` to insert the content.
url=generate text file url from row content
load_description( url, description_selector )
//step 3: present html
$(fixed-layer).html(txt)
//at this time , the description div is blank. the callback function will insert content into div when fetched.
}
function load_description (url, description_selector) {
function insert ( descrp_text ) {
$(description_selector).html(descrp_text).hide.slide_down()
}
$.get(url, insert, 'text')
}
my plan is to loop through rown to change the fixed layer, then dumps out the fixed layer :
for ( i=0; i < row_counts ; i++ ){
staticOverlayDetails(i)
console.log($(fixed_layer_selector).html())
but the async $.get() call makes this attempt impossible. because only when insert function finished , the content in layer is stable and ready to be dumped, if I use this loop, when $.get returns, the presentation layer is already showing later layers, I don't know how to wait for it.
how can I make my code waiting for the calling back function of $.get() to finish, so I can save the full details for the row, then continue to loop with the next row ?
according to link , I tried to modify the $.get request in the function load_content_for_an_element sync :
function load_description (url, description_selector) {
function insert ( descrp_text ) {
$(description_selector).html(descrp_text).hide.slide_down()
}
$.get({url:url, success:insert, sync:false});
}
but the browser make request to https://host.address/[object%20Object] and it returns with 404 error. seems it convert the passed object to string, then treat it as url to fetch.
some additional info:
1, because this project has several tables each with different rendering logic, I want to make the changes as small as possible, that's why I want to dump the generated presentation layer instead of changing the generating code itself. all different tables render the same layer, that's the easiest way to do it.
2, I want to dump the code to generate an page to read, only need to dump once , so there is no worries about performance. actually I can use a mouse macro to do it , but I think that's too dumb, I want to learn the javascript way to do it :)
I'm so sorry my English is bad and I want to express such a complex situation, so please ask for more infos if my words are not clear, I'm so sorry to see other people waste time because of my mistake words...
Promises are your friend. Here's an example of how you would structure your code to use them:
// Set up a staging area
var staticOverlays = [];
loadStaticOverlayDetails(index) {
return $.get(url, insert, data....).then(function (data) {
staticOverlays[index] = data;
});
}
// Request your data
var promises = [];
for ( var i=0; i < 100; i++ ) {
promises.push(loadStaticOverlayDetails(i));
}
// Wait for the data to complete loading
$.when(promises).then(function insert() {
for ( i=0; i < staticOverlays.length; i++ ) {
var overlay = staticOverlays[i];
// step 1:generate html text structure for the row, but leaves an element blank
var txt = html_code_generated
// insert data into element in fixed layer;
$(fixed-layer).html(txt);
}
});
As an aside, if you're really making 100+ http requests you should consider moving some of this logic to the server because that many http requests will hurt your user experience...

Inject/execute JS code to IPython notebook and forbid its further execution on page reload

I'm writing the library which has to embed javascript code to IPython notebook and execute it. The HTML/JS code looks like:
<div id="unique_id"></div>
<script>
var div = document.getElementById("unique_id");
// Do the job and get "output"
div.textContent = output; // display output after the cell
</script>
And the python code:
from IPython import display
display.display(display.HTML(code))
The side-effect is that the javascript code is stored in the output of the cell in notebook, and every time when the page is reloaded or the notebook is opened it will run again.
Are there any way of forbidding the code to be executed on reload? Or is it possible to run the javascript code without saving it within the output?
I've figured out the hack.
The trick is to use update=True argument of the IPython.display.display() which will replace the output with a new one (see here for an example).
So what is needed to be done: first output javascript that does the job, and then waits until the div with a certain ID is created, to fill it with the output. Once this display() is called, we could call display a second time updating the first one with the actual HTML with the div. So the javascript code once finished will fill it with the results, but the code itself will not be saved.
Here's the test code:
First, define the callback function (it looks like, it is important here to display it as HTML("<script> ... </script>") rather than Javascript(...)):
from IPython.display import display, HTML, Javascript
js_getResults = """<script>
function getResults(data, div_id) {
var checkExist = setInterval(function() {
if ($('#' + div_id).length) {
document.getElementById(div_id).textContent = data;
clearInterval(checkExist);
}
}, 100);
};
</script>"""
display(HTML(js_getResults))
And then execute the update trick in one cell:
js_request = '$.get("http://slow.server/", function(data){getResults(data, "unique_id");});'
html_div = '<div id="unique_id">Waiting for response...</div>'
display(Javascript(js_request), display_id='unique_disp_id')
display(HTML(html_div), display_id='unique_disp_id', update=True)
After the callback of get() is executed, the content Waiting for response... will be replaced with the output from the server.
After running into the same issue of Javascript executing on every notebook open, I adapted #Vladimir's solution to a more general form:
Use fresh unique IDs on every render (since old ID is saved with the HTML output of the notebook).
No polling to determine when HTML element is rendered.
Of course, when the notebook is closed, no HTML modifications done by JS are saved.
Key Insight: Replace Cell Output
from IPython.display import clear_output, display, HTML, Javascript
# JavaScript code here will execute once and will not be saved into the notebook.
display(Javascript('...'))
# `clear_output` replaces the need for `display_id` + `update`
clear_output()
# JavaScript code here *will* be saved into the notebook and executed on every open.
display(HTML('...'))
Making it Work
The challenge here is that the HTML and Javascript blocks can be rendered out of order, and the code which manipulates the HTML element needs to only execute once.
import random
from IPython.display import display, Javascript, HTML, clear_output
unique_id = str(random.randint(100000, 999999))
display(Javascript(
'''
var id = '%(unique_id)s';
// Make a new global function with a unique name, to prevent collisions with past
// executions of this cell (since JS state is reused).
window['render_' + id] = function() {
// Put data fetching function here.
$('#' + id).text('Hello at ' + new Date());
}
// See if the `HTML` block executed first, and if so trigger the render.
if ($('#' + id).length) {
window['render_' + id]();
}
''' % dict(unique_id=unique_id)
# Use % instead of .format since the latter requires {{ and }} escaping.
))
clear_output()
display(HTML(
'''
<div id="%(unique_id)s"></div>
<!-- When this script block executes, the <div> is ready for data. -->
<script type="text/javascript">
var id = '%(unique_id)s';
// See if the `Javascript` block executed first, and if so trigger the render.
if (window['render_' + id]) {
window['render_' + id]();
}
</script>
''' % {'unique_id': unique_id}
))
To keep the notebook clean, I would put this plumbing code into a separate .py file and import it from Jupyter.

Calling a normal function and/or ajax call based on user selection. I need ideas?

So I'm giving my user two buttons. I'll refer to them "Button 1" and "Button 2".
Button 1 does:
function button1 () {
// push whatever data to Output
}
Button 2 does:
$.getJSON(x,function(){
// my code for this is fine
// push whatever data to Output
});
The user could pick either button. I don't want to run the ajax call at runtime, but when the user clicks Save at the end.
On save:
// checks to see which button is checked and chains the functions together
// appropriately and sends all the data to Output as a string
It's a little more complicated / confusing than at first glance. Please give me your thoughts.

Invoke JavaScript function at GridView cell click

I have a DevExpress gridview with the following settings.
settings.Name = "DetailGridView";
// this calls DetailGridView.StartEditRow() on client side
settings.ClientSideEvents.RowClick = "Fn.startEditingRow";
settings.SettingsEditing.Mode = DevExpress.Web.GridViewEditingMode.Inline;
I have removed many other settings I have for simplicity, but ask if you think I need to show some other settings which are relevant.
Now I want to invoke a JavaScript function at each cell click. To do that I have added this settings, as per this SO answer, and this DevExpress thread
settings.HtmlDataCellPrepared += (sender, e) =>
{
string onClickFunctionJS = "Fn.DetailOnlyOnCellClick({0},'{1}');";
e.Cell.Attributes.Add("onclick", String.Format(onClickFunctionJS, e.VisibleIndex, e.DataColumn.FieldName));
};
The JS function Fn.DetailOnlyOnCellClick() prints to console the value of the field name (the 2nd argument). But it only prints clicked cell's field name the first time the row is clicked. After that clicking on a different cell in the selected row doesn't trigger the function Fn.DetailOnlyOnCellClick() anymore. I have observed that if I turn off ClientSideEvents.RowClick, it works fine, but I can't turn that off for other reasons. How can I get the which cell the user clicks on while keeping ClientSideEvents.RowClick on?
I have solved my problem, thanks to DevExpress support center. First, I couldn't use settings.HtmlDataCellPrepared because, as I mentioned in the question, the grid view enters edit mode when a row is clicked, and in edit mode settings.HtmlDataCellPrepared is not supposed to work the way I need it for my situation. So I started to use this code:
settings.CellEditorInitialize = (s, e) =>
{
var elementEditor = e.Editor;
string onClickFunctionJS = "DetailOnlyOnCellClick({0},'{1}');";
DevExpress.Web.Rendering.GridViewTableInlineEditorCell cell =
elementEditor.Parent as DevExpress.Web.Rendering.GridViewTableInlineEditorCell;
cell.Attributes.Add("onclick",
String.Format(onClickFunctionJS, e.VisibleIndex, e.Column.FieldName));
};
This code worked fine for every different control. The only catch was when the field is a checkbox and the cell is disabled. To get around this, I have used this code for checkbox controls:
string func = String.Format(
"ASPxClientUtils.AttachEventToElement(s.GetMainElement(), 'click', function() {{ {0} }} )",
String.Format(onClickFunctionJS, e.VisibleIndex, e.Column.FieldName));
(elementEditor as ASPxCheckBox).ClientSideEvents.Init = String.Format("function(s,e){{ {0}; }}", func);
This code works perfectly.
Links to the two methods used:
ASPxClientUtils.AttachEventToElement
ASPxClientControlBase.GetMainElement
P.S.: Both these approaches I have shown here are from DevExpress experts, and I got these answers from DevExpress's support center. I will provide link to my question once I can fix the issue with license and the ticket becomes public.

Refresh to repeat control not working normal after deleting 1 row

I have a field named "selectedTime" in a document, this fields stores the selected timings added by user.Adding times is working perfect.This is back-end.
Now I will explain this issue of selecting date from front end.I have given a button add to add times.The custom control of date-time gets added to repeat control on click of Add button.Even if I check in document it shows the list of selected times.Even this works fine.
Now if I want to delete a selected time from repeat control randomly, it deleted that particular record from document, but on the page the last record of the repeat gets disappears,
I was assuming that this is the issue with partial refresh of repeat control,I have even tried that but no result.Full refresh breaks the page.
java script code for the delete button
`var doc:NotesDocument = database.getDocumentByUNID(context.getUrlParameter("refId"))
var selectedTimes:java.util.Vector = doc.getItemValue("selectedTimes");
if(selectedTimes != null){
var sdtString = getComponent("inputHidden1").getValue();
if(selectedTimes.contains(sdtString))
selectedTimes.remove(sdtString);
doc.replaceItemValue("selectedTimes",selectedTimes);
doc.save();
};
var url:XSPUrl = context.getUrl();
view.postScript("window.refresh('"+url+"')");`
I know it is difficult to understand what i want to explain but any suggestion on this will be appreciated.
Even if anybody have any idea to delete the a field values of a documents,In my case field name is "selectedTimes" and the values are added times in repeat control, Please share.
Edit 1:
//Repeat Control
var doc:NotesDocument = database.getDocumentByUNID(context.getUrlParameter("refId"))
var selectedTimes:java.util.Vector = doc.getItemValue("selectedTimes");
return selectedTimes;
Another try could be link the repeat with a viewScope instead of the document:
1) In the event beforeLoadPage/afterLoadPage: Get the value from the document, and put it in a viewScope variable:
// beforeLoadPage event:
// ... get the doc
viewScope.selectedTimes = doc.getItemValue("selectedTimes");
2) In the repeat control, use the viewScope:
<xp:repeat value="#{viewScope.selectedTimes}"...
3) When an update is done, update both the viewScope and the document:
//...update the View Scope variable and get the document:
doc.replaceItemValue("selectedTimes", viewScope.selectedTimes);
This could be a hint if the document would be added as DataSource:
Do you have the document included in the XPage as a DataSource? In that case, try to get and update the NotesXspDocument instead of the Document from the DB:
XPage:
<xp:this.data>
<xp:dominoDocument var="xspDocument"
action="editDocument"
documentId="#{param.unid}">
</xp:dominoDocument>
</xp:this.data>
SSJS code: work directly with the XspDocument
var selectedTimes:java.util.Vector = xspDocument.getItemValue("selectedTimes");
...
xspDocument.replaceItemValue("selectedTimes", selectedTimes);
This could be a hint if the value would not be removed from the document:
In sdtString you are getting a String value:
var sdtString = getComponent("inputHidden1").getValue();
If you have the time values stored as NotesDateTimes, you will get this type of value inside the Vector and the remove method won't find the String and nothing will be removed.
// In a Vector<NotesDateTime> the String cannot be found:
selectedTimes.remove(sdtString);
Be sure you remove the same type of value you get in the Vector

Categories