styleable.py 4.5 KB

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