styleable.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. # Copyright (c) 2010-2024 openpyxl
  2. from copy import copy
  3. from .numbers import (
  4. BUILTIN_FORMATS,
  5. BUILTIN_FORMATS_MAX_SIZE,
  6. BUILTIN_FORMATS_REVERSE,
  7. )
  8. from .proxy import StyleProxy
  9. from .cell_style import StyleArray
  10. from .named_styles import NamedStyle
  11. from .builtins import styles
  12. class StyleDescriptor:
  13. def __init__(self, collection, key):
  14. self.collection = collection
  15. self.key = key
  16. def __set__(self, instance, value):
  17. coll = getattr(instance.parent.parent, self.collection)
  18. if not getattr(instance, "_style"):
  19. instance._style = StyleArray()
  20. setattr(instance._style, self.key, coll.add(value))
  21. def __get__(self, instance, cls):
  22. coll = getattr(instance.parent.parent, self.collection)
  23. if not getattr(instance, "_style"):
  24. instance._style = StyleArray()
  25. idx = getattr(instance._style, self.key)
  26. return StyleProxy(coll[idx])
  27. class NumberFormatDescriptor:
  28. key = "numFmtId"
  29. collection = '_number_formats'
  30. def __set__(self, instance, value):
  31. coll = getattr(instance.parent.parent, self.collection)
  32. if value in BUILTIN_FORMATS_REVERSE:
  33. idx = BUILTIN_FORMATS_REVERSE[value]
  34. else:
  35. idx = coll.add(value) + BUILTIN_FORMATS_MAX_SIZE
  36. if not getattr(instance, "_style"):
  37. instance._style = StyleArray()
  38. setattr(instance._style, self.key, idx)
  39. def __get__(self, instance, cls):
  40. if not getattr(instance, "_style"):
  41. instance._style = StyleArray()
  42. idx = getattr(instance._style, self.key)
  43. if idx < BUILTIN_FORMATS_MAX_SIZE:
  44. return BUILTIN_FORMATS.get(idx, "General")
  45. coll = getattr(instance.parent.parent, self.collection)
  46. return coll[idx - BUILTIN_FORMATS_MAX_SIZE]
  47. class NamedStyleDescriptor:
  48. key = "xfId"
  49. collection = "_named_styles"
  50. def __set__(self, instance, value):
  51. if not getattr(instance, "_style"):
  52. instance._style = StyleArray()
  53. coll = getattr(instance.parent.parent, self.collection)
  54. if isinstance(value, NamedStyle):
  55. style = value
  56. if style not in coll:
  57. instance.parent.parent.add_named_style(style)
  58. elif value not in coll.names:
  59. if value in styles: # is it builtin?
  60. style = styles[value]
  61. if style not in coll:
  62. instance.parent.parent.add_named_style(style)
  63. else:
  64. raise ValueError("{0} is not a known style".format(value))
  65. else:
  66. style = coll[value]
  67. instance._style = copy(style.as_tuple())
  68. def __get__(self, instance, cls):
  69. if not getattr(instance, "_style"):
  70. instance._style = StyleArray()
  71. idx = getattr(instance._style, self.key)
  72. coll = getattr(instance.parent.parent, self.collection)
  73. return coll.names[idx]
  74. class StyleArrayDescriptor:
  75. def __init__(self, key):
  76. self.key = key
  77. def __set__(self, instance, value):
  78. if instance._style is None:
  79. instance._style = StyleArray()
  80. setattr(instance._style, self.key, value)
  81. def __get__(self, instance, cls):
  82. if instance._style is None:
  83. return False
  84. return bool(getattr(instance._style, self.key))
  85. class StyleableObject:
  86. """
  87. Base class for styleble objects implementing proxy and lookup functions
  88. """
  89. font = StyleDescriptor('_fonts', "fontId")
  90. fill = StyleDescriptor('_fills', "fillId")
  91. border = StyleDescriptor('_borders', "borderId")
  92. number_format = NumberFormatDescriptor()
  93. protection = StyleDescriptor('_protections', "protectionId")
  94. alignment = StyleDescriptor('_alignments', "alignmentId")
  95. style = NamedStyleDescriptor()
  96. quotePrefix = StyleArrayDescriptor('quotePrefix')
  97. pivotButton = StyleArrayDescriptor('pivotButton')
  98. __slots__ = ('parent', '_style')
  99. def __init__(self, sheet, style_array=None):
  100. self.parent = sheet
  101. if style_array is not None:
  102. style_array = StyleArray(style_array)
  103. self._style = style_array
  104. @property
  105. def style_id(self):
  106. if self._style is None:
  107. self._style = StyleArray()
  108. return self.parent.parent._cell_styles.add(self._style)
  109. @property
  110. def has_style(self):
  111. if self._style is None:
  112. return False
  113. return any(self._style)