Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to download a file using ipywidget button?

I build a ipywidget button. I hope that when the button is clicked, the program do the computing, and get a result string, then user can download the string as a file.

THe codes are like this:

import ipywidgets as widgets


download_button = widgets.ToggleButton()
download_button.on_click(do_some_work)

def do_some_work(content)-> str:
    res = compute()
    # Here comes the problem: how to let user download the res as a file?

def compute()-> str:
    # ... do_some_compute
    return res

I have read ipywidgets doc for plenty times, but could not find a solution.

I use an alternative way now (which seriously affected the user experience): create a HTML widget and when download_button is clicked, change the value of HTML widget to an link to data:text/plain;charset=utf-8,{res} to let user click and download, but is there any way to achieve this with single one click?

Any help will be much appreciate.

like image 811
loopy Avatar asked May 10 '20 07:05

loopy


People also ask

What is IPY widget in Python?

Ipywidget is an open-source python library that is used to generate different types of widgets and implement them in a jupyter notebook. It is easy to use and provides a variety of interactive widgets. In this article, we will explore what are the different types of widgets that ipywidget provides.

How many code examples of ipywidgets are there?

The following are 30 code examples of ipywidgets.Button () . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may also want to check out all available functions/classes of the module ipywidgets , or try the search function .

How to upload multiple file formats in ipywidgets?

We can select different file formats that can be uploaded by allowing them in the ‘accept’ parameter, also in order to allow multiple files to be uploaded, we will set the ‘multiple’ parameter as True. These are some of the basic and most used widgets that can be created using ipywidgets.

How to create a progress bar using IPY widget?

Ipywidget allows us to create a progress bar using one of its widgets named Progress. It can be used to show that something is loading or progress of a certain work. widget.IntProgress ( value=1, min=0, max=10, step=1,description='Progress:', bar_style='info',orientation='horizontal')


2 Answers

The most elegant way I've seen is Solution 1 here (slightly modified and presented below):

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))
like image 69
Poompil Avatar answered Oct 22 '22 03:10

Poompil


Late to the game but if anyone else faces this issue and needs dynamic file content, here is an approach for this. The code is inspired by the answer by @Poompil . Also, there might be more elegant way to bypass browser cache but could not make it to work in Jupyter.

import base64
import hashlib
from typing import Callable

import ipywidgets
from IPython.display import HTML, display


class DownloadButton(ipywidgets.Button):
    """Download button with dynamic content

    The content is generated using a callback when the button is clicked.
    """

    def __init__(self, filename: str, contents: Callable[[], str], **kwargs):
        super(DownloadButton, self).__init__(**kwargs)
        self.filename = filename
        self.contents = contents
        self.on_click(self.__on_click)

    def __on_click(self, b):
        contents: bytes = self.contents().encode('utf-8')
        b64 = base64.b64encode(contents)
        payload = b64.decode()
        digest = hashlib.md5(contents).hexdigest()  # bypass browser cache
        id = f'dl_{digest}'

        display(HTML(f"""
<html>
<body>
<a id="{id}" download="{self.filename}" href="data:text/csv;base64,{payload}" download>
</a>

<script>
(function download() {{
document.getElementById('{id}').click();
}})()
</script>

</body>
</html>
"""))

Now we can simply add

DownloadButton(filename='foo.txt', contents=lambda: f'hello {time.time()}', description='download')

which adds a download button and the contents of the downloaded file is generated when the button is clocked.

enter image description here

like image 40
ollik1 Avatar answered Oct 22 '22 05:10

ollik1