merge.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. from __future__ import absolute_import
  2. # Copyright (c) 2010-2019 openpyxl
  3. from openpyxl.descriptors.serialisable import Serialisable
  4. from openpyxl.descriptors import (
  5. Integer,
  6. Sequence,
  7. )
  8. from openpyxl.cell.cell import MergedCell
  9. from openpyxl.styles.borders import Border
  10. from .cell_range import CellRange
  11. class MergeCell(CellRange):
  12. tagname = "mergeCell"
  13. ref = CellRange.coord
  14. __attrs__ = ("ref",)
  15. def __init__(self,
  16. ref=None,
  17. ):
  18. super(MergeCell, self).__init__(ref)
  19. def __copy__(self):
  20. return self.__class__(self.ref)
  21. class MergeCells(Serialisable):
  22. tagname = "mergeCells"
  23. count = Integer(allow_none=True)
  24. mergeCell = Sequence(expected_type=MergeCell, )
  25. __elements__ = ('mergeCell',)
  26. __attrs__ = ('count',)
  27. def __init__(self,
  28. count=None,
  29. mergeCell=(),
  30. ):
  31. self.mergeCell = mergeCell
  32. @property
  33. def count(self):
  34. return len(self.mergeCell)
  35. class MergedCellRange(CellRange):
  36. """
  37. MergedCellRange stores the border information of a merged cell in the top
  38. left cell of the merged cell.
  39. The remaining cells in the merged cell are stored as MergedCell objects and
  40. get their border information from the upper left cell.
  41. """
  42. def __init__(self, worksheet, coord):
  43. self.ws = worksheet
  44. super(MergedCellRange, self).__init__(range_string=coord)
  45. self.start_cell = None
  46. self._get_borders()
  47. def _get_borders(self):
  48. """
  49. If the upper left cell of the merged cell does not yet exist, it is
  50. created.
  51. The upper left cell gets the border information of the bottom and right
  52. border from the bottom right cell of the merged cell, if available.
  53. """
  54. # Top-left cell.
  55. self.start_cell = self.ws._cells.get((self.min_row, self.min_col))
  56. if self.start_cell is None:
  57. self.start_cell = self.ws.cell(row=self.min_row, column=self.min_col)
  58. # Bottom-right cell
  59. end_cell = self.ws._cells.get((self.max_row, self.max_col))
  60. if end_cell is not None:
  61. self.start_cell.border += Border(right=end_cell.border.right,
  62. bottom=end_cell.border.bottom)
  63. def format(self):
  64. """
  65. Each cell of the merged cell is created as MergedCell if it does not
  66. already exist.
  67. The MergedCells at the edge of the merged cell gets its borders from
  68. the upper left cell.
  69. - The top MergedCells get the top border from the top left cell.
  70. - The bottom MergedCells get the bottom border from the top left cell.
  71. - The left MergedCells get the left border from the top left cell.
  72. - The right MergedCells get the right border from the top left cell.
  73. """
  74. names = ['top', 'left', 'right', 'bottom']
  75. for name in names:
  76. side = getattr(self.start_cell.border, name)
  77. border = Border(**{name:side})
  78. for coord in getattr(self, name):
  79. cell = self.ws._cells.get(coord)
  80. if cell is None:
  81. row, col = coord
  82. cell = MergedCell(self.ws, row=row, column=col)
  83. self.ws._cells[(cell.row, cell.column)] = cell
  84. cell.border += border