Download widget?

Hi, I’m making an app with ipywidgets, voila and binder, which basically creates a word document based on certain parameters. The only thing I need is that the user can download the word document created, but the command doc.save(‘FileName.docx’) saves the document in the same jupyter and would have to open the left menu and download it, if it were in jupyterlab, but as I’m using voila, this menu no longer appears, so I don’t know if anyone knows how to download the word document created directly, simply by pressing a button or in some other way. Thanks in advance for any suggestions or support.

doc.save() is doing just what it should do. You don’t want that. What else have you tried?

See:

1 Like

Hi, from first of your links, i found this video. It shows exactly my case:

This is the situation:

1 minute video.

I tried with voila configuration → whitelist docx, but not works. Now, i think my only option will be use pydrive with credentials. Thanks anyway !.

Yes, and on that page there is an example here that agoose says works in voila and I just tested a variation of it.

Here is my set-up preparing for the demo.
I clicked here to open a session (happened to be based on this repo here, but that isn’t really important as it didn’t use the method they tried in the notebook there). Then when the session came up I used JupyterLab’s file navigator to duplicate voilademo.py and renamed it to voilademo.txt.

Here is my code that I put in a new notebook, and then hit the voila icon in the upper right side to try it in Voila:

# based on https://github.com/voila-dashboards/voila/issues/711#issuecomment-695872958
from ipywidgets import Output, Button
from IPython.display import HTML, clear_output
from base64 import b64encode

download_output = Output()
display(download_output)
def trigger_download(text, filename, kind='text/json'):
    # see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs for details
    content_b64 = b64encode(text.encode()).decode()
    data_url = f'data:{kind};charset=utf-8;base64,{content_b64}'
    js_code = f"""
        var a = document.createElement('a');
        a.setAttribute('download', '{filename}');
        a.setAttribute('href', '{data_url}');
        a.click()
    """
    with download_output:
        clear_output()
        display(HTML(f'<script>{js_code}</script>'))

btn = Button(description='download a text file')
file = open("voilademo.txt", "r")
s = file.read()
def download_text(e=None):
    trigger_download(s, 'voilademo.txt', kind='text/plain')
btn.on_click(download_text)
display(btn)

It worked and downloaded the contents of voilademo.txt while running in Voila.

Hopefullly, if you follow it step-by-step the demo will work for you and you can adapt the code to your situation.


Minor aside:
Please don’t share an old video that’s already in the linked issue and isn’t your issue as you aren’t running on that machine with those versions. (Or at least make it clear you are quoting something else. You can use markdown to make quote sections.) Plus, that video doesn’t share any useable code. The code with it would have made it potentially more useful for others to build on and test.

1 Like

I think the easiest solution would be the following:

import solara

filepath = "/Users/maartenbreddels/beach.jpeg"
data = open(filepath, "rb").read()
solara.FileDownload(data=data, label="Download image", filename="image.jpeg")

import ipywidgets as widgets
widgets.VBox([
    solara.FileDownload.widget(data=data, label="Download image", filename="image.jpeg")
])

inspired by:

2 Likes

I adapted the code you gave me to use it with word documents (.docx) with the help of GPT chat and it worked perfectly in voila, but executed from my computer. It was not the case with Binder, since using the same code and the same libraries, it did not work as you can see in the video. Maybe a more advanced configuration of binder is required to allow downloads. Thanks anyway.

import ipywidgets as ipw
from docx import Document
from IPython.display import HTML, clear_output
from base64 import b64encode

def trigger_download(content, filename, kind='application/vnd.openxmlformats-officedocument.wordprocessingml.document'):
    # see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs for details
    content_b64 = b64encode(content).decode()
    data_url = f'data:{kind};charset=utf-8;base64,{content_b64}'
    js_code = f"""
        var a = document.createElement('a');
        a.setAttribute('download', '{filename}');
        a.setAttribute('href', '{data_url}');
        a.click()
    """
    with download_output:
        clear_output()
        display(HTML(f'<script>{js_code}</script>'))

def download_docx(e=None):
    document = Document()
    document.add_heading(w1.value, 0)
    document.save(w1.value + '.docx')    
    with open(w1.value + '.docx', 'rb') as f:
        content = f.read()
    trigger_download(content, w1.value + '.docx')        
        
download_output = ipw.Output()
w1 = ipw.Text()
w2 = ipw.Button(description = 'Create')
w2.on_click(download_docx)
w3 = ipw.HBox([w1, w2, download_output])
display(w3)

Link → DownloadWidget.mp4 - Google Drive

Yes, I had also found that library in my search, but I need my app to work in binder and google colab, and unfortunately, solara widgets can not be displayed in google colab =(.

1 Like

Your adapted code works in launches from MyBinder.
Are you sure you don’t have pop-up blocker or virus blocker on?

And please when you have a public repo you are working in trying to get things to work on MyBInder, share the link to the repo and the launch URL. It makes it much easier for others to test what you are working on in the same environment as you.

I just tested it by launching via here (it’s using your repo here) and then switching to classic mode using the ‘Help’ menu of JupyterLab. (‘Help’ > ‘Launch Classic Notebook’ .) Then in classic mode I made a new notebook using the ‘New’ dropdown on the upper, right side and pasted in your code. Then I click on the Voila button on the toolbar above. In the Voila rendering, I was able to create and download .docx files with whatever I typed in the text input form. I do note that the correct .docx extension gets added when saving to my machine even though I cannot see it when I specify the file name in the download panel.

Wait, as long as you don’t try to run you code in JupyterLab, switching to the Voila interface from inside the JupyterLab interface allows your adapted code to work, too.

The only weirdness I see is that by using old versions of things in your requirements.txt, you’ve somehow broken things working in JupyterLab. (It’s always best to leave things unpinned and check if things work. And then if you know you need specific versions, then try pinning and see if things still work.) But that sort of makes since you are using a year-old version of Voila and ipywidgets version 7 had a lot of issues fixed in ipywidgets version 8. And that weirdness could be tied to the newer use of a different default server where ‘jupyter-notebook’ vs. ‘jupyter-server’, see here. Or a combination since you are trying to use old stuff that maybe isn’t fully compatible.

1 Like

Strange that it worked for you and not for me. I have 2 antivirus, but they have never generated problems when downloading a file, or in any case, they warn me if I am sure to make the download, but while I tried the code in binder, I did not get any message. Maybe it’s the antivirus or the ad block extension that I have installed in crhome, but I think it’s more likely to be because of the ipywidgets version, which as you mention, is not the most updated. For now, I am using pydrive and an html viewer to upload and display the generated docx (this allows to download it), however, when I finish I will test the code you provided me with the updated version of ipywidgets and if all goes well, I will publish it. Greetings and thanks for the support =).

Did you try the exact route I suggested? I used your code in the same type of mybinder session that you would have had because I launched from your repo, and it was the same one as you made your video. So it shouldn’t have anything to do with the ipywidgets version. That was only why it wasn’t working in JupyterLab launched from your repo.

There was an issue in Reacton, the underlying library of Solara that made the .widget(…) call not run some colab workarounds, which we do now.
If you install solara, and make sure you have pip install "reacton~=1.5.1" it should work. The advantage of the solution of Solara is that it should work in all environments, without having to tweak the server its running under.
Let me know if you run into other issues.

2 Likes