import pandas._libs.json as json from pandas.io.excel._base import ExcelWriter from pandas.io.excel._util import _validate_freeze_panes class _XlwtWriter(ExcelWriter): engine = "xlwt" supported_extensions = (".xls",) def __init__(self, path, engine=None, encoding=None, mode="w", **engine_kwargs): # Use the xlwt module as the Excel writer. import xlwt engine_kwargs["engine"] = engine if mode == "a": raise ValueError("Append mode is not supported with xlwt!") super().__init__(path, mode=mode, **engine_kwargs) if encoding is None: encoding = "ascii" self.book = xlwt.Workbook(encoding=encoding) self.fm_datetime = xlwt.easyxf(num_format_str=self.datetime_format) self.fm_date = xlwt.easyxf(num_format_str=self.date_format) def save(self): """ Save workbook to disk. """ return self.book.save(self.path) def write_cells( self, cells, sheet_name=None, startrow=0, startcol=0, freeze_panes=None ): # Write the frame cells using xlwt. sheet_name = self._get_sheet_name(sheet_name) if sheet_name in self.sheets: wks = self.sheets[sheet_name] else: wks = self.book.add_sheet(sheet_name) self.sheets[sheet_name] = wks if _validate_freeze_panes(freeze_panes): wks.set_panes_frozen(True) wks.set_horz_split_pos(freeze_panes[0]) wks.set_vert_split_pos(freeze_panes[1]) style_dict = {} for cell in cells: val, fmt = self._value_with_fmt(cell.val) stylekey = json.dumps(cell.style) if fmt: stylekey += fmt if stylekey in style_dict: style = style_dict[stylekey] else: style = self._convert_to_style(cell.style, fmt) style_dict[stylekey] = style if cell.mergestart is not None and cell.mergeend is not None: wks.write_merge( startrow + cell.row, startrow + cell.mergestart, startcol + cell.col, startcol + cell.mergeend, val, style, ) else: wks.write(startrow + cell.row, startcol + cell.col, val, style) @classmethod def _style_to_xlwt(cls, item, firstlevel=True, field_sep=",", line_sep=";"): """helper which recursively generate an xlwt easy style string for example: hstyle = {"font": {"bold": True}, "border": {"top": "thin", "right": "thin", "bottom": "thin", "left": "thin"}, "align": {"horiz": "center"}} will be converted to font: bold on; \ border: top thin, right thin, bottom thin, left thin; \ align: horiz center; """ if hasattr(item, "items"): if firstlevel: it = [ "{key}: {val}".format(key=key, val=cls._style_to_xlwt(value, False)) for key, value in item.items() ] out = "{sep} ".format(sep=(line_sep).join(it)) return out else: it = [ "{key} {val}".format(key=key, val=cls._style_to_xlwt(value, False)) for key, value in item.items() ] out = "{sep} ".format(sep=(field_sep).join(it)) return out else: item = "{item}".format(item=item) item = item.replace("True", "on") item = item.replace("False", "off") return item @classmethod def _convert_to_style(cls, style_dict, num_format_str=None): """ converts a style_dict to an xlwt style object Parameters ---------- style_dict : style dictionary to convert num_format_str : optional number format string """ import xlwt if style_dict: xlwt_stylestr = cls._style_to_xlwt(style_dict) style = xlwt.easyxf(xlwt_stylestr, field_sep=",", line_sep=";") else: style = xlwt.XFStyle() if num_format_str is not None: style.num_format_str = num_format_str return style