Row.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. # -*- coding: windows-1252 -*-
  2. from decimal import Decimal
  3. from . import BIFFRecords
  4. from . import Style
  5. from .Cell import StrCell, BlankCell, NumberCell, FormulaCell, MulBlankCell, BooleanCell, ErrorCell, \
  6. _get_cells_biff_data_mul
  7. from . import ExcelFormula
  8. import datetime as dt
  9. from .Formatting import Font
  10. from .compat import basestring, xrange, int_types, iteritems
  11. class Row(object):
  12. __slots__ = [# private variables
  13. "__idx",
  14. "__parent",
  15. "__parent_wb",
  16. "__cells",
  17. "__min_col_idx",
  18. "__max_col_idx",
  19. "__xf_index",
  20. "__has_default_xf_index",
  21. "__height_in_pixels",
  22. # public variables
  23. "height",
  24. "has_default_height",
  25. "height_mismatch",
  26. "level",
  27. "collapse",
  28. "hidden",
  29. "space_above",
  30. "space_below"]
  31. def __init__(self, rowx, parent_sheet):
  32. if not (isinstance(rowx, int_types) and 0 <= rowx <= 65535):
  33. raise ValueError("row index was %r, not allowed by .xls format" % rowx)
  34. self.__idx = rowx
  35. self.__parent = parent_sheet
  36. self.__parent_wb = parent_sheet.get_parent()
  37. self.__cells = {}
  38. self.__min_col_idx = 0
  39. self.__max_col_idx = 0
  40. self.__xf_index = 0x0F
  41. self.__has_default_xf_index = 0
  42. self.__height_in_pixels = 0x11
  43. self.height = 0x00FF
  44. self.has_default_height = 0x00
  45. self.height_mismatch = 0
  46. self.level = 0
  47. self.collapse = 0
  48. self.hidden = 0
  49. self.space_above = 0
  50. self.space_below = 0
  51. def __adjust_height(self, style):
  52. twips = style.font.height
  53. points = float(twips)/20.0
  54. # Cell height in pixels can be calcuted by following approx. formula:
  55. # cell height in pixels = font height in points * 83/50 + 2/5
  56. # It works when screen resolution is 96 dpi
  57. pix = int(round(points*83.0/50.0 + 2.0/5.0))
  58. if pix > self.__height_in_pixels:
  59. self.__height_in_pixels = pix
  60. def __adjust_bound_col_idx(self, *args):
  61. for arg in args:
  62. iarg = int(arg)
  63. if not ((0 <= iarg <= 255) and arg == iarg):
  64. raise ValueError("column index (%r) not an int in range(256)" % arg)
  65. sheet = self.__parent
  66. if iarg < self.__min_col_idx:
  67. self.__min_col_idx = iarg
  68. if iarg > self.__max_col_idx:
  69. self.__max_col_idx = iarg
  70. if iarg < sheet.first_used_col:
  71. sheet.first_used_col = iarg
  72. if iarg > sheet.last_used_col:
  73. sheet.last_used_col = iarg
  74. def __excel_date_dt(self, date):
  75. adj = False
  76. if isinstance(date, dt.date):
  77. if self.__parent_wb.dates_1904:
  78. epoch_tuple = (1904, 1, 1)
  79. else:
  80. epoch_tuple = (1899, 12, 31)
  81. adj = True
  82. if isinstance(date, dt.datetime):
  83. epoch = dt.datetime(*epoch_tuple)
  84. else:
  85. epoch = dt.date(*epoch_tuple)
  86. else: # it's a datetime.time instance
  87. date = dt.datetime.combine(dt.datetime(1900, 1, 1), date)
  88. epoch = dt.datetime(1900, 1, 1)
  89. delta = date - epoch
  90. xldate = delta.days + delta.seconds / 86400.0
  91. # Add a day for Excel's missing leap day in 1900
  92. if adj and xldate > 59:
  93. xldate += 1
  94. return xldate
  95. def get_height_in_pixels(self):
  96. return self.__height_in_pixels
  97. def set_style(self, style):
  98. self.__adjust_height(style)
  99. self.__xf_index = self.__parent_wb.add_style(style)
  100. self.__has_default_xf_index = 1
  101. def get_xf_index(self):
  102. return self.__xf_index
  103. def get_cells_count(self):
  104. return len(self.__cells)
  105. def get_min_col(self):
  106. return self.__min_col_idx
  107. def get_max_col(self):
  108. return self.__max_col_idx
  109. def get_row_biff_data(self):
  110. height_options = (self.height & 0x07FFF)
  111. height_options |= (self.has_default_height & 0x01) << 15
  112. options = (self.level & 0x07) << 0
  113. options |= (self.collapse & 0x01) << 4
  114. options |= (self.hidden & 0x01) << 5
  115. options |= (self.height_mismatch & 0x01) << 6
  116. options |= (self.__has_default_xf_index & 0x01) << 7
  117. options |= (0x01 & 0x01) << 8
  118. options |= (self.__xf_index & 0x0FFF) << 16
  119. options |= (self.space_above & 1) << 28
  120. options |= (self.space_below & 1) << 29
  121. return BIFFRecords.RowRecord(self.__idx, self.__min_col_idx,
  122. self.__max_col_idx, height_options, options).get()
  123. def insert_cell(self, col_index, cell_obj):
  124. if col_index in self.__cells:
  125. if not self.__parent._cell_overwrite_ok:
  126. msg = "Attempt to overwrite cell: sheetname=%r rowx=%d colx=%d" \
  127. % (self.__parent.name, self.__idx, col_index)
  128. raise Exception(msg)
  129. prev_cell_obj = self.__cells[col_index]
  130. sst_idx = getattr(prev_cell_obj, 'sst_idx', None)
  131. if sst_idx is not None:
  132. self.__parent_wb.del_str(sst_idx)
  133. self.__cells[col_index] = cell_obj
  134. def insert_mulcells(self, colx1, colx2, cell_obj):
  135. self.insert_cell(colx1, cell_obj)
  136. for col_index in xrange(colx1+1, colx2+1):
  137. self.insert_cell(col_index, None)
  138. def get_cells_biff_data(self):
  139. cell_items = [item for item in iteritems(self.__cells) if item[1] is not None]
  140. cell_items.sort() # in column order
  141. return _get_cells_biff_data_mul(self.__idx, cell_items)
  142. # previously:
  143. # return ''.join([cell.get_biff_data() for colx, cell in cell_items])
  144. def get_index(self):
  145. return self.__idx
  146. def set_cell_text(self, colx, value, style=Style.default_style):
  147. self.__adjust_height(style)
  148. self.__adjust_bound_col_idx(colx)
  149. xf_index = self.__parent_wb.add_style(style)
  150. self.insert_cell(colx, StrCell(self.__idx, colx, xf_index, self.__parent_wb.add_str(value)))
  151. def set_cell_blank(self, colx, style=Style.default_style):
  152. self.__adjust_height(style)
  153. self.__adjust_bound_col_idx(colx)
  154. xf_index = self.__parent_wb.add_style(style)
  155. self.insert_cell(colx, BlankCell(self.__idx, colx, xf_index))
  156. def set_cell_mulblanks(self, first_colx, last_colx, style=Style.default_style):
  157. assert 0 <= first_colx <= last_colx <= 255
  158. self.__adjust_height(style)
  159. self.__adjust_bound_col_idx(first_colx, last_colx)
  160. xf_index = self.__parent_wb.add_style(style)
  161. # ncols = last_colx - first_colx + 1
  162. self.insert_mulcells(first_colx, last_colx, MulBlankCell(self.__idx, first_colx, last_colx, xf_index))
  163. def set_cell_number(self, colx, number, style=Style.default_style):
  164. self.__adjust_height(style)
  165. self.__adjust_bound_col_idx(colx)
  166. xf_index = self.__parent_wb.add_style(style)
  167. self.insert_cell(colx, NumberCell(self.__idx, colx, xf_index, number))
  168. def set_cell_date(self, colx, datetime_obj, style=Style.default_style):
  169. self.__adjust_height(style)
  170. self.__adjust_bound_col_idx(colx)
  171. xf_index = self.__parent_wb.add_style(style)
  172. self.insert_cell(colx,
  173. NumberCell(self.__idx, colx, xf_index, self.__excel_date_dt(datetime_obj)))
  174. def set_cell_formula(self, colx, formula, style=Style.default_style, calc_flags=0):
  175. self.__adjust_height(style)
  176. self.__adjust_bound_col_idx(colx)
  177. xf_index = self.__parent_wb.add_style(style)
  178. self.__parent_wb.add_sheet_reference(formula)
  179. self.insert_cell(colx, FormulaCell(self.__idx, colx, xf_index, formula, calc_flags=0))
  180. def set_cell_boolean(self, colx, value, style=Style.default_style):
  181. self.__adjust_height(style)
  182. self.__adjust_bound_col_idx(colx)
  183. xf_index = self.__parent_wb.add_style(style)
  184. self.insert_cell(colx, BooleanCell(self.__idx, colx, xf_index, bool(value)))
  185. def set_cell_error(self, colx, error_string_or_code, style=Style.default_style):
  186. self.__adjust_height(style)
  187. self.__adjust_bound_col_idx(colx)
  188. xf_index = self.__parent_wb.add_style(style)
  189. self.insert_cell(colx, ErrorCell(self.__idx, colx, xf_index, error_string_or_code))
  190. def write(self, col, label, style=Style.default_style):
  191. self.__adjust_height(style)
  192. self.__adjust_bound_col_idx(col)
  193. style_index = self.__parent_wb.add_style(style)
  194. if isinstance(label, basestring):
  195. if len(label) > 0:
  196. self.insert_cell(col,
  197. StrCell(self.__idx, col, style_index, self.__parent_wb.add_str(label))
  198. )
  199. else:
  200. self.insert_cell(col, BlankCell(self.__idx, col, style_index))
  201. elif isinstance(label, bool): # bool is subclass of int; test bool first
  202. self.insert_cell(col, BooleanCell(self.__idx, col, style_index, label))
  203. elif isinstance(label, int_types+(float, Decimal)):
  204. self.insert_cell(col, NumberCell(self.__idx, col, style_index, label))
  205. elif isinstance(label, (dt.datetime, dt.date, dt.time)):
  206. date_number = self.__excel_date_dt(label)
  207. self.insert_cell(col, NumberCell(self.__idx, col, style_index, date_number))
  208. elif label is None:
  209. self.insert_cell(col, BlankCell(self.__idx, col, style_index))
  210. elif isinstance(label, ExcelFormula.Formula):
  211. self.__parent_wb.add_sheet_reference(label)
  212. self.insert_cell(col, FormulaCell(self.__idx, col, style_index, label))
  213. elif isinstance(label, (list, tuple)):
  214. self.__rich_text_helper(col, label, style, style_index)
  215. else:
  216. raise Exception("Unexpected data type %r" % type(label))
  217. def set_cell_rich_text(self, col, rich_text_list, style=Style.default_style):
  218. self.__adjust_height(style)
  219. self.__adjust_bound_col_idx(col)
  220. if not isinstance(rich_text_list, (list, tuple)):
  221. raise Exception("Unexpected data type %r" % type(rich_text_list))
  222. self.__rich_text_helper(col, rich_text_list, style)
  223. def __rich_text_helper(self, col, rich_text_list, style, style_index=None):
  224. if style_index is None:
  225. style_index = self.__parent_wb.add_style(style)
  226. default_font = None
  227. rt = []
  228. for data in rich_text_list:
  229. if isinstance(data, basestring):
  230. s = data
  231. font = default_font
  232. elif isinstance(data, (list, tuple)):
  233. if not isinstance(data[0], basestring) or not isinstance(data[1], Font):
  234. raise Exception ("Unexpected data type %r, %r" % (type(data[0]), type(data[1])))
  235. s = data[0]
  236. font = self.__parent_wb.add_font(data[1])
  237. else:
  238. raise Exception ("Unexpected data type %r" % type(data))
  239. if s:
  240. rt.append((s, font))
  241. if default_font is None:
  242. default_font = self.__parent_wb.add_font(style.font)
  243. if rt:
  244. self.insert_cell(col, StrCell(self.__idx, col, style_index, self.__parent_wb.add_rt(rt)))
  245. else:
  246. self.insert_cell(col, BlankCell(self.__idx, col, style_index))
  247. write_blanks = set_cell_mulblanks
  248. write_rich_text = set_cell_rich_text