axis.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  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. Typed,
  6. Float,
  7. NoneSet,
  8. Bool,
  9. Integer,
  10. MinMax,
  11. NoneSet,
  12. Set,
  13. String,
  14. Alias,
  15. )
  16. from openpyxl.descriptors.excel import (
  17. ExtensionList,
  18. Percentage,
  19. _explicit_none,
  20. )
  21. from openpyxl.descriptors.nested import (
  22. NestedValue,
  23. NestedSet,
  24. NestedBool,
  25. NestedNoneSet,
  26. NestedFloat,
  27. NestedInteger,
  28. NestedMinMax,
  29. )
  30. from openpyxl.xml.constants import CHART_NS
  31. from .descriptors import NumberFormatDescriptor
  32. from .layout import Layout
  33. from .text import Text, RichText
  34. from .shapes import GraphicalProperties
  35. from .title import Title, TitleDescriptor
  36. class ChartLines(Serialisable):
  37. tagname = "chartLines"
  38. spPr = Typed(expected_type=GraphicalProperties, allow_none=True)
  39. graphicalProperties = Alias('spPr')
  40. def __init__(self, spPr=None):
  41. self.spPr = spPr
  42. class Scaling(Serialisable):
  43. tagname = "scaling"
  44. logBase = NestedFloat(allow_none=True)
  45. orientation = NestedSet(values=(['maxMin', 'minMax']))
  46. max = NestedFloat(allow_none=True)
  47. min = NestedFloat(allow_none=True)
  48. extLst = Typed(expected_type=ExtensionList, allow_none=True)
  49. __elements__ = ('logBase', 'orientation', 'max', 'min',)
  50. def __init__(self,
  51. logBase=None,
  52. orientation="minMax",
  53. max=None,
  54. min=None,
  55. extLst=None,
  56. ):
  57. self.logBase = logBase
  58. self.orientation = orientation
  59. self.max = max
  60. self.min = min
  61. class _BaseAxis(Serialisable):
  62. axId = NestedInteger(expected_type=int)
  63. scaling = Typed(expected_type=Scaling)
  64. delete = NestedBool(allow_none=True)
  65. axPos = NestedSet(values=(['b', 'l', 'r', 't']))
  66. majorGridlines = Typed(expected_type=ChartLines, allow_none=True)
  67. minorGridlines = Typed(expected_type=ChartLines, allow_none=True)
  68. title = TitleDescriptor()
  69. numFmt = NumberFormatDescriptor()
  70. number_format = Alias("numFmt")
  71. majorTickMark = NestedNoneSet(values=(['cross', 'in', 'out']), to_tree=_explicit_none)
  72. minorTickMark = NestedNoneSet(values=(['cross', 'in', 'out']), to_tree=_explicit_none)
  73. tickLblPos = NestedNoneSet(values=(['high', 'low', 'nextTo']))
  74. spPr = Typed(expected_type=GraphicalProperties, allow_none=True)
  75. graphicalProperties = Alias('spPr')
  76. txPr = Typed(expected_type=RichText, allow_none=True)
  77. textProperties = Alias('txPr')
  78. crossAx = NestedInteger(expected_type=int) # references other axis
  79. crosses = NestedNoneSet(values=(['autoZero', 'max', 'min']))
  80. crossesAt = NestedFloat(allow_none=True)
  81. # crosses & crossesAt are mutually exclusive
  82. __elements__ = ('axId', 'scaling', 'delete', 'axPos', 'majorGridlines',
  83. 'minorGridlines', 'title', 'numFmt', 'majorTickMark', 'minorTickMark',
  84. 'tickLblPos', 'spPr', 'txPr', 'crossAx', 'crosses', 'crossesAt')
  85. def __init__(self,
  86. axId=None,
  87. scaling=None,
  88. delete=None,
  89. axPos='l',
  90. majorGridlines=None,
  91. minorGridlines=None,
  92. title=None,
  93. numFmt=None,
  94. majorTickMark=None,
  95. minorTickMark=None,
  96. tickLblPos=None,
  97. spPr=None,
  98. txPr= None,
  99. crossAx=None,
  100. crosses=None,
  101. crossesAt=None,
  102. ):
  103. self.axId = axId
  104. if scaling is None:
  105. scaling = Scaling()
  106. self.scaling = scaling
  107. self.delete = delete
  108. self.axPos = axPos
  109. self.majorGridlines = majorGridlines
  110. self.minorGridlines = minorGridlines
  111. self.title = title
  112. self.numFmt = numFmt
  113. self.majorTickMark = majorTickMark
  114. self.minorTickMark = minorTickMark
  115. self.tickLblPos = tickLblPos
  116. self.spPr = spPr
  117. self.txPr = txPr
  118. self.crossAx = crossAx
  119. self.crosses = crosses
  120. self.crossesAt = None
  121. class DisplayUnitsLabel(Serialisable):
  122. tagname = "dispUnitsLbl"
  123. layout = Typed(expected_type=Layout, allow_none=True)
  124. tx = Typed(expected_type=Text, allow_none=True)
  125. text = Alias("tx")
  126. spPr = Typed(expected_type=GraphicalProperties, allow_none=True)
  127. graphicalProperties = Alias("spPr")
  128. txPr = Typed(expected_type=RichText, allow_none=True)
  129. textPropertes = Alias("txPr")
  130. __elements__ = ('layout', 'tx', 'spPr', 'txPr')
  131. def __init__(self,
  132. layout=None,
  133. tx=None,
  134. spPr=None,
  135. txPr=None,
  136. ):
  137. self.layout = layout
  138. self.tx = tx
  139. self.spPr = spPr
  140. self.txPr = txPr
  141. class DisplayUnitsLabelList(Serialisable):
  142. tagname = "dispUnits"
  143. custUnit = NestedFloat(allow_none=True)
  144. builtInUnit = NestedNoneSet(values=(['hundreds', 'thousands',
  145. 'tenThousands', 'hundredThousands', 'millions', 'tenMillions',
  146. 'hundredMillions', 'billions', 'trillions']))
  147. dispUnitsLbl = Typed(expected_type=DisplayUnitsLabel, allow_none=True)
  148. extLst = Typed(expected_type=ExtensionList, allow_none=True)
  149. __elements__ = ('custUnit', 'builtInUnit', 'dispUnitsLbl',)
  150. def __init__(self,
  151. custUnit=None,
  152. builtInUnit=None,
  153. dispUnitsLbl=None,
  154. extLst=None,
  155. ):
  156. self.custUnit = custUnit
  157. self.builtInUnit = builtInUnit
  158. self.dispUnitsLbl = dispUnitsLbl
  159. class NumericAxis(_BaseAxis):
  160. tagname = "valAx"
  161. axId = _BaseAxis.axId
  162. scaling = _BaseAxis.scaling
  163. delete = _BaseAxis.delete
  164. axPos = _BaseAxis.axPos
  165. majorGridlines = _BaseAxis.majorGridlines
  166. minorGridlines = _BaseAxis.minorGridlines
  167. title = _BaseAxis.title
  168. numFmt = _BaseAxis.numFmt
  169. majorTickMark = _BaseAxis.majorTickMark
  170. minorTickMark = _BaseAxis.minorTickMark
  171. tickLblPos = _BaseAxis.tickLblPos
  172. spPr = _BaseAxis.spPr
  173. txPr = _BaseAxis.txPr
  174. crossAx = _BaseAxis.crossAx
  175. crosses = _BaseAxis.crosses
  176. crossesAt = _BaseAxis.crossesAt
  177. crossBetween = NestedNoneSet(values=(['between', 'midCat']))
  178. majorUnit = NestedFloat(allow_none=True)
  179. minorUnit = NestedFloat(allow_none=True)
  180. dispUnits = Typed(expected_type=DisplayUnitsLabelList, allow_none=True)
  181. extLst = Typed(expected_type=ExtensionList, allow_none=True)
  182. __elements__ = _BaseAxis.__elements__ + ('crossBetween', 'majorUnit',
  183. 'minorUnit', 'dispUnits',)
  184. def __init__(self,
  185. crossBetween=None,
  186. majorUnit=None,
  187. minorUnit=None,
  188. dispUnits=None,
  189. extLst=None,
  190. **kw
  191. ):
  192. self.crossBetween = crossBetween
  193. self.majorUnit = majorUnit
  194. self.minorUnit = minorUnit
  195. self.dispUnits = dispUnits
  196. kw.setdefault('majorGridlines', ChartLines())
  197. kw.setdefault('axId', 100)
  198. kw.setdefault('crossAx', 10)
  199. super(NumericAxis, self).__init__(**kw)
  200. @classmethod
  201. def from_tree(cls, node):
  202. """
  203. Special case value axes with no gridlines
  204. """
  205. self = super(NumericAxis, cls).from_tree(node)
  206. gridlines = node.find("{%s}majorGridlines" % CHART_NS)
  207. if gridlines is None:
  208. self.majorGridlines = None
  209. return self
  210. class TextAxis(_BaseAxis):
  211. tagname = "catAx"
  212. axId = _BaseAxis.axId
  213. scaling = _BaseAxis.scaling
  214. delete = _BaseAxis.delete
  215. axPos = _BaseAxis.axPos
  216. majorGridlines = _BaseAxis.majorGridlines
  217. minorGridlines = _BaseAxis.minorGridlines
  218. title = _BaseAxis.title
  219. numFmt = _BaseAxis.numFmt
  220. majorTickMark = _BaseAxis.majorTickMark
  221. minorTickMark = _BaseAxis.minorTickMark
  222. tickLblPos = _BaseAxis.tickLblPos
  223. spPr = _BaseAxis.spPr
  224. txPr = _BaseAxis.txPr
  225. crossAx = _BaseAxis.crossAx
  226. crosses = _BaseAxis.crosses
  227. crossesAt = _BaseAxis.crossesAt
  228. auto = NestedBool(allow_none=True)
  229. lblAlgn = NestedNoneSet(values=(['ctr', 'l', 'r']))
  230. lblOffset = NestedMinMax(min=0, max=1000)
  231. tickLblSkip = NestedInteger(allow_none=True)
  232. tickMarkSkip = NestedInteger(allow_none=True)
  233. noMultiLvlLbl = NestedBool(allow_none=True)
  234. extLst = Typed(expected_type=ExtensionList, allow_none=True)
  235. __elements__ = _BaseAxis.__elements__ + ('auto', 'lblAlgn', 'lblOffset',
  236. 'tickLblSkip', 'tickMarkSkip', 'noMultiLvlLbl')
  237. def __init__(self,
  238. auto=None,
  239. lblAlgn=None,
  240. lblOffset=100,
  241. tickLblSkip=None,
  242. tickMarkSkip=None,
  243. noMultiLvlLbl=None,
  244. extLst=None,
  245. **kw
  246. ):
  247. self.auto = auto
  248. self.lblAlgn = lblAlgn
  249. self.lblOffset = lblOffset
  250. self.tickLblSkip = tickLblSkip
  251. self.tickMarkSkip = tickMarkSkip
  252. self.noMultiLvlLbl = noMultiLvlLbl
  253. kw.setdefault('axId', 10)
  254. kw.setdefault('crossAx', 100)
  255. super(TextAxis, self).__init__(**kw)
  256. class DateAxis(TextAxis):
  257. tagname = "dateAx"
  258. axId = _BaseAxis.axId
  259. scaling = _BaseAxis.scaling
  260. delete = _BaseAxis.delete
  261. axPos = _BaseAxis.axPos
  262. majorGridlines = _BaseAxis.majorGridlines
  263. minorGridlines = _BaseAxis.minorGridlines
  264. title = _BaseAxis.title
  265. numFmt = _BaseAxis.numFmt
  266. majorTickMark = _BaseAxis.majorTickMark
  267. minorTickMark = _BaseAxis.minorTickMark
  268. tickLblPos = _BaseAxis.tickLblPos
  269. spPr = _BaseAxis.spPr
  270. txPr = _BaseAxis.txPr
  271. crossAx = _BaseAxis.crossAx
  272. crosses = _BaseAxis.crosses
  273. crossesAt = _BaseAxis.crossesAt
  274. auto = NestedBool(allow_none=True)
  275. lblOffset = NestedInteger(allow_none=True)
  276. baseTimeUnit = NestedNoneSet(values=(['days', 'months', 'years']))
  277. majorUnit = NestedFloat(allow_none=True)
  278. majorTimeUnit = NestedNoneSet(values=(['days', 'months', 'years']))
  279. minorUnit = NestedFloat(allow_none=True)
  280. minorTimeUnit = NestedNoneSet(values=(['days', 'months', 'years']))
  281. extLst = Typed(expected_type=ExtensionList, allow_none=True)
  282. __elements__ = _BaseAxis.__elements__ + ('auto', 'lblOffset',
  283. 'baseTimeUnit', 'majorUnit', 'majorTimeUnit', 'minorUnit',
  284. 'minorTimeUnit')
  285. def __init__(self,
  286. auto=None,
  287. lblOffset=None,
  288. baseTimeUnit=None,
  289. majorUnit=None,
  290. majorTimeUnit=None,
  291. minorUnit=None,
  292. minorTimeUnit=None,
  293. extLst=None,
  294. **kw
  295. ):
  296. self.auto = auto
  297. self.lblOffset = lblOffset
  298. self.baseTimeUnit = baseTimeUnit
  299. self.majorUnit = majorUnit
  300. self.majorTimeUnit = majorTimeUnit
  301. self.minorUnit = minorUnit
  302. self.minorTimeUnit = minorTimeUnit
  303. kw.setdefault('axId', 500)
  304. kw.setdefault('lblOffset', lblOffset)
  305. super(DateAxis, self).__init__(**kw)
  306. class SeriesAxis(_BaseAxis):
  307. tagname = "serAx"
  308. axId = _BaseAxis.axId
  309. scaling = _BaseAxis.scaling
  310. delete = _BaseAxis.delete
  311. axPos = _BaseAxis.axPos
  312. majorGridlines = _BaseAxis.majorGridlines
  313. minorGridlines = _BaseAxis.minorGridlines
  314. title = _BaseAxis.title
  315. numFmt = _BaseAxis.numFmt
  316. majorTickMark = _BaseAxis.majorTickMark
  317. minorTickMark = _BaseAxis.minorTickMark
  318. tickLblPos = _BaseAxis.tickLblPos
  319. spPr = _BaseAxis.spPr
  320. txPr = _BaseAxis.txPr
  321. crossAx = _BaseAxis.crossAx
  322. crosses = _BaseAxis.crosses
  323. crossesAt = _BaseAxis.crossesAt
  324. tickLblSkip = NestedInteger(allow_none=True)
  325. tickMarkSkip = NestedInteger(allow_none=True)
  326. extLst = Typed(expected_type=ExtensionList, allow_none=True)
  327. __elements__ = _BaseAxis.__elements__ + ('tickLblSkip', 'tickMarkSkip')
  328. def __init__(self,
  329. tickLblSkip=None,
  330. tickMarkSkip=None,
  331. extLst=None,
  332. **kw
  333. ):
  334. self.tickLblSkip = tickLblSkip
  335. self.tickMarkSkip = tickMarkSkip
  336. kw.setdefault('axId', 1000)
  337. kw.setdefault('crossAx', 10)
  338. super(SeriesAxis, self).__init__(**kw)