1

I am implementing a button from ipywidget that by clicking it, it downloads an Excel created from a formatted/styled dataframe (i.e. with cells highlighted in colors).

If it is pure data (no color styling etc), I could have saved the data into csv format and solve the problem based on this thread How to download a file using ipywidget button?.

My code

import ipywidgets
import numpy as np
import pandas as pd

from IPython.display import HTML, display
from ipywidgets import widgets

import pandas.io.formats.style

class DownloadButtonExcel(ipywidgets.Button):
    """
    Download button with dynamic content
    The content is generated using a callback when the button is clicked.
    The code is based on ollik1's answer at https://stackoverflow.com/questions/61708701/how-to-download-a-file-using-ipywidget-button/68683463#68683463
    """

    def __init__(self, filename: str, contents: Callable[[], pandas.io.formats.style.Styler], **kwargs):
        super(DownloadButtonExcel, self).__init__(**kwargs)
        self.filename = filename
        self.contents = contents
        self.on_click(self.__on_click)
        self.output = widgets.Output()
        display(self.output)

    def __on_click(self, b):
        df = self.contents()
        df.to_excel(self.filename, engine='openpyxl')
        digest = pd.util.hash_pandas_object(df.data).sum()  # bypass browser cache        
        id = f"dl_{digest}"

        with self.output:
            display(
                HTML(
                    f"""
                        <html>
                        <body>
                        <a id="{id}" download ="{self.filename}" href="{self.filename}" download>
                        </a>

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

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



import pandas as pd
import numpy as np
import matplotlib as mpl

df = pd.DataFrame([[38.0, 2.0, 18.0, 22.0, 21, np.nan],[19, 439, 6, 452, 226,232]],
                  index=pd.Index(['Tumour (Positive)', 'Non-Tumour (Negative)'], name='Actual Label:'),
                  columns=pd.MultiIndex.from_product([['Decision Tree', 'Regression', 'Random'],['Tumour', 'Non-Tumour']], names=['Model:', 'Predicted:']))

df_style = df.style.format(precision = 2).background_gradient().hide(axis = 'index')

download_button_excel = DownloadButtonExcel(
    filename="Test.xlsx",
    contents=lambda: df_style,
    description="Download",
    style={"button_color": "transparent"},
)

The above code creates a button and download the Excel file with correct format. Different from csv case, where the data can be encoded/decoded and saved in the cache, for current implementation, the df.to_excel(self.filename, engine='openpyxl') code downloads the Excel to local folder.

I am not sure (1) how to encode/decode pandas Styler just as in csv case and implement it using that approach. (2) I feel I could have moved df.to_excel(self.filename, engine='openpyxl') to inside function download(), so that the click on button triggers download. But I don't know how.

Any help would be greatly appreciated.

Chenyang
  • 161
  • 1
  • 11
  • Hi mate, pandas dataframe styles can be shown in HTML only, if you want it to be shown on excel, you should use excel writer, - https://pandas.pydata.org/docs/reference/api/pandas.ExcelWriter.html and https://xlsxwriter.readthedocs.io/working_with_pandas.html – NoobVB Sep 16 '22 at 10:41

0 Answers0