_xlwt.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import pandas._libs.json as json
  2. from pandas.io.excel._base import ExcelWriter
  3. from pandas.io.excel._util import _validate_freeze_panes
  4. class _XlwtWriter(ExcelWriter):
  5. engine = "xlwt"
  6. supported_extensions = (".xls",)
  7. def __init__(self, path, engine=None, encoding=None, mode="w", **engine_kwargs):
  8. # Use the xlwt module as the Excel writer.
  9. import xlwt
  10. engine_kwargs["engine"] = engine
  11. if mode == "a":
  12. raise ValueError("Append mode is not supported with xlwt!")
  13. super().__init__(path, mode=mode, **engine_kwargs)
  14. if encoding is None:
  15. encoding = "ascii"
  16. self.book = xlwt.Workbook(encoding=encoding)
  17. self.fm_datetime = xlwt.easyxf(num_format_str=self.datetime_format)
  18. self.fm_date = xlwt.easyxf(num_format_str=self.date_format)
  19. def save(self):
  20. """
  21. Save workbook to disk.
  22. """
  23. return self.book.save(self.path)
  24. def write_cells(
  25. self, cells, sheet_name=None, startrow=0, startcol=0, freeze_panes=None
  26. ):
  27. # Write the frame cells using xlwt.
  28. sheet_name = self._get_sheet_name(sheet_name)
  29. if sheet_name in self.sheets:
  30. wks = self.sheets[sheet_name]
  31. else:
  32. wks = self.book.add_sheet(sheet_name)
  33. self.sheets[sheet_name] = wks
  34. if _validate_freeze_panes(freeze_panes):
  35. wks.set_panes_frozen(True)
  36. wks.set_horz_split_pos(freeze_panes[0])
  37. wks.set_vert_split_pos(freeze_panes[1])
  38. style_dict = {}
  39. for cell in cells:
  40. val, fmt = self._value_with_fmt(cell.val)
  41. stylekey = json.dumps(cell.style)
  42. if fmt:
  43. stylekey += fmt
  44. if stylekey in style_dict:
  45. style = style_dict[stylekey]
  46. else:
  47. style = self._convert_to_style(cell.style, fmt)
  48. style_dict[stylekey] = style
  49. if cell.mergestart is not None and cell.mergeend is not None:
  50. wks.write_merge(
  51. startrow + cell.row,
  52. startrow + cell.mergestart,
  53. startcol + cell.col,
  54. startcol + cell.mergeend,
  55. val,
  56. style,
  57. )
  58. else:
  59. wks.write(startrow + cell.row, startcol + cell.col, val, style)
  60. @classmethod
  61. def _style_to_xlwt(
  62. cls, item, firstlevel: bool = True, field_sep=",", line_sep=";"
  63. ) -> str:
  64. """helper which recursively generate an xlwt easy style string
  65. for example:
  66. hstyle = {"font": {"bold": True},
  67. "border": {"top": "thin",
  68. "right": "thin",
  69. "bottom": "thin",
  70. "left": "thin"},
  71. "align": {"horiz": "center"}}
  72. will be converted to
  73. font: bold on; \
  74. border: top thin, right thin, bottom thin, left thin; \
  75. align: horiz center;
  76. """
  77. if hasattr(item, "items"):
  78. if firstlevel:
  79. it = [
  80. f"{key}: {cls._style_to_xlwt(value, False)}"
  81. for key, value in item.items()
  82. ]
  83. out = f"{(line_sep).join(it)} "
  84. return out
  85. else:
  86. it = [
  87. f"{key} {cls._style_to_xlwt(value, False)}"
  88. for key, value in item.items()
  89. ]
  90. out = f"{(field_sep).join(it)} "
  91. return out
  92. else:
  93. item = f"{item}"
  94. item = item.replace("True", "on")
  95. item = item.replace("False", "off")
  96. return item
  97. @classmethod
  98. def _convert_to_style(cls, style_dict, num_format_str=None):
  99. """
  100. converts a style_dict to an xlwt style object
  101. Parameters
  102. ----------
  103. style_dict : style dictionary to convert
  104. num_format_str : optional number format string
  105. """
  106. import xlwt
  107. if style_dict:
  108. xlwt_stylestr = cls._style_to_xlwt(style_dict)
  109. style = xlwt.easyxf(xlwt_stylestr, field_sep=",", line_sep=";")
  110. else:
  111. style = xlwt.XFStyle()
  112. if num_format_str is not None:
  113. style.num_format_str = num_format_str
  114. return style