如何使用 ipywidget 按钮下载文件?

loo*_*opy 5 python python-3.x jupyter-notebook ipywidgets

我构建了一个 ipywidget 按钮。我希望当按钮被点击时,程序进行计算,并得到一个结果字符串,然后用户可以将该字符串作为文件下载。

代码是这样的:

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

Run Code Online (Sandbox Code Playgroud)

我已多次阅读ipywidgets 文档,但找不到解决方案。

我现在使用另一种方式(严重影响用户体验):创建一个 HTML 小部件,当单击 download_button 时,将 HTML 小部件的值更改为一个链接data:text/plain;charset=utf-8,{res}以让用户单击并下载,但有什么方法可以实现这一点只需单击一下?

任何帮助将不胜感激。

小智 8

我见过的最优雅的方法是此处的解决方案 1 (略有修改并在下面介绍):

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))
Run Code Online (Sandbox Code Playgroud)

  • 这次真是万分感谢!但我不确定这总体上对我有多大帮助。就我而言,该文件在初始化按钮时不存在,而是在单击按钮后计算。我实际上正在寻找一种方法让按钮同时触发计算和下载。 (4认同)

oll*_*ik1 8

游戏迟到了,但如果其他人面临这个问题并且需要动态文件内容,这里有一个方法。该代码的灵感来自 @Poompil 的答案。此外,可能有更优雅的方法来绕过浏览器缓存,但无法使其在 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>
"""))

Run Code Online (Sandbox Code Playgroud)

现在我们可以简单地添加

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

它添加了一个下载按钮,当该按钮计时时,会生成下载文件的内容。

在此输入图像描述

  • 这在 Jupyter 中效果很好,但在 JupyterLab 中不起作用(使用 3.2.5 进行测试)。知道如何让它在 JupyterLab 中工作吗? (2认同)