Contenidos

IPython.Display - la parte web

En IPython.display existen 23 clases que son muy prácticas para agregar interactividad a Jupyter Notebook/Lab.

Acá probamos únicamente 3 que se relacionan con la web: IFrame, HTML, Javascript.

1. Antes de: principales errores

1.1 Usando display generosamente

Mi principal aprendizaje es que IPython.display genera objetos de distintos tipos. Los objetos no se muestran o ejecutan por defecto. Si el objeto es el resultado de la celda, el objeto si se despliega o ejecuta.

Si no es el resultado de la celda, no se muestra/ejecuta, a menos que se use el método display.

Diferencias entre hermanos

Jupyter Notebook, Jupyter Lab y Google Colab no son completamente similares. Hasta ahora, lo que he visto:

  • En Google Colab está IPython 5.0.0. No es lo mejor para evaluar javascript. De hecho, hay funciones que no existen: alert y prompt no están.

Link interesante: https://colab.research.google.com/notebooks/snippets/advanced_outputs.ipynb#scrollTo=iXZ0xoQd2kCe

2. Páginas web

2.1 IFrame

IFrame(src, 
       width, 
       height, 
       **kwargs)

Permite insertar una página web como un IFrame (inline frame). La página debe permitirlo (no todas lo hacen). Por ejemplo, para insertar una encuesta o una referencia en un jupyter notebook o una presentación con RISE.

Métodos o atributos del objeto creado:

  • iframe: String con la representación HTML del objeto. No es muy útil.
from IPython.display import IFrame
IFrame("https://es.wikipedia.org/wiki/IFrame", width=800, height=400)

Para más información, ver la documentación con IFrame.?

2.2 Javascript

Javascript(data=None, 
           url=None, 
           filename=None, 
           lib=None, 
           css=None)

Carga el código javascript en la página. En el notebook, el elemento conteneder será element, y jQuery estará disponible. Contenido agregado a element serán visibles en la área de output.

Métodos o atributos del objeto creado:

  • metadata: Atributo string con la metadata con la que fue creado el objeto.
  • reaload: Método para recargar el contenido del objeto, si fue creado desde url o archivo.
from IPython.display import Javascript
Javascript('alert("¡¡Tenemos javascript!!");')
from IPython.display import Javascript
Javascript('console.log("¡¡Tenemos javascript!!");')
from IPython.display import Javascript
js_str = """
var a = 1;
console.log("JS1: a = "+a);
var b = 2;
console.log("JS1: b = " + b);
console.log("JS1: a+b = " + (a+b));
"""
Javascript(js_str)
from IPython.display import Javascript, display
js_str_1 = """
var a = 1;
console.log("JS2: a ="+a);
"""
js_str_2 = """
var b = 2;
console.log("JS2: b =" + b);
console.log("JS2: a+b=" + (a+b));
"""
js_1 = Javascript(js_str_1)
js_2 = Javascript(js_str_2)
display(js_1, js_2)
from IPython.display import Javascript
Javascript?
Init signature: Javascript(data=None, url=None, filename=None, lib=None, css=None)
Docstring:      Validate that display data is text
Init docstring:
Create a Javascript display object given raw data.

When this object is returned by an expression or passed to the
display function, it will result in the data being displayed
in the frontend. If the data is a URL, the data will first be
downloaded and then displayed.

In the Notebook, the containing element will be available as `element`,
and jQuery will be available.  Content appended to `element` will be
visible in the output area.

Parameters
----------
data : unicode, str or bytes
    The Javascript source code or a URL to download it from.
url : unicode
    A URL to download the data from.
filename : unicode
    Path to a local file to load the data from.
lib : list or str
    A sequence of Javascript library URLs to load asynchronously before
    running the source code. The full URLs of the libraries should
    be given. A single Javascript library URL can also be given as a
    string.
css: : list or str
    A sequence of css files to load before running the source code.
    The full URLs of the css files should be given. A single css URL
    can also be given as a string.
File:           /miniconda3/lib/python3.7/site-packages/IPython/core/display.py
Type:           type
Subclasses:     
from IPython.display import Javascript
print("Methods:")
print(sorted([f for f in dir(Javascript) if not f.startswith('_')]))

2.3 HTML

HTML(data=None, 
     url=None, 
     filename=None, 
     metadata=None)

Observación/Consejos:

  • No incluir secciones de <html>, <head> o <body>.
  • El Javascript vive en la celda de notebook, las variables no se traspasan de una celda a otra. Ver los ejemplos al final.
  • En Google Colab algunas funciones de javascript no están disponibles, en particular 'alert'.

Métodos o atributos del objeto creado:

  • metadata: String con la metadata con la que fue creado el objeto.
  • reaload(): Método para recargar el contenido del objeto, si fue creado desde url o archivo.
from IPython.display import HTML
HTML("<div style='background-color:lightblue'>Este es un ejemplo.</div>")
Este es un ejemplo.
# Ver resultado con la consola de javascript
from IPython.display import HTML
my_html = """
<script>
function button_action(value){
  var msg = 'La variable tiene valor '+ value;
  console.log(msg);
  alert(msg);
};
</script>

<div> 
Variable: 
<input type='text' id='my_input_form' value='42'> 
<button onclick='button_action(document.getElementById("my_input_form").value)'>Click</button>
</div>
"""
HTML(data=my_html)
Variable:
from IPython.display import HTML
my_filename = "html/ejemplo.html"
HTML(filename=my_filename)

Título Página HTM

from IPython.display import HTML
my_url = "https://raw.githubusercontent.com/sebastiandres/2021-05-IPython-display/master/html/ejemplo.html"
HTML(url=my_url)

Título Página HTM

from IPython.display import HTML
my_html = """
<script>
var a = 1;
console.log("HTML: a ="+a);
</script>
"""
HTML(my_html)

Más información

from IPython.display import HTML
HTML?
from IPython.display import HTML
print("Methods:")
print(sorted([f for f in dir(HTML) if not f.startswith('_')]))

3. Experimentos

3.1 Experimento HTML 1:

Valores de variables no se traspasan de una celda a otra. Definiremos aen la primera celda, y trataremos de imprimir el valor de a en la celda siguiente.

from IPython.display import HTML
my_html = """
<script>
var num = 42;
console.log("HTML1A: num = " + num);
</script>
"""
HTML(my_html)
from IPython.display import Javascript
my_html = """
<script>
var b = 2;
console.log("HTML1B1: num =" + num);
console.log("HTML1B2: b =" + b);
console.log("HTML1B3: num+b=" + (num+b));
</script>
"""
HTML(my_html)

Experimento HTML 2:

Colocando varios scripts en un único html: si funciona.

from IPython.display import HTML
my_html = """
<script>
var a = 1;
console.log("HTML2A: a = "+a);
</script>

<script>
var b = 2;
console.log("HTML2B: b = "+b);
</script>

<script>
console.log("HTML2C: a+b = "+(a+b));
</script>
"""
HTML(my_html)

Experimento HTML 3:

Ejecutando varios HTMLs por separado.

from IPython.display import HTML, display
my_html = """
<script>
var a = 1;
console.log("HTM3: a = "+a);
</script>
"""
h1 = HTML(my_html)
my_html = """
<script>
var b = 2;
console.log("HTML3: b = "+b);
</script>
"""
h2 = HTML(my_html)
my_html = """
<script>
console.log("HTML3: a+b = "+(a+b));
</script>
"""
h3 = HTML(my_html)
display(h1, h2, h3) # Hay que poner todos, sino solo muestra el último

Experimento HTML 4:

El hermano pobre RISE, ¿cómo hacer presentaciones con reveal.js?

!cat body.html
<body>

<link rel="stylesheet" href="reveal.css">
<script src="reveal.js"></script>
<script src="body.js"></script>

<button id="my_button" onclick="launch_reveal()">Click me</button>

<div id="reveal_id" class="reveal">
	<div class="slides">
		<section>Slide 1</section>
		<section>Slide 2</section>
		<section>Slide 3</section>
	</div>
</div>

</body>
from IPython.display import HTML, Javascript, display
HTML(filename="body.html")
Slide 1
Slide 2
Slide 3
from IPython.display import HTML, Javascript, display
my_path = "https://raw.githubusercontent.com/sebastiandres/2021-05-IPython-display/master/"
my_js = Javascript(url=my_path+"body.js", lib=my_path+"reveal.js", css=my_path+"reveal.css")
my_html = HTML(filename="body.html")
display(my_js)
display(my_html)
Slide 1
Slide 2
Slide 3
from IPython.display import HTML, Javascript, display
my_js = """
var link = document.createElement('link');
link.rel = "stylesheet"; link.type = "text/css";
link.href = "reveal.css";
document.querySelector('head').appendChild(link);
"""
Javascript(my_js)

Experimento JS 1:

Ejecutando varios javascript por separado.

from IPython.display import Javascript
js_str_1 = """
var num = 42;
console.log("JS1A: num = " + num);
"""
Javascript(js_str_1)
from IPython.display import Javascript
js_str_2 = """
var num = 42;
var b = 2;
console.log("JS1B1: num = " + num);
console.log("JS1B2: b = " + b);
console.log("JS1B3: num+b = " + (num+b));
"""
Javascript(js_str_2)

Experimento JS 2:

Usando javascript (puro) para agregar elementos html de manera dinámica. Observación: No se usa html, sino sólo Javascript sobre el "element".

from IPython.display import Javascript
my_js = """
function button_action(value){
  console.log('La variable tiene valor '+ value);
};

element.append(`<div> 
Variable: 
<input type='text' id='my_input_form' value='42'> 
<button onclick='button_action(document.getElementById("my_input_form").value)'>Click</button>
</div>`);
"""
Javascript(my_js)

Experimento JS 3:

Usando javascript para graficar.

CURRENTLY NOT WORKING

# https://github.com/timqian/chart.xkcd
from IPython.display import Javascript
my_js = """
  const svg = document.querySelector('.line-chart')

  new chartXkcd.Line(svg, {
    title: 'Monthly income of an indie developer',
    xLabel: 'Month',
    yLabel: '$ Dollars',
    data: {
      labels:['1', '2', '3', '4', '5', '6','7', '8', '9', '10'],
      datasets: [{
        label: 'Plan',
        data: [30, 70, 200, 300, 500 ,800, 1500, 2900, 5000, 8000],
      }, {
        label: 'Reality',
        data: [0, 1, 30, 70, 80, 100, 50, 80, 40, 150],
      }]
    },
    options: {}
  });
  
  element.append(`<svg class="line-chart"></svg>`)
"""
my_lib = "https://cdn.jsdelivr.net/npm/chart.xkcd@1/dist/chart.xkcd.min.js"
Javascript(data=my_js, lib=my_lib)

Experimento JS 3:

Usando javascript para graficar.

CURRENTLY NOT WORKING

from IPython.display import Javascript
# Link: https://github.com/timqian/chart.xkcd/blob/master/examples/index.js
my_url = "https://raw.githubusercontent.com/timqian/chart.xkcd/master/examples/index.js"
my_lib = "https://cdn.jsdelivr.net/npm/chart.xkcd@1/dist/chart.xkcd.min.js"
Javascript(url=my_url, lib=my_lib)
import IPython

js_code = \
'''
let message = "Hello world!";
//document.querySelector("#output-area").appendChild(document.createTextNode(message));
element.append(document.createTextNode(message));
'''

display(IPython.display.Javascript(js_code))
import IPython

js_code = \
'''
var a = prompt("Hello", "Sebas");
alert(a);
'''

display(IPython.display.Javascript(js_code))
from IPython.display import HTML, Image

canvas_html = """
<canvas width=%d height=%d style="background-color:rgb(240,240,240)"></canvas>
<button>Guess Number</button>
<script>
var canvas = document.querySelector('canvas')
var ctx = canvas.getContext('2d')
ctx.lineWidth = %d
var button = document.querySelector('button')
var mouse = {x: 0, y: 0}
canvas.addEventListener('mousemove', function(e) {
  mouse.x = e.pageX - this.offsetLeft
  mouse.y = e.pageY - this.offsetTop
})
canvas.onmousedown = ()=>{
  ctx.beginPath()
  ctx.moveTo(mouse.x, mouse.y)
  canvas.addEventListener('mousemove', onPaint)
}
canvas.onmouseup = ()=>{
  canvas.removeEventListener('mousemove', onPaint)
}
var onPaint = ()=>{
  ctx.lineTo(mouse.x, mouse.y)
  ctx.stroke()
}
var data = new Promise(resolve=>{
  button.onclick = ()=>{
    resolve(canvas.toDataURL('image/png'))
  }
})
</script>
"""
print(canvas_html % (280, 280, 10))

def draw(filename='drawing.png', w=280, h=280, line_width=10):
  display(HTML(canvas_html % (w, h, line_width)))

draw()
<canvas width=280 height=280 style="background-color:rgb(240,240,240)"></canvas>
<button>Guess Number</button>
<script>
var canvas = document.querySelector('canvas')
var ctx = canvas.getContext('2d')
ctx.lineWidth = 10
var button = document.querySelector('button')
var mouse = {x: 0, y: 0}
canvas.addEventListener('mousemove', function(e) {
  mouse.x = e.pageX - this.offsetLeft
  mouse.y = e.pageY - this.offsetTop
})
canvas.onmousedown = ()=>{
  ctx.beginPath()
  ctx.moveTo(mouse.x, mouse.y)
  canvas.addEventListener('mousemove', onPaint)
}
canvas.onmouseup = ()=>{
  canvas.removeEventListener('mousemove', onPaint)
}
var onPaint = ()=>{
  ctx.lineTo(mouse.x, mouse.y)
  ctx.stroke()
}
var data = new Promise(resolve=>{
  button.onclick = ()=>{
    resolve(canvas.toDataURL('image/png'))
  }
})
</script>

from IPython.display import HTML
HTML(filename="drawing.html")