multi_col_table_helper.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. # -*- coding: utf-8 -*-
  2. from . import util
  3. # 得到多组列表
  4. # 用法举例:
  5. # helper = MultiColTableHelper(6,2,3) # 一页6行、一页2组(指2大列)、每组3列
  6. # helper.add_a_style_class('cell_sum', '''
  7. # .cell_sum{
  8. # font-size:18px;
  9. # }''')#add_a_style_class必须在add_group_data方法前面。可以有多个
  10. # helper.add_a_style_class('cell_content', '''
  11. # .cell_content{
  12. # font_size:16px;
  13. # border-right: 1px solid gray;
  14. # border-collapse:collapse;
  15. # border-spacing:0;
  16. # }''')#add_a_style_class必须在add_group_data方法前面。可以有多个
  17. # 只有一个数据,表示合计行;如果是一个也没有,则表示空行。==合计行的数据,格式需要自己处理==
  18. # helper.add_group_data([u'<td colspan="3" class="cell_sum">合计 21</td>']) # 合计行
  19. # helper.add_group_data(['a', 'b', 'c'], ['cell_content', 'cell_content', 'cell_content'])
  20. # helper.add_group_data(['1', '2', '3'], ['cell_content', 'cell_content', 'cell_content'])
  21. # helper.add_group_data(['4', '5', '6'], ['cell_content', 'cell_content', 'cell_content'])
  22. # helper.add_group_data(['7', '8', '9'], ['cell_content', 'cell_content', 'cell_content'])
  23. # helper.add_group_data(['11', '12', '13'], ['cell_content', 'cell_content', 'cell_content'])
  24. # helper.add_group_data(['14', '15', '16'], ['cell_content', 'cell_content', 'cell_content'])
  25. # helper.add_group_data([u'<td colspan="3" class="cell_sum">合计 22 其他 33</td>']) # 合计行
  26. # helper.add_group_data(['a', 'b', 'c'], ['cell_content', 'cell_content', 'cell_content'])
  27. # helper.add_group_data(['21', '22', '23'], ['cell_content', 'cell_content', 'cell_content'])
  28. # helper.set_between_table()
  29. # helper.get() # 结果参见后面的“多组列表”
  30. # ======多组列表======
  31. # 若数据为:
  32. # |合计 21 |14 |15 |16 |
  33. # |a |b |c |合计 22 其他 33 |
  34. # |1 |2 |3 |a |b |c |
  35. # |4 |5 |6 |21 |22 |23 |
  36. # |7 |8 |9 | |
  37. # |11 |12 |13 | |
  38. # 则:表示2组,每组有3列
  39. class MultiColTableHelper(object):
  40. default_title_attr = u'style="font-size:20px;" align="center"'
  41. def __init__(self, row_count, group_count, col_count_in_group):
  42. if group_count < 1:
  43. group_count = 1
  44. if row_count < 0:
  45. row_count = 0
  46. self._class_dic = {}
  47. self._row_count = row_count
  48. self._group_count = group_count
  49. self._col_count_in_group = col_count_in_group
  50. self._data = []
  51. self._string_between_table = u''
  52. self._title = u''
  53. self._title_attr = u''
  54. def add_a_style_class(self, class_name, class_content):
  55. self._class_dic[class_name] = class_content
  56. return
  57. def set_title(self, title, title_attr=''):
  58. self._title = title
  59. if title_attr:
  60. self._title_attr = title_attr
  61. else:
  62. self._title_attr = self.default_title_attr
  63. return
  64. def add_group_data(self, group_row, style_or_class=[]):
  65. data = []
  66. if not group_row:
  67. self._data.append(data) # 空行
  68. return
  69. if len(group_row) == 1:
  70. data.append(Cell(group_row[0], need_add_td=False)) # 一行只有一个数据。用于合计行。合计行需要包含格式
  71. else:
  72. if not style_or_class:
  73. for c in group_row:
  74. data.append(Cell(c))
  75. else:
  76. _len = len(style_or_class)
  77. if _len < len(group_row):
  78. tail = group_row[_len:] # 没有格式的
  79. self._add_cell(data, group_row, _len, style_or_class)
  80. for c in tail:
  81. data.append(Cell(c))
  82. else:
  83. self._add_cell(data, group_row, len(group_row), style_or_class)
  84. self._data.append(data)
  85. return
  86. def _add_cell(self, data, group_row, length, style_class):
  87. for i in range(length):
  88. if style_class[i] in self._class_dic:
  89. data.append(Cell(group_row[i], style_class_name=style_class[i]))
  90. else:
  91. data.append(Cell(group_row[i], style=style_class[i]))
  92. return
  93. def set_between_table(self, string_between_table='''<div style="padding-bottom:20px;"></div>'''):
  94. self._string_between_table = string_between_table
  95. def get(self):
  96. if not self._data:
  97. return u''
  98. if not self._row_count:
  99. return self._get_one_table_page()
  100. group_arr = util.list_split(self._data, self._row_count)
  101. page_arr = util.list_split(group_arr, self._group_count)
  102. table_data_list = []
  103. for group_columns in page_arr:
  104. row_list = MultiColTableHelper._get_row_list(group_columns, self._row_count, self._col_count_in_group)
  105. table_data_list.append(row_list)
  106. return self._get_table(table_data_list)
  107. def _get_one_table_page(self):
  108. remain = len(self._data) % self._group_count
  109. row_count = len(self._data) / self._group_count
  110. if remain > 0:
  111. row_count += 1
  112. group_arr = util.list_split(self._data, row_count)
  113. row_list = MultiColTableHelper._get_row_list(group_arr, row_count, self._col_count_in_group)
  114. return self._get_table([row_list])
  115. # 参数group_arr:
  116. # 为一个list,每一项表示一个组(多个列;每组的多个列一一对应)
  117. # 每个组,为一个list,每一项,表示这一组中的一行
  118. # 组中的一行,为一个list,每一项表示一个单元格的信息,即一个Cell对象
  119. # 参数row_count:
  120. # table的行数。也是group_arr中,每个组的最大行数(要么所有组为row_count行;要么前几组为row_count行,最后一组为小于row_count行)
  121. # 参数col_count_in_group:
  122. # 每个组里面的列的个数
  123. @staticmethod
  124. def _get_row_list(group_arr, row_count, col_count_in_group):
  125. result = []
  126. if not group_arr:
  127. return result
  128. need_add_empty = True
  129. for i in range(row_count):
  130. row = []
  131. empty_count = 0 # 最后一组中,为空的行数
  132. for group in group_arr:
  133. if len(group) > i:
  134. for cell in group[i]:
  135. row.append(cell)
  136. else:
  137. empty_count = row_count - i
  138. if need_add_empty and empty_count > 0:
  139. need_add_empty = False
  140. if empty_count == 1:
  141. cell = Cell(u'', attr=u'''colspan="%s"''' % col_count_in_group)
  142. else:
  143. cell = Cell(u'', attr=u'''colspan="%s" rowspan="%s"''' % (col_count_in_group, empty_count))
  144. row.append(cell)
  145. result.append(row)
  146. return result
  147. # 参数row_list_arr:
  148. # 为一个list,每一项,为一个table的数据;
  149. # 每个table的数据,为一个list,每一项表示一行数据;
  150. # 每一行数据,为一个list,每一项为一个Cell对象
  151. def _get_table(self, row_list_arr):
  152. result = []
  153. title = self._get_title()
  154. for table_row_list in row_list_arr:
  155. if title:
  156. result.append(title)
  157. table = Table()
  158. table.set_class(self._class_dic)
  159. table.add_rows(table_row_list)
  160. result.append(table.get())
  161. return self._string_between_table.join(result)
  162. def _get_title(self):
  163. if self._title:
  164. attr = self._title_attr if self._title_attr else self.default_title_attr
  165. return u'<div%s>%s</div>' % (u' ' + attr, self._title)
  166. return u''
  167. class Cell(object):
  168. def __init__(self, data, style_class_name=u'', style=u'', need_add_td=True, attr=u''):
  169. self._data = data
  170. self._style_class_name = style_class_name
  171. self._style = style
  172. self._need_add_td = need_add_td
  173. self._attr = attr
  174. def add_class(self, style_class):
  175. if self._style_class_name:
  176. self._style_class_name += ' ' + style_class
  177. else:
  178. self._style_class_name = style_class
  179. def get(self):
  180. if self._need_add_td:
  181. c = u''' class="%s"''' % self._style_class_name if self._style_class_name else u''
  182. s = u''' style="%s"''' % self._style if self._style else u''
  183. a = u' ' + self._attr if self._attr else u''
  184. return u'''<td%s%s%s>%s</td>''' % (c, s, a, self._data)
  185. return self._data
  186. class Table(object):
  187. table_start = u'''<table style="width:100%;border: 1px solid gray;font-size:15px;">
  188. '''
  189. table_end = u'''
  190. </table>
  191. '''
  192. line_start = u'''
  193. <tr>'''
  194. line_end = u'''
  195. </tr>'''
  196. style_format = u'''
  197. <style>
  198. table {
  199. border-collapse:collapse; /* 关键属性:合并表格内外边框(其实表格边框有2px,外面1px,里面还有1px哦) */
  200. border:solid #FFF; /* 设置边框属性:样式(solid=实线)、颜色(#FFF=黑) */
  201. border-width:1px 0 0 1px; /* 设置边框状粗细:上 右 下 左 = 对应:1px 0 0 1px */
  202. }
  203. table caption {font-size:14px;font-weight:bolder;}
  204. table th, table td {border:solid #999;border-width:0 1px 1px 0;padding:2px;} /* 设置表格每个td的边框, 只设置下侧和右侧的边框 */
  205. tfoot td {text-align:center;}
  206. %s
  207. </style>
  208. '''
  209. def __init__(self):
  210. self._rows = []
  211. self._class_dic = {}
  212. pass
  213. def set_class(self, class_dic):
  214. self._class_dic = class_dic
  215. def add_row(self, row):
  216. self._rows.append(row)
  217. return
  218. def add_rows(self, rows):
  219. self._rows += rows
  220. def get(self):
  221. result = self.table_start + self._get_style()
  222. for row in self._rows:
  223. result += self.line_start
  224. for cell in row:
  225. result += cell.get()
  226. result += self.line_end
  227. result += self.table_end
  228. return result
  229. def _get_style(self):
  230. if self._class_dic:
  231. v = '''
  232. '''.join(self._class_dic.values())
  233. else:
  234. v = ''
  235. return self.style_format % v