ipywidgets

Notes

ipysheet

  • 實測沒辦法當 user input 用

    • 無法直接從剪貼簿貼上一個 cell range(不過有人 request 了)

    • 從 ipysheet 複製到剪貼簿倒是可以

    • 一格一格敲完後也不知道怎麼把值傳回 df(ipysheet.calculation 只能用來加 cell 之間的 dependency)

    • 這裡說 Voila 無法 render ipysheet

[9]:
import ipysheet
import numpy as np
from ipysheet import to_dataframe
from pandas import DataFrame

sheet = ipysheet.sheet(rows=3, columns=2)

df = DataFrame(np.zeros((3, 2)))

def record(change):
    for i in range(3):
        for j in range(2):
            df.iloc[i, j] = cell(i, j).value

for i in range(3):
    for j in range(2):
        ipysheet.cell(i, j).observe(record, 'value')

sheet

Math Formula Generator

  • 這裡有在討論怎麼把 widgets 轉成 static image

[4]:
import ipywidgets as widgets

txt = widgets.Textarea(
    value='Hello World',
    placeholder='Type something',
    description='String:',
    disabled=False
)
import numpy as np

txt.on_msg?
Signature: txt.on_msg(callback, remove=False)
Docstring:
(Un)Register a custom msg receive callback.

Parameters
----------
callback: callable
    callback will be passed three arguments when a message arrives::

        callback(widget, content, buffers)

remove: bool
    True if the callback should be unregistered.
File:      /srv/conda/envs/notebook/lib/python3.7/site-packages/ipywidgets/widgets/widget.py
Type:      method

[1]:
# mathurl

import ipywidgets as widgets
from IPython.display import display, clear_output

formulaInput = widgets.Textarea()
button = widgets.Button(description='Convert')
output = widgets.Output()

def test(f):
    with output:
        clear_output()
        display(widgets.HTMLMath(formulaInput.value))

button.on_click(test)

display(formulaInput, button, output)
  • matplotlib 就可以 render 數學式了

[2]:
# https://stackoverflow.com/questions/14110709/creating-images-of-mathematical-expressions-from-tex-using-matplotlib

import pylab

formula = r'$x=3^2, y = \frac{1}{\frac{2}{3}}}$'

fig = pylab.figure()
text = fig.text(0, 0, formula)

# Saving the figure will render the text.
dpi = 300
fig.savefig('formula.png', dpi=dpi)

# Now we can work with text's bounding box.
bbox = text.get_window_extent()
width, height = bbox.size / float(dpi) + 0.005

# Adjust the figure size so it can hold the entire text.
fig.set_size_inches((width, height))

# Adjust text's vertical position.
dy = (bbox.ymin/float(dpi))/height
text.set_position((0, -dy))

# Save the adjusted text.
fig.savefig('formula.png', dpi=dpi)
<Figure size 58.2375x18.8461 with 0 Axes>
  • 這裡有一個 open source JS 版本

Notebook to PDF Converter Without Callback

  • FileLink 可以改。同一個 issue,有人示範了 download button,不需要知道 file encoding。下面這個版本可以成功佈署到 Voila 上

  • GridspecLayout 不好用,試了半天還是用 HBox 和 VBox

[2]:
from ipywidgets import FileUpload, Button, Output
from IPython.display import display, HTML
import subprocess, os, base64

output = Output()
fileInput = FileUpload()
convertButton = Button(description='Convert')

def convert(b):
    with output:
        print('Your PDF will be ready in a few seconds. ')
        nbName = list(fileInput.value.keys())[0]
        pdfName = nbName.replace('ipynb', 'pdf')
        with open(nbName, 'w+b') as f:
            f.write(fileInput.data[0])
        proc = subprocess.run(['jupyter', 'nbconvert', nbName, '--to', 'pdf'])
        if proc.returncode == 1:
            print('LaTeX compiling error!')
        else:
            with open(pdfName, "rb") as f:
                data = f.read()
                b64 = base64.b64encode(data)
                payload = b64.decode()
            display(HTML(f'''<a download="{pdfName}" href="data:text/csv;base64,{payload}" target="_blank">
                                <button class="p-Widget jupyter-widgets jupyter-button widget-button"> Download PDF </button>
                            </a>'''))

convertButton.on_click(convert)

display(fileInput, convertButton, output)

Notebook to PDF Converter

用 Javascript window.location.href 可以知道當前 server url。下面這個 app 在 JupyterLab 是完全可以跑的,不過 Volia 一遇到 Javascript 會有很多問題,所以用 Voila 佈署失敗。如果把 alert("aaa"); 插在 callback 的最前面,實驗顯示 Voila 根本完全沒有執行 callback。這個 issue 提到 Voila 遇到 callback 會失敗的問題。目前還沒有修好

[8]:
from ipywidgets import FileUpload, Button, Output
from IPython.display import display, FileLink, HTML, Javascript
import subprocess, os

env = 'lab'   # change to 'voila' before deploying

output = Output()
fileInput = FileUpload()
convertButton = Button(description='Convert')

def convert(b):
    with output:
        print('Your PDF will be ready in a few seconds. ')
        nbName = list(fileInput.value.keys())[0]
        pdfName = nbName.replace('ipynb', 'pdf')
        with open(nbName, 'w+b') as f:
            f.write(fileInput.data[0])
        proc = subprocess.run(['jupyter', 'nbconvert', nbName, '--to', 'pdf'])
        if proc.returncode == 1:
            print('LaTeX compiling error!')
        else:
            pdfPath = '/'.join(['tree'] + str(FileLink(pdfName)).split('/')[3:])
            display(HTML('''
                        <html>
                        <body>
                        <script type="text/javascript">
                            function setHref() {
                                var server = window.location.href;
                                var homeIdx = server.indexOf("%s");
                                server = server.slice(0, homeIdx);
                                var href = server + "%s";
                                open(href);
                            }
                        </script>
                        <form name="buttonForm">
                            <button class="p-Widget jupyter-widgets jupyter-button widget-button" onClick="setHref();return true;">
                                Download PDF
                            </button>
                        </form>
                        </body>
                        </html>
                         '''% (env, pdfPath)))

convertButton.on_click(convert)

display(fileInput, convertButton, output)

Download Button Example in ipywidgets

This works in Voila, but to download PDF this way one needs to know the encoding of the PDF.

[2]:
from ipywidgets import HTML
from IPython.display import display

import base64

res = 'computed results'

#FILE
filename = 'res.txt'
b64 = base64.b64encode(res.encode())
payload = b64.decode()

#BUTTONS
html_buttons = '''<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<a download="{filename}" href="data:text/csv;base64,{payload}" download>
<button class="p-Widget jupyter-widgets jupyter-button widget-button mod-warning">Download File</button>
</a>
</body>
</html>
'''

html_button = html_buttons.format(payload=payload,filename=filename)
display(HTML(html_button))

Get Server URL and Pass to Python

  • 下面這段 code 來自這裡。可能在 Jupyter Notebook 可以跑可是在 JupyterLab 不行因為 JavaScript 被 block 了

  • 可能有辦法透過 pyviz_comms 得到 URL(sandbox-stable 上有安裝),不過能不能在 Voila 上跑是另一個問題

[4]:
%%javascript

IPython.notebook.kernel.execute("url = '" + window.location + "'")

Create New Cell Programmatically

[3]:
def create_new_cell(contents):
    from IPython.core.getipython import get_ipython
    shell = get_ipython()
    payload = dict(
        source='set_next_input',
        text=contents,
        replace=False,
    )
    shell.payload_manager.write_payload(payload, single = False)

contents = 'print("Hello World")'
create_new_cell(contents=contents)
[ ]:
print("Hello World")