mirror of
https://github.com/aykhans/AzSuicideDataVisualization.git
synced 2025-07-02 14:27:31 +00:00
Version 0.1
Added sidebar, Dashboard, Line Graph
This commit is contained in:
11
.venv/Lib/site-packages/openpyxl/styles/__init__.py
Normal file
11
.venv/Lib/site-packages/openpyxl/styles/__init__.py
Normal file
@ -0,0 +1,11 @@
|
||||
# Copyright (c) 2010-2022 openpyxl
|
||||
|
||||
|
||||
from .alignment import Alignment
|
||||
from .borders import Border, Side
|
||||
from .colors import Color
|
||||
from .fills import PatternFill, GradientFill, Fill
|
||||
from .fonts import Font, DEFAULT_FONT
|
||||
from .numbers import NumberFormatDescriptor, is_date_format, is_builtin
|
||||
from .protection import Protection
|
||||
from .named_styles import NamedStyle
|
72
.venv/Lib/site-packages/openpyxl/styles/alignment.py
Normal file
72
.venv/Lib/site-packages/openpyxl/styles/alignment.py
Normal file
@ -0,0 +1,72 @@
|
||||
# Copyright (c) 2010-2022 openpyxl
|
||||
|
||||
from openpyxl.compat import safe_string
|
||||
|
||||
from openpyxl.descriptors import Bool, MinMax, Min, Alias, NoneSet
|
||||
from openpyxl.descriptors.serialisable import Serialisable
|
||||
|
||||
|
||||
horizontal_alignments = (
|
||||
"general", "left", "center", "right", "fill", "justify", "centerContinuous",
|
||||
"distributed", )
|
||||
vertical_aligments = (
|
||||
"top", "center", "bottom", "justify", "distributed",
|
||||
)
|
||||
|
||||
class Alignment(Serialisable):
|
||||
"""Alignment options for use in styles."""
|
||||
|
||||
tagname = "alignment"
|
||||
|
||||
__fields__ = ('horizontal',
|
||||
'vertical',
|
||||
'textRotation',
|
||||
'wrapText',
|
||||
'shrinkToFit',
|
||||
'indent',
|
||||
'relativeIndent',
|
||||
'justifyLastLine',
|
||||
'readingOrder',
|
||||
)
|
||||
horizontal = NoneSet(values=horizontal_alignments)
|
||||
vertical = NoneSet(values=vertical_aligments)
|
||||
textRotation = NoneSet(values=range(181))
|
||||
textRotation.values.add(255)
|
||||
text_rotation = Alias('textRotation')
|
||||
wrapText = Bool(allow_none=True)
|
||||
wrap_text = Alias('wrapText')
|
||||
shrinkToFit = Bool(allow_none=True)
|
||||
shrink_to_fit = Alias('shrinkToFit')
|
||||
indent = MinMax(min=0, max=255)
|
||||
relativeIndent = MinMax(min=-255, max=255)
|
||||
justifyLastLine = Bool(allow_none=True)
|
||||
readingOrder = Min(min=0)
|
||||
|
||||
def __init__(self, horizontal=None, vertical=None,
|
||||
textRotation=0, wrapText=None, shrinkToFit=None, indent=0, relativeIndent=0,
|
||||
justifyLastLine=None, readingOrder=0, text_rotation=None,
|
||||
wrap_text=None, shrink_to_fit=None, mergeCell=None):
|
||||
self.horizontal = horizontal
|
||||
self.vertical = vertical
|
||||
self.indent = indent
|
||||
self.relativeIndent = relativeIndent
|
||||
self.justifyLastLine = justifyLastLine
|
||||
self.readingOrder = readingOrder
|
||||
if text_rotation is not None:
|
||||
textRotation = text_rotation
|
||||
if textRotation is not None:
|
||||
self.textRotation = int(textRotation)
|
||||
if wrap_text is not None:
|
||||
wrapText = wrap_text
|
||||
self.wrapText = wrapText
|
||||
if shrink_to_fit is not None:
|
||||
shrinkToFit = shrink_to_fit
|
||||
self.shrinkToFit = shrinkToFit
|
||||
# mergeCell is vestigial
|
||||
|
||||
|
||||
def __iter__(self):
|
||||
for attr in self.__attrs__:
|
||||
value = getattr(self, attr)
|
||||
if value is not None and value != 0:
|
||||
yield attr, safe_string(value)
|
113
.venv/Lib/site-packages/openpyxl/styles/borders.py
Normal file
113
.venv/Lib/site-packages/openpyxl/styles/borders.py
Normal file
@ -0,0 +1,113 @@
|
||||
# Copyright (c) 2010-2022 openpyxl
|
||||
|
||||
from openpyxl.compat import safe_string
|
||||
from openpyxl.descriptors import (
|
||||
NoneSet,
|
||||
Typed,
|
||||
Bool,
|
||||
Alias,
|
||||
Sequence,
|
||||
Integer,
|
||||
)
|
||||
from openpyxl.descriptors.serialisable import Serialisable
|
||||
|
||||
from .colors import ColorDescriptor
|
||||
|
||||
|
||||
BORDER_NONE = None
|
||||
BORDER_DASHDOT = 'dashDot'
|
||||
BORDER_DASHDOTDOT = 'dashDotDot'
|
||||
BORDER_DASHED = 'dashed'
|
||||
BORDER_DOTTED = 'dotted'
|
||||
BORDER_DOUBLE = 'double'
|
||||
BORDER_HAIR = 'hair'
|
||||
BORDER_MEDIUM = 'medium'
|
||||
BORDER_MEDIUMDASHDOT = 'mediumDashDot'
|
||||
BORDER_MEDIUMDASHDOTDOT = 'mediumDashDotDot'
|
||||
BORDER_MEDIUMDASHED = 'mediumDashed'
|
||||
BORDER_SLANTDASHDOT = 'slantDashDot'
|
||||
BORDER_THICK = 'thick'
|
||||
BORDER_THIN = 'thin'
|
||||
|
||||
|
||||
class Side(Serialisable):
|
||||
|
||||
"""Border options for use in styles.
|
||||
Caution: if you do not specify a border_style, other attributes will
|
||||
have no effect !"""
|
||||
|
||||
__fields__ = ('style',
|
||||
'color')
|
||||
|
||||
color = ColorDescriptor(allow_none=True)
|
||||
style = NoneSet(values=('dashDot','dashDotDot', 'dashed','dotted',
|
||||
'double','hair', 'medium', 'mediumDashDot', 'mediumDashDotDot',
|
||||
'mediumDashed', 'slantDashDot', 'thick', 'thin')
|
||||
)
|
||||
border_style = Alias('style')
|
||||
|
||||
def __init__(self, style=None, color=None, border_style=None):
|
||||
if border_style is not None:
|
||||
style = border_style
|
||||
self.style = style
|
||||
self.color = color
|
||||
|
||||
|
||||
class Border(Serialisable):
|
||||
"""Border positioning for use in styles."""
|
||||
|
||||
tagname = "border"
|
||||
|
||||
__fields__ = ('left',
|
||||
'right',
|
||||
'top',
|
||||
'bottom',
|
||||
'diagonal',
|
||||
'diagonal_direction',
|
||||
'vertical',
|
||||
'horizontal')
|
||||
__elements__ = ('start', 'end', 'left', 'right', 'top', 'bottom',
|
||||
'diagonal', 'vertical', 'horizontal')
|
||||
|
||||
# child elements
|
||||
start = Typed(expected_type=Side, allow_none=True)
|
||||
end = Typed(expected_type=Side, allow_none=True)
|
||||
left = Typed(expected_type=Side, allow_none=True)
|
||||
right = Typed(expected_type=Side, allow_none=True)
|
||||
top = Typed(expected_type=Side, allow_none=True)
|
||||
bottom = Typed(expected_type=Side, allow_none=True)
|
||||
diagonal = Typed(expected_type=Side, allow_none=True)
|
||||
vertical = Typed(expected_type=Side, allow_none=True)
|
||||
horizontal = Typed(expected_type=Side, allow_none=True)
|
||||
# attributes
|
||||
outline = Bool()
|
||||
diagonalUp = Bool()
|
||||
diagonalDown = Bool()
|
||||
|
||||
def __init__(self, left=None, right=None, top=None,
|
||||
bottom=None, diagonal=None, diagonal_direction=None,
|
||||
vertical=None, horizontal=None, diagonalUp=False, diagonalDown=False,
|
||||
outline=True, start=None, end=None):
|
||||
self.left = left
|
||||
self.right = right
|
||||
self.top = top
|
||||
self.bottom = bottom
|
||||
self.diagonal = diagonal
|
||||
self.vertical = vertical
|
||||
self.horizontal = horizontal
|
||||
self.diagonal_direction = diagonal_direction
|
||||
self.diagonalUp = diagonalUp
|
||||
self.diagonalDown = diagonalDown
|
||||
self.outline = outline
|
||||
self.start = start
|
||||
self.end = end
|
||||
|
||||
def __iter__(self):
|
||||
for attr in self.__attrs__:
|
||||
value = getattr(self, attr)
|
||||
if value and attr != "outline":
|
||||
yield attr, safe_string(value)
|
||||
elif attr == "outline" and not value:
|
||||
yield attr, safe_string(value)
|
||||
|
||||
DEFAULT_BORDER = Border(left=Side(), right=Side(), top=Side(), bottom=Side(), diagonal=Side())
|
1397
.venv/Lib/site-packages/openpyxl/styles/builtins.py
Normal file
1397
.venv/Lib/site-packages/openpyxl/styles/builtins.py
Normal file
File diff suppressed because it is too large
Load Diff
202
.venv/Lib/site-packages/openpyxl/styles/cell_style.py
Normal file
202
.venv/Lib/site-packages/openpyxl/styles/cell_style.py
Normal file
@ -0,0 +1,202 @@
|
||||
# Copyright (c) 2010-2022 openpyxl
|
||||
|
||||
from array import array
|
||||
|
||||
from openpyxl.descriptors.serialisable import Serialisable
|
||||
from openpyxl.descriptors import (
|
||||
Typed,
|
||||
Float,
|
||||
Bool,
|
||||
Integer,
|
||||
Sequence,
|
||||
)
|
||||
from openpyxl.descriptors.excel import ExtensionList
|
||||
from openpyxl.utils.indexed_list import IndexedList
|
||||
|
||||
|
||||
from .alignment import Alignment
|
||||
from .protection import Protection
|
||||
|
||||
|
||||
class ArrayDescriptor(object):
|
||||
|
||||
def __init__(self, key):
|
||||
self.key = key
|
||||
|
||||
def __get__(self, instance, cls):
|
||||
return instance[self.key]
|
||||
|
||||
def __set__(self, instance, value):
|
||||
instance[self.key] = value
|
||||
|
||||
|
||||
class StyleArray(array):
|
||||
"""
|
||||
Simplified named tuple with an array
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
tagname = 'xf'
|
||||
|
||||
fontId = ArrayDescriptor(0)
|
||||
fillId = ArrayDescriptor(1)
|
||||
borderId = ArrayDescriptor(2)
|
||||
numFmtId = ArrayDescriptor(3)
|
||||
protectionId = ArrayDescriptor(4)
|
||||
alignmentId = ArrayDescriptor(5)
|
||||
pivotButton = ArrayDescriptor(6)
|
||||
quotePrefix = ArrayDescriptor(7)
|
||||
xfId = ArrayDescriptor(8)
|
||||
|
||||
|
||||
def __new__(cls, args=[0]*9):
|
||||
return array.__new__(cls, 'i', args)
|
||||
|
||||
|
||||
def __hash__(self):
|
||||
return hash(tuple(self))
|
||||
|
||||
|
||||
def __copy__(self):
|
||||
return StyleArray((self))
|
||||
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
return StyleArray((self))
|
||||
|
||||
|
||||
class CellStyle(Serialisable):
|
||||
|
||||
tagname = "xf"
|
||||
|
||||
numFmtId = Integer()
|
||||
fontId = Integer()
|
||||
fillId = Integer()
|
||||
borderId = Integer()
|
||||
xfId = Integer(allow_none=True)
|
||||
quotePrefix = Bool(allow_none=True)
|
||||
pivotButton = Bool(allow_none=True)
|
||||
applyNumberFormat = Bool(allow_none=True)
|
||||
applyFont = Bool(allow_none=True)
|
||||
applyFill = Bool(allow_none=True)
|
||||
applyBorder = Bool(allow_none=True)
|
||||
applyAlignment = Bool(allow_none=True)
|
||||
applyProtection = Bool(allow_none=True)
|
||||
alignment = Typed(expected_type=Alignment, allow_none=True)
|
||||
protection = Typed(expected_type=Protection, allow_none=True)
|
||||
extLst = Typed(expected_type=ExtensionList, allow_none=True)
|
||||
|
||||
__elements__ = ('alignment', 'protection')
|
||||
__attrs__ = ("numFmtId", "fontId", "fillId", "borderId",
|
||||
"applyAlignment", "applyProtection", "pivotButton", "quotePrefix", "xfId")
|
||||
|
||||
def __init__(self,
|
||||
numFmtId=0,
|
||||
fontId=0,
|
||||
fillId=0,
|
||||
borderId=0,
|
||||
xfId=None,
|
||||
quotePrefix=None,
|
||||
pivotButton=None,
|
||||
applyNumberFormat=None,
|
||||
applyFont=None,
|
||||
applyFill=None,
|
||||
applyBorder=None,
|
||||
applyAlignment=None,
|
||||
applyProtection=None,
|
||||
alignment=None,
|
||||
protection=None,
|
||||
extLst=None,
|
||||
):
|
||||
self.numFmtId = numFmtId
|
||||
self.fontId = fontId
|
||||
self.fillId = fillId
|
||||
self.borderId = borderId
|
||||
self.xfId = xfId
|
||||
self.quotePrefix = quotePrefix
|
||||
self.pivotButton = pivotButton
|
||||
self.applyNumberFormat = applyNumberFormat
|
||||
self.applyFont = applyFont
|
||||
self.applyFill = applyFill
|
||||
self.applyBorder = applyBorder
|
||||
self.alignment = alignment
|
||||
self.protection = protection
|
||||
|
||||
|
||||
def to_array(self):
|
||||
"""
|
||||
Convert to StyleArray
|
||||
"""
|
||||
style = StyleArray()
|
||||
for k in ("fontId", "fillId", "borderId", "numFmtId", "pivotButton",
|
||||
"quotePrefix", "xfId"):
|
||||
v = getattr(self, k, 0)
|
||||
if v is not None:
|
||||
setattr(style, k, v)
|
||||
return style
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_array(cls, style):
|
||||
"""
|
||||
Convert from StyleArray
|
||||
"""
|
||||
return cls(numFmtId=style.numFmtId, fontId=style.fontId,
|
||||
fillId=style.fillId, borderId=style.borderId, xfId=style.xfId,
|
||||
quotePrefix=style.quotePrefix, pivotButton=style.pivotButton,)
|
||||
|
||||
|
||||
@property
|
||||
def applyProtection(self):
|
||||
return self.protection is not None or None
|
||||
|
||||
|
||||
@property
|
||||
def applyAlignment(self):
|
||||
return self.alignment is not None or None
|
||||
|
||||
|
||||
class CellStyleList(Serialisable):
|
||||
|
||||
tagname = "cellXfs"
|
||||
|
||||
__attrs__ = ("count",)
|
||||
|
||||
count = Integer(allow_none=True)
|
||||
xf = Sequence(expected_type=CellStyle)
|
||||
alignment = Sequence(expected_type=Alignment)
|
||||
protection = Sequence(expected_type=Protection)
|
||||
|
||||
__elements__ = ('xf',)
|
||||
|
||||
def __init__(self,
|
||||
count=None,
|
||||
xf=(),
|
||||
):
|
||||
self.xf = xf
|
||||
|
||||
|
||||
@property
|
||||
def count(self):
|
||||
return len(self.xf)
|
||||
|
||||
|
||||
def __getitem__(self, idx):
|
||||
return self.xf[idx]
|
||||
|
||||
|
||||
def _to_array(self):
|
||||
"""
|
||||
Extract protection and alignments, convert to style array
|
||||
"""
|
||||
self.prots = IndexedList([Protection()])
|
||||
self.alignments = IndexedList([Alignment()])
|
||||
styles = [] # allow duplicates
|
||||
for xf in self.xf:
|
||||
style = xf.to_array()
|
||||
if xf.alignment is not None:
|
||||
style.alignmentId = self.alignments.add(xf.alignment)
|
||||
if xf.protection is not None:
|
||||
style.protectionId = self.prots.add(xf.protection)
|
||||
styles.append(style)
|
||||
return IndexedList(styles)
|
172
.venv/Lib/site-packages/openpyxl/styles/colors.py
Normal file
172
.venv/Lib/site-packages/openpyxl/styles/colors.py
Normal file
@ -0,0 +1,172 @@
|
||||
# Copyright (c) 2010-2022 openpyxl
|
||||
|
||||
import re
|
||||
from openpyxl.compat import safe_string
|
||||
from openpyxl.descriptors import (
|
||||
String,
|
||||
Bool,
|
||||
MinMax,
|
||||
Integer,
|
||||
Typed,
|
||||
)
|
||||
from openpyxl.descriptors.sequence import NestedSequence
|
||||
from openpyxl.descriptors.serialisable import Serialisable
|
||||
|
||||
# Default Color Index as per 18.8.27 of ECMA Part 4
|
||||
COLOR_INDEX = (
|
||||
'00000000', '00FFFFFF', '00FF0000', '0000FF00', '000000FF', #0-4
|
||||
'00FFFF00', '00FF00FF', '0000FFFF', '00000000', '00FFFFFF', #5-9
|
||||
'00FF0000', '0000FF00', '000000FF', '00FFFF00', '00FF00FF', #10-14
|
||||
'0000FFFF', '00800000', '00008000', '00000080', '00808000', #15-19
|
||||
'00800080', '00008080', '00C0C0C0', '00808080', '009999FF', #20-24
|
||||
'00993366', '00FFFFCC', '00CCFFFF', '00660066', '00FF8080', #25-29
|
||||
'000066CC', '00CCCCFF', '00000080', '00FF00FF', '00FFFF00', #30-34
|
||||
'0000FFFF', '00800080', '00800000', '00008080', '000000FF', #35-39
|
||||
'0000CCFF', '00CCFFFF', '00CCFFCC', '00FFFF99', '0099CCFF', #40-44
|
||||
'00FF99CC', '00CC99FF', '00FFCC99', '003366FF', '0033CCCC', #45-49
|
||||
'0099CC00', '00FFCC00', '00FF9900', '00FF6600', '00666699', #50-54
|
||||
'00969696', '00003366', '00339966', '00003300', '00333300', #55-59
|
||||
'00993300', '00993366', '00333399', '00333333', #60-63
|
||||
)
|
||||
# indices 64 and 65 are reserved for the system foreground and background colours respectively
|
||||
|
||||
# Will remove these definitions in a future release
|
||||
BLACK = COLOR_INDEX[0]
|
||||
WHITE = COLOR_INDEX[1]
|
||||
#RED = COLOR_INDEX[2]
|
||||
#DARKRED = COLOR_INDEX[8]
|
||||
BLUE = COLOR_INDEX[4]
|
||||
#DARKBLUE = COLOR_INDEX[12]
|
||||
#GREEN = COLOR_INDEX[3]
|
||||
#DARKGREEN = COLOR_INDEX[9]
|
||||
#YELLOW = COLOR_INDEX[5]
|
||||
#DARKYELLOW = COLOR_INDEX[19]
|
||||
|
||||
|
||||
aRGB_REGEX = re.compile("^([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6})$")
|
||||
|
||||
|
||||
class RGB(Typed):
|
||||
"""
|
||||
Descriptor for aRGB values
|
||||
If not supplied alpha is 00
|
||||
"""
|
||||
|
||||
expected_type = str
|
||||
|
||||
def __set__(self, instance, value):
|
||||
if not self.allow_none:
|
||||
m = aRGB_REGEX.match(value)
|
||||
if m is None:
|
||||
raise ValueError("Colors must be aRGB hex values")
|
||||
if len(value) == 6:
|
||||
value = "00" + value
|
||||
super(RGB, self).__set__(instance, value)
|
||||
|
||||
|
||||
class Color(Serialisable):
|
||||
"""Named colors for use in styles."""
|
||||
|
||||
tagname = "color"
|
||||
|
||||
rgb = RGB()
|
||||
indexed = Integer()
|
||||
auto = Bool()
|
||||
theme = Integer()
|
||||
tint = MinMax(min=-1, max=1, expected_type=float)
|
||||
type = String()
|
||||
|
||||
|
||||
def __init__(self, rgb=BLACK, indexed=None, auto=None, theme=None, tint=0.0, index=None, type='rgb'):
|
||||
if index is not None:
|
||||
indexed = index
|
||||
if indexed is not None:
|
||||
self.type = 'indexed'
|
||||
self.indexed = indexed
|
||||
elif theme is not None:
|
||||
self.type = 'theme'
|
||||
self.theme = theme
|
||||
elif auto is not None:
|
||||
self.type = 'auto'
|
||||
self.auto = auto
|
||||
else:
|
||||
self.rgb = rgb
|
||||
self.type = 'rgb'
|
||||
self.tint = tint
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return getattr(self, self.type)
|
||||
|
||||
@value.setter
|
||||
def value(self, value):
|
||||
setattr(self, self.type, value)
|
||||
|
||||
def __iter__(self):
|
||||
attrs = [(self.type, self.value)]
|
||||
if self.tint != 0:
|
||||
attrs.append(('tint', self.tint))
|
||||
for k, v in attrs:
|
||||
yield k, safe_string(v)
|
||||
|
||||
@property
|
||||
def index(self):
|
||||
# legacy
|
||||
return self.value
|
||||
|
||||
|
||||
def __add__(self, other):
|
||||
"""
|
||||
Adding colours is undefined behaviour best do nothing
|
||||
"""
|
||||
if not isinstance(other, Color):
|
||||
return super(Color, self).__add__(other)
|
||||
return self
|
||||
|
||||
|
||||
class ColorDescriptor(Typed):
|
||||
|
||||
expected_type = Color
|
||||
|
||||
def __set__(self, instance, value):
|
||||
if isinstance(value, str):
|
||||
value = Color(rgb=value)
|
||||
super(ColorDescriptor, self).__set__(instance, value)
|
||||
|
||||
|
||||
class RgbColor(Serialisable):
|
||||
|
||||
tagname = "rgbColor"
|
||||
|
||||
rgb = RGB()
|
||||
|
||||
def __init__(self,
|
||||
rgb=None,
|
||||
):
|
||||
self.rgb = rgb
|
||||
|
||||
|
||||
class ColorList(Serialisable):
|
||||
|
||||
tagname = "colors"
|
||||
|
||||
indexedColors = NestedSequence(expected_type=RgbColor)
|
||||
mruColors = NestedSequence(expected_type=Color)
|
||||
|
||||
__elements__ = ('indexedColors', 'mruColors')
|
||||
|
||||
def __init__(self,
|
||||
indexedColors=(),
|
||||
mruColors=(),
|
||||
):
|
||||
self.indexedColors = indexedColors
|
||||
self.mruColors = mruColors
|
||||
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self.indexedColors) or bool(self.mruColors)
|
||||
|
||||
|
||||
@property
|
||||
def index(self):
|
||||
return [val.rgb for val in self.indexedColors]
|
95
.venv/Lib/site-packages/openpyxl/styles/differential.py
Normal file
95
.venv/Lib/site-packages/openpyxl/styles/differential.py
Normal file
@ -0,0 +1,95 @@
|
||||
# Copyright (c) 2010-2022 openpyxl
|
||||
|
||||
from openpyxl.descriptors import (
|
||||
Typed,
|
||||
Sequence,
|
||||
Alias,
|
||||
)
|
||||
from openpyxl.descriptors.serialisable import Serialisable
|
||||
from openpyxl.styles import (
|
||||
Font,
|
||||
Fill,
|
||||
Border,
|
||||
Alignment,
|
||||
Protection,
|
||||
)
|
||||
from .numbers import NumberFormat
|
||||
|
||||
|
||||
class DifferentialStyle(Serialisable):
|
||||
|
||||
tagname = "dxf"
|
||||
|
||||
__elements__ = ("font", "numFmt", "fill", "alignment", "border", "protection")
|
||||
|
||||
font = Typed(expected_type=Font, allow_none=True)
|
||||
numFmt = Typed(expected_type=NumberFormat, allow_none=True)
|
||||
fill = Typed(expected_type=Fill, allow_none=True)
|
||||
alignment = Typed(expected_type=Alignment, allow_none=True)
|
||||
border = Typed(expected_type=Border, allow_none=True)
|
||||
protection = Typed(expected_type=Protection, allow_none=True)
|
||||
|
||||
def __init__(self,
|
||||
font=None,
|
||||
numFmt=None,
|
||||
fill=None,
|
||||
alignment=None,
|
||||
border=None,
|
||||
protection=None,
|
||||
extLst=None,
|
||||
):
|
||||
self.font = font
|
||||
self.numFmt = numFmt
|
||||
self.fill = fill
|
||||
self.alignment = alignment
|
||||
self.border = border
|
||||
self.protection = protection
|
||||
self.extLst = extLst
|
||||
|
||||
|
||||
class DifferentialStyleList(Serialisable):
|
||||
"""
|
||||
Dedupable container for differential styles.
|
||||
"""
|
||||
|
||||
tagname = "dxfs"
|
||||
|
||||
dxf = Sequence(expected_type=DifferentialStyle)
|
||||
styles = Alias("dxf")
|
||||
__attrs__ = ("count",)
|
||||
|
||||
|
||||
def __init__(self, dxf=(), count=None):
|
||||
self.dxf = dxf
|
||||
|
||||
|
||||
def append(self, dxf):
|
||||
"""
|
||||
Check to see whether style already exists and append it if does not.
|
||||
"""
|
||||
if not isinstance(dxf, DifferentialStyle):
|
||||
raise TypeError('expected ' + str(DifferentialStyle))
|
||||
if dxf in self.styles:
|
||||
return
|
||||
self.styles.append(dxf)
|
||||
|
||||
|
||||
def add(self, dxf):
|
||||
"""
|
||||
Add a differential style and return its index
|
||||
"""
|
||||
self.append(dxf)
|
||||
return self.styles.index(dxf)
|
||||
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self.styles)
|
||||
|
||||
|
||||
def __getitem__(self, idx):
|
||||
return self.styles[idx]
|
||||
|
||||
|
||||
@property
|
||||
def count(self):
|
||||
return len(self.dxf)
|
224
.venv/Lib/site-packages/openpyxl/styles/fills.py
Normal file
224
.venv/Lib/site-packages/openpyxl/styles/fills.py
Normal file
@ -0,0 +1,224 @@
|
||||
from __future__ import division
|
||||
# Copyright (c) 2010-2022 openpyxl
|
||||
|
||||
from openpyxl.descriptors import (
|
||||
Float,
|
||||
Set,
|
||||
Alias,
|
||||
NoneSet,
|
||||
Sequence,
|
||||
Integer,
|
||||
MinMax,
|
||||
)
|
||||
from openpyxl.descriptors.serialisable import Serialisable
|
||||
from openpyxl.compat import safe_string
|
||||
|
||||
from .colors import ColorDescriptor, Color
|
||||
|
||||
from openpyxl.xml.functions import Element, localname
|
||||
from openpyxl.xml.constants import SHEET_MAIN_NS
|
||||
|
||||
|
||||
FILL_NONE = 'none'
|
||||
FILL_SOLID = 'solid'
|
||||
FILL_PATTERN_DARKDOWN = 'darkDown'
|
||||
FILL_PATTERN_DARKGRAY = 'darkGray'
|
||||
FILL_PATTERN_DARKGRID = 'darkGrid'
|
||||
FILL_PATTERN_DARKHORIZONTAL = 'darkHorizontal'
|
||||
FILL_PATTERN_DARKTRELLIS = 'darkTrellis'
|
||||
FILL_PATTERN_DARKUP = 'darkUp'
|
||||
FILL_PATTERN_DARKVERTICAL = 'darkVertical'
|
||||
FILL_PATTERN_GRAY0625 = 'gray0625'
|
||||
FILL_PATTERN_GRAY125 = 'gray125'
|
||||
FILL_PATTERN_LIGHTDOWN = 'lightDown'
|
||||
FILL_PATTERN_LIGHTGRAY = 'lightGray'
|
||||
FILL_PATTERN_LIGHTGRID = 'lightGrid'
|
||||
FILL_PATTERN_LIGHTHORIZONTAL = 'lightHorizontal'
|
||||
FILL_PATTERN_LIGHTTRELLIS = 'lightTrellis'
|
||||
FILL_PATTERN_LIGHTUP = 'lightUp'
|
||||
FILL_PATTERN_LIGHTVERTICAL = 'lightVertical'
|
||||
FILL_PATTERN_MEDIUMGRAY = 'mediumGray'
|
||||
|
||||
fills = (FILL_SOLID, FILL_PATTERN_DARKDOWN, FILL_PATTERN_DARKGRAY,
|
||||
FILL_PATTERN_DARKGRID, FILL_PATTERN_DARKHORIZONTAL, FILL_PATTERN_DARKTRELLIS,
|
||||
FILL_PATTERN_DARKUP, FILL_PATTERN_DARKVERTICAL, FILL_PATTERN_GRAY0625,
|
||||
FILL_PATTERN_GRAY125, FILL_PATTERN_LIGHTDOWN, FILL_PATTERN_LIGHTGRAY,
|
||||
FILL_PATTERN_LIGHTGRID, FILL_PATTERN_LIGHTHORIZONTAL,
|
||||
FILL_PATTERN_LIGHTTRELLIS, FILL_PATTERN_LIGHTUP, FILL_PATTERN_LIGHTVERTICAL,
|
||||
FILL_PATTERN_MEDIUMGRAY)
|
||||
|
||||
|
||||
class Fill(Serialisable):
|
||||
|
||||
"""Base class"""
|
||||
|
||||
tagname = "fill"
|
||||
|
||||
@classmethod
|
||||
def from_tree(cls, el):
|
||||
children = [c for c in el]
|
||||
if not children:
|
||||
return
|
||||
child = children[0]
|
||||
if "patternFill" in child.tag:
|
||||
return PatternFill._from_tree(child)
|
||||
return super(Fill, GradientFill).from_tree(child)
|
||||
|
||||
|
||||
class PatternFill(Fill):
|
||||
"""Area fill patterns for use in styles.
|
||||
Caution: if you do not specify a fill_type, other attributes will have
|
||||
no effect !"""
|
||||
|
||||
tagname = "patternFill"
|
||||
|
||||
__elements__ = ('fgColor', 'bgColor')
|
||||
|
||||
patternType = NoneSet(values=fills)
|
||||
fill_type = Alias("patternType")
|
||||
fgColor = ColorDescriptor()
|
||||
start_color = Alias("fgColor")
|
||||
bgColor = ColorDescriptor()
|
||||
end_color = Alias("bgColor")
|
||||
|
||||
def __init__(self, patternType=None, fgColor=Color(), bgColor=Color(),
|
||||
fill_type=None, start_color=None, end_color=None):
|
||||
if fill_type is not None:
|
||||
patternType = fill_type
|
||||
self.patternType = patternType
|
||||
if start_color is not None:
|
||||
fgColor = start_color
|
||||
self.fgColor = fgColor
|
||||
if end_color is not None:
|
||||
bgColor = end_color
|
||||
self.bgColor = bgColor
|
||||
|
||||
@classmethod
|
||||
def _from_tree(cls, el):
|
||||
attrib = dict(el.attrib)
|
||||
for child in el:
|
||||
desc = localname(child)
|
||||
attrib[desc] = Color.from_tree(child)
|
||||
return cls(**attrib)
|
||||
|
||||
|
||||
def to_tree(self, tagname=None, idx=None):
|
||||
parent = Element("fill")
|
||||
el = Element(self.tagname)
|
||||
if self.patternType is not None:
|
||||
el.set('patternType', self.patternType)
|
||||
for c in self.__elements__:
|
||||
value = getattr(self, c)
|
||||
if value != Color():
|
||||
el.append(value.to_tree(c))
|
||||
parent.append(el)
|
||||
return parent
|
||||
|
||||
|
||||
DEFAULT_EMPTY_FILL = PatternFill()
|
||||
DEFAULT_GRAY_FILL = PatternFill(patternType='gray125')
|
||||
|
||||
|
||||
class Stop(Serialisable):
|
||||
|
||||
tagname = "stop"
|
||||
|
||||
position = MinMax(min=0, max=1)
|
||||
color = ColorDescriptor()
|
||||
|
||||
def __init__(self, color, position):
|
||||
self.position = position
|
||||
self.color = color
|
||||
|
||||
|
||||
def _assign_position(values):
|
||||
"""
|
||||
Automatically assign positions if a list of colours is provided.
|
||||
|
||||
It is not permitted to mix colours and stops
|
||||
"""
|
||||
n_values = len(values)
|
||||
n_stops = sum(isinstance(value, Stop) for value in values)
|
||||
|
||||
if n_stops == 0:
|
||||
interval = 1
|
||||
if n_values > 2:
|
||||
interval = 1 / (n_values - 1)
|
||||
values = [Stop(value, i * interval)
|
||||
for i, value in enumerate(values)]
|
||||
|
||||
elif n_stops < n_values:
|
||||
raise ValueError('Cannot interpret mix of Stops and Colors in GradientFill')
|
||||
|
||||
pos = set()
|
||||
for stop in values:
|
||||
if stop.position in pos:
|
||||
raise ValueError("Duplicate position {0}".format(stop.position))
|
||||
pos.add(stop.position)
|
||||
|
||||
return values
|
||||
|
||||
|
||||
class StopList(Sequence):
|
||||
|
||||
expected_type = Stop
|
||||
|
||||
def __set__(self, obj, values):
|
||||
values = _assign_position(values)
|
||||
super(StopList, self).__set__(obj, values)
|
||||
|
||||
|
||||
class GradientFill(Fill):
|
||||
"""Fill areas with gradient
|
||||
|
||||
Two types of gradient fill are supported:
|
||||
|
||||
- A type='linear' gradient interpolates colours between
|
||||
a set of specified Stops, across the length of an area.
|
||||
The gradient is left-to-right by default, but this
|
||||
orientation can be modified with the degree
|
||||
attribute. A list of Colors can be provided instead
|
||||
and they will be positioned with equal distance between them.
|
||||
|
||||
- A type='path' gradient applies a linear gradient from each
|
||||
edge of the area. Attributes top, right, bottom, left specify
|
||||
the extent of fill from the respective borders. Thus top="0.2"
|
||||
will fill the top 20% of the cell.
|
||||
|
||||
"""
|
||||
|
||||
tagname = "gradientFill"
|
||||
|
||||
type = Set(values=('linear', 'path'))
|
||||
fill_type = Alias("type")
|
||||
degree = Float()
|
||||
left = Float()
|
||||
right = Float()
|
||||
top = Float()
|
||||
bottom = Float()
|
||||
stop = StopList()
|
||||
|
||||
|
||||
def __init__(self, type="linear", degree=0, left=0, right=0, top=0,
|
||||
bottom=0, stop=()):
|
||||
self.degree = degree
|
||||
self.left = left
|
||||
self.right = right
|
||||
self.top = top
|
||||
self.bottom = bottom
|
||||
self.stop = stop
|
||||
self.type = type
|
||||
|
||||
|
||||
def __iter__(self):
|
||||
for attr in self.__attrs__:
|
||||
value = getattr(self, attr)
|
||||
if value:
|
||||
yield attr, safe_string(value)
|
||||
|
||||
|
||||
def to_tree(self, tagname=None, namespace=None, idx=None):
|
||||
parent = Element("fill")
|
||||
el = super(GradientFill, self).to_tree()
|
||||
parent.append(el)
|
||||
return parent
|
113
.venv/Lib/site-packages/openpyxl/styles/fonts.py
Normal file
113
.venv/Lib/site-packages/openpyxl/styles/fonts.py
Normal file
@ -0,0 +1,113 @@
|
||||
# Copyright (c) 2010-2022 openpyxl
|
||||
|
||||
|
||||
from openpyxl.descriptors import (
|
||||
Alias,
|
||||
Sequence,
|
||||
Integer
|
||||
)
|
||||
from openpyxl.descriptors.serialisable import Serialisable
|
||||
|
||||
from openpyxl.descriptors.nested import (
|
||||
NestedValue,
|
||||
NestedBool,
|
||||
NestedNoneSet,
|
||||
NestedMinMax,
|
||||
NestedString,
|
||||
NestedInteger,
|
||||
NestedFloat,
|
||||
)
|
||||
from .colors import ColorDescriptor, Color, BLACK
|
||||
|
||||
from openpyxl.compat import safe_string
|
||||
from openpyxl.xml.functions import Element, SubElement
|
||||
from openpyxl.xml.constants import SHEET_MAIN_NS
|
||||
|
||||
|
||||
def _no_value(tagname, value, namespace=None):
|
||||
if value:
|
||||
return Element(tagname, val=safe_string(value))
|
||||
|
||||
|
||||
class Font(Serialisable):
|
||||
"""Font options used in styles."""
|
||||
|
||||
UNDERLINE_DOUBLE = 'double'
|
||||
UNDERLINE_DOUBLE_ACCOUNTING = 'doubleAccounting'
|
||||
UNDERLINE_SINGLE = 'single'
|
||||
UNDERLINE_SINGLE_ACCOUNTING = 'singleAccounting'
|
||||
|
||||
name = NestedString(allow_none=True)
|
||||
charset = NestedInteger(allow_none=True)
|
||||
family = NestedMinMax(min=0, max=14, allow_none=True)
|
||||
sz = NestedFloat(allow_none=True)
|
||||
size = Alias("sz")
|
||||
b = NestedBool(to_tree=_no_value)
|
||||
bold = Alias("b")
|
||||
i = NestedBool(to_tree=_no_value)
|
||||
italic = Alias("i")
|
||||
strike = NestedBool(allow_none=True)
|
||||
strikethrough = Alias("strike")
|
||||
outline = NestedBool(allow_none=True)
|
||||
shadow = NestedBool(allow_none=True)
|
||||
condense = NestedBool(allow_none=True)
|
||||
extend = NestedBool(allow_none=True)
|
||||
u = NestedNoneSet(values=('single', 'double', 'singleAccounting',
|
||||
'doubleAccounting'))
|
||||
underline = Alias("u")
|
||||
vertAlign = NestedNoneSet(values=('superscript', 'subscript', 'baseline'))
|
||||
color = ColorDescriptor(allow_none=True)
|
||||
scheme = NestedNoneSet(values=("major", "minor"))
|
||||
|
||||
tagname = "font"
|
||||
|
||||
__elements__ = ('name', 'charset', 'family', 'b', 'i', 'strike', 'outline',
|
||||
'shadow', 'condense', 'color', 'extend', 'sz', 'u', 'vertAlign',
|
||||
'scheme')
|
||||
|
||||
|
||||
def __init__(self, name=None, sz=None, b=None, i=None, charset=None,
|
||||
u=None, strike=None, color=None, scheme=None, family=None, size=None,
|
||||
bold=None, italic=None, strikethrough=None, underline=None,
|
||||
vertAlign=None, outline=None, shadow=None, condense=None,
|
||||
extend=None):
|
||||
self.name = name
|
||||
self.family = family
|
||||
if size is not None:
|
||||
sz = size
|
||||
self.sz = sz
|
||||
if bold is not None:
|
||||
b = bold
|
||||
self.b = b
|
||||
if italic is not None:
|
||||
i = italic
|
||||
self.i = i
|
||||
if underline is not None:
|
||||
u = underline
|
||||
self.u = u
|
||||
if strikethrough is not None:
|
||||
strike = strikethrough
|
||||
self.strike = strike
|
||||
self.color = color
|
||||
self.vertAlign = vertAlign
|
||||
self.charset = charset
|
||||
self.outline = outline
|
||||
self.shadow = shadow
|
||||
self.condense = condense
|
||||
self.extend = extend
|
||||
self.scheme = scheme
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_tree(cls, node):
|
||||
"""
|
||||
Set default value for underline if child element is present
|
||||
"""
|
||||
underline = node.find("{%s}u" % SHEET_MAIN_NS)
|
||||
if underline is not None and underline.get('val') is None:
|
||||
underline.set("val", "single")
|
||||
return super(Font, cls).from_tree(node)
|
||||
|
||||
|
||||
DEFAULT_FONT = Font(name="Calibri", sz=11, family=2, b=False, i=False,
|
||||
color=Color(theme=1), scheme="minor")
|
291
.venv/Lib/site-packages/openpyxl/styles/named_styles.py
Normal file
291
.venv/Lib/site-packages/openpyxl/styles/named_styles.py
Normal file
@ -0,0 +1,291 @@
|
||||
# Copyright (c) 2010-2022 openpyxl
|
||||
|
||||
from openpyxl.compat import safe_string
|
||||
|
||||
from openpyxl.descriptors import (
|
||||
Typed,
|
||||
Integer,
|
||||
Bool,
|
||||
String,
|
||||
Sequence,
|
||||
)
|
||||
from openpyxl.descriptors.excel import ExtensionList
|
||||
from openpyxl.descriptors.serialisable import Serialisable
|
||||
|
||||
from .fills import PatternFill, Fill
|
||||
from .fonts import Font
|
||||
from .borders import Border
|
||||
from .alignment import Alignment
|
||||
from .protection import Protection
|
||||
from .numbers import (
|
||||
NumberFormatDescriptor,
|
||||
BUILTIN_FORMATS_MAX_SIZE,
|
||||
BUILTIN_FORMATS_REVERSE,
|
||||
)
|
||||
from .cell_style import (
|
||||
StyleArray,
|
||||
CellStyle,
|
||||
)
|
||||
|
||||
|
||||
class NamedStyle(Serialisable):
|
||||
|
||||
"""
|
||||
Named and editable styles
|
||||
"""
|
||||
|
||||
font = Typed(expected_type=Font)
|
||||
fill = Typed(expected_type=Fill)
|
||||
border = Typed(expected_type=Border)
|
||||
alignment = Typed(expected_type=Alignment)
|
||||
number_format = NumberFormatDescriptor()
|
||||
protection = Typed(expected_type=Protection)
|
||||
builtinId = Integer(allow_none=True)
|
||||
hidden = Bool(allow_none=True)
|
||||
xfId = Integer(allow_none=True)
|
||||
name = String()
|
||||
_wb = None
|
||||
_style = StyleArray()
|
||||
|
||||
|
||||
def __init__(self,
|
||||
name="Normal",
|
||||
font=Font(),
|
||||
fill=PatternFill(),
|
||||
border=Border(),
|
||||
alignment=Alignment(),
|
||||
number_format=None,
|
||||
protection=Protection(),
|
||||
builtinId=None,
|
||||
hidden=False,
|
||||
xfId=None,
|
||||
):
|
||||
self.name = name
|
||||
self.font = font
|
||||
self.fill = fill
|
||||
self.border = border
|
||||
self.alignment = alignment
|
||||
self.number_format = number_format
|
||||
self.protection = protection
|
||||
self.builtinId = builtinId
|
||||
self.hidden = hidden
|
||||
self._wb = None
|
||||
self._style = StyleArray()
|
||||
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
super(NamedStyle, self).__setattr__(attr, value)
|
||||
if getattr(self, '_wb', None) and attr in (
|
||||
'font', 'fill', 'border', 'alignment', 'number_format', 'protection',
|
||||
):
|
||||
self._recalculate()
|
||||
|
||||
|
||||
def __iter__(self):
|
||||
for key in ('name', 'builtinId', 'hidden', 'xfId'):
|
||||
value = getattr(self, key, None)
|
||||
if value is not None:
|
||||
yield key, safe_string(value)
|
||||
|
||||
|
||||
@property
|
||||
def xfId(self):
|
||||
"""
|
||||
Index of the style in the list of named styles
|
||||
"""
|
||||
return self._style.xfId
|
||||
|
||||
|
||||
def _set_index(self, idx):
|
||||
"""
|
||||
Allow the containing list to set the index
|
||||
"""
|
||||
self._style.xfId = idx
|
||||
|
||||
|
||||
def bind(self, wb):
|
||||
"""
|
||||
Bind a named style to a workbook
|
||||
"""
|
||||
self._wb = wb
|
||||
self._recalculate()
|
||||
|
||||
|
||||
def _recalculate(self):
|
||||
self._style.fontId = self._wb._fonts.add(self.font)
|
||||
self._style.borderId = self._wb._borders.add(self.border)
|
||||
self._style.fillId = self._wb._fills.add(self.fill)
|
||||
self._style.protectionId = self._wb._protections.add(self.protection)
|
||||
self._style.alignmentId = self._wb._alignments.add(self.alignment)
|
||||
fmt = self.number_format
|
||||
if fmt in BUILTIN_FORMATS_REVERSE:
|
||||
fmt = BUILTIN_FORMATS_REVERSE[fmt]
|
||||
else:
|
||||
fmt = self._wb._number_formats.add(self.number_format) + (
|
||||
BUILTIN_FORMATS_MAX_SIZE)
|
||||
self._style.numFmtId = fmt
|
||||
|
||||
|
||||
def as_tuple(self):
|
||||
"""Return a style array representing the current style"""
|
||||
return self._style
|
||||
|
||||
|
||||
def as_xf(self):
|
||||
"""
|
||||
Return equivalent XfStyle
|
||||
"""
|
||||
xf = CellStyle.from_array(self._style)
|
||||
xf.xfId = None
|
||||
xf.pivotButton = None
|
||||
xf.quotePrefix = None
|
||||
if self.alignment != Alignment():
|
||||
xf.alignment = self.alignment
|
||||
if self.protection != Protection():
|
||||
xf.protection = self.protection
|
||||
return xf
|
||||
|
||||
|
||||
def as_name(self):
|
||||
"""
|
||||
Return relevant named style
|
||||
|
||||
"""
|
||||
named = _NamedCellStyle(
|
||||
name=self.name,
|
||||
builtinId=self.builtinId,
|
||||
hidden=self.hidden,
|
||||
xfId=self.xfId
|
||||
)
|
||||
return named
|
||||
|
||||
|
||||
class NamedStyleList(list):
|
||||
"""
|
||||
Named styles are editable and can be applied to multiple objects
|
||||
|
||||
As only the index is stored in referencing objects the order mus
|
||||
be preserved.
|
||||
"""
|
||||
|
||||
@property
|
||||
def names(self):
|
||||
return [s.name for s in self]
|
||||
|
||||
|
||||
def __getitem__(self, key):
|
||||
if isinstance(key, int):
|
||||
return super(NamedStyleList, self).__getitem__(key)
|
||||
|
||||
names = self.names
|
||||
if key not in names:
|
||||
raise KeyError("No named style with the name{0} exists".format(key))
|
||||
|
||||
for idx, name in enumerate(names):
|
||||
if name == key:
|
||||
return self[idx]
|
||||
|
||||
|
||||
def append(self, style):
|
||||
if not isinstance(style, NamedStyle):
|
||||
raise TypeError("""Only NamedStyle instances can be added""")
|
||||
elif style.name in self.names:
|
||||
raise ValueError("""Style {0} exists already""".format(style.name))
|
||||
style._set_index(len(self))
|
||||
super(NamedStyleList, self).append(style)
|
||||
|
||||
|
||||
class _NamedCellStyle(Serialisable):
|
||||
|
||||
"""
|
||||
Pointer-based representation of named styles in XML
|
||||
xfId refers to the corresponding CellStyleXfs
|
||||
|
||||
Not used in client code.
|
||||
"""
|
||||
|
||||
tagname = "cellStyle"
|
||||
|
||||
name = String()
|
||||
xfId = Integer()
|
||||
builtinId = Integer(allow_none=True)
|
||||
iLevel = Integer(allow_none=True)
|
||||
hidden = Bool(allow_none=True)
|
||||
customBuiltin = Bool(allow_none=True)
|
||||
extLst = Typed(expected_type=ExtensionList, allow_none=True)
|
||||
|
||||
__elements__ = ()
|
||||
|
||||
|
||||
def __init__(self,
|
||||
name=None,
|
||||
xfId=None,
|
||||
builtinId=None,
|
||||
iLevel=None,
|
||||
hidden=None,
|
||||
customBuiltin=None,
|
||||
extLst=None,
|
||||
):
|
||||
self.name = name
|
||||
self.xfId = xfId
|
||||
self.builtinId = builtinId
|
||||
self.iLevel = iLevel
|
||||
self.hidden = hidden
|
||||
self.customBuiltin = customBuiltin
|
||||
|
||||
|
||||
class _NamedCellStyleList(Serialisable):
|
||||
"""
|
||||
Container for named cell style objects
|
||||
|
||||
Not used in client code
|
||||
"""
|
||||
|
||||
tagname = "cellStyles"
|
||||
|
||||
count = Integer(allow_none=True)
|
||||
cellStyle = Sequence(expected_type=_NamedCellStyle)
|
||||
|
||||
__attrs__ = ("count",)
|
||||
|
||||
def __init__(self,
|
||||
count=None,
|
||||
cellStyle=(),
|
||||
):
|
||||
self.cellStyle = cellStyle
|
||||
|
||||
|
||||
@property
|
||||
def count(self):
|
||||
return len(self.cellStyle)
|
||||
|
||||
|
||||
@property
|
||||
def names(self):
|
||||
"""
|
||||
Convert to NamedStyle objects and remove duplicates.
|
||||
|
||||
In theory the highest xfId wins but in practice they are duplicates
|
||||
so it doesn't matter.
|
||||
"""
|
||||
|
||||
def sort_fn(v):
|
||||
return v.xfId
|
||||
|
||||
styles = []
|
||||
names = set()
|
||||
|
||||
for ns in sorted(self.cellStyle, key=sort_fn):
|
||||
if ns.name in names:
|
||||
continue
|
||||
|
||||
style = NamedStyle(
|
||||
name=ns.name,
|
||||
hidden=ns.hidden,
|
||||
builtinId = ns.builtinId
|
||||
)
|
||||
names.add(ns.name)
|
||||
style._set_index(len(styles)) # assign xfId
|
||||
styles.append(style)
|
||||
|
||||
return NamedStyleList(styles)
|
200
.venv/Lib/site-packages/openpyxl/styles/numbers.py
Normal file
200
.venv/Lib/site-packages/openpyxl/styles/numbers.py
Normal file
@ -0,0 +1,200 @@
|
||||
# Copyright (c) 2010-2022 openpyxl
|
||||
|
||||
import re
|
||||
|
||||
from openpyxl.descriptors import (
|
||||
String,
|
||||
Sequence,
|
||||
Integer,
|
||||
)
|
||||
from openpyxl.descriptors.serialisable import Serialisable
|
||||
|
||||
|
||||
BUILTIN_FORMATS = {
|
||||
0: 'General',
|
||||
1: '0',
|
||||
2: '0.00',
|
||||
3: '#,##0',
|
||||
4: '#,##0.00',
|
||||
5: '"$"#,##0_);("$"#,##0)',
|
||||
6: '"$"#,##0_);[Red]("$"#,##0)',
|
||||
7: '"$"#,##0.00_);("$"#,##0.00)',
|
||||
8: '"$"#,##0.00_);[Red]("$"#,##0.00)',
|
||||
9: '0%',
|
||||
10: '0.00%',
|
||||
11: '0.00E+00',
|
||||
12: '# ?/?',
|
||||
13: '# ??/??',
|
||||
14: 'mm-dd-yy',
|
||||
15: 'd-mmm-yy',
|
||||
16: 'd-mmm',
|
||||
17: 'mmm-yy',
|
||||
18: 'h:mm AM/PM',
|
||||
19: 'h:mm:ss AM/PM',
|
||||
20: 'h:mm',
|
||||
21: 'h:mm:ss',
|
||||
22: 'm/d/yy h:mm',
|
||||
|
||||
37: '#,##0_);(#,##0)',
|
||||
38: '#,##0_);[Red](#,##0)',
|
||||
39: '#,##0.00_);(#,##0.00)',
|
||||
40: '#,##0.00_);[Red](#,##0.00)',
|
||||
|
||||
41: r'_(* #,##0_);_(* \(#,##0\);_(* "-"_);_(@_)',
|
||||
42: r'_("$"* #,##0_);_("$"* \(#,##0\);_("$"* "-"_);_(@_)',
|
||||
43: r'_(* #,##0.00_);_(* \(#,##0.00\);_(* "-"??_);_(@_)',
|
||||
|
||||
44: r'_("$"* #,##0.00_)_("$"* \(#,##0.00\)_("$"* "-"??_)_(@_)',
|
||||
45: 'mm:ss',
|
||||
46: '[h]:mm:ss',
|
||||
47: 'mmss.0',
|
||||
48: '##0.0E+0',
|
||||
49: '@', }
|
||||
|
||||
BUILTIN_FORMATS_MAX_SIZE = 164
|
||||
BUILTIN_FORMATS_REVERSE = dict(
|
||||
[(value, key) for key, value in BUILTIN_FORMATS.items()])
|
||||
|
||||
FORMAT_GENERAL = BUILTIN_FORMATS[0]
|
||||
FORMAT_TEXT = BUILTIN_FORMATS[49]
|
||||
FORMAT_NUMBER = BUILTIN_FORMATS[1]
|
||||
FORMAT_NUMBER_00 = BUILTIN_FORMATS[2]
|
||||
FORMAT_NUMBER_COMMA_SEPARATED1 = BUILTIN_FORMATS[4]
|
||||
FORMAT_NUMBER_COMMA_SEPARATED2 = '#,##0.00_-'
|
||||
FORMAT_PERCENTAGE = BUILTIN_FORMATS[9]
|
||||
FORMAT_PERCENTAGE_00 = BUILTIN_FORMATS[10]
|
||||
FORMAT_DATE_YYYYMMDD2 = 'yyyy-mm-dd'
|
||||
FORMAT_DATE_YYMMDD = 'yy-mm-dd'
|
||||
FORMAT_DATE_DDMMYY = 'dd/mm/yy'
|
||||
FORMAT_DATE_DMYSLASH = 'd/m/y'
|
||||
FORMAT_DATE_DMYMINUS = 'd-m-y'
|
||||
FORMAT_DATE_DMMINUS = 'd-m'
|
||||
FORMAT_DATE_MYMINUS = 'm-y'
|
||||
FORMAT_DATE_XLSX14 = BUILTIN_FORMATS[14]
|
||||
FORMAT_DATE_XLSX15 = BUILTIN_FORMATS[15]
|
||||
FORMAT_DATE_XLSX16 = BUILTIN_FORMATS[16]
|
||||
FORMAT_DATE_XLSX17 = BUILTIN_FORMATS[17]
|
||||
FORMAT_DATE_XLSX22 = BUILTIN_FORMATS[22]
|
||||
FORMAT_DATE_DATETIME = 'yyyy-mm-dd h:mm:ss'
|
||||
FORMAT_DATE_TIME1 = BUILTIN_FORMATS[18]
|
||||
FORMAT_DATE_TIME2 = BUILTIN_FORMATS[19]
|
||||
FORMAT_DATE_TIME3 = BUILTIN_FORMATS[20]
|
||||
FORMAT_DATE_TIME4 = BUILTIN_FORMATS[21]
|
||||
FORMAT_DATE_TIME5 = BUILTIN_FORMATS[45]
|
||||
FORMAT_DATE_TIME6 = BUILTIN_FORMATS[21]
|
||||
FORMAT_DATE_TIME7 = 'i:s.S'
|
||||
FORMAT_DATE_TIME8 = 'h:mm:ss@'
|
||||
FORMAT_DATE_TIMEDELTA = '[hh]:mm:ss'
|
||||
FORMAT_DATE_YYMMDDSLASH = 'yy/mm/dd@'
|
||||
FORMAT_CURRENCY_USD_SIMPLE = '"$"#,##0.00_-'
|
||||
FORMAT_CURRENCY_USD = '$#,##0_-'
|
||||
FORMAT_CURRENCY_EUR_SIMPLE = '[$EUR ]#,##0.00_-'
|
||||
|
||||
|
||||
COLORS = r"\[(BLACK|BLUE|CYAN|GREEN|MAGENTA|RED|WHITE|YELLOW)\]"
|
||||
LITERAL_GROUP = r'".*?"' # anything in quotes
|
||||
LOCALE_GROUP = r'\[(?!hh?\]|mm?\]|ss?\])[^\]]*\]' # anything in square brackets, except hours or minutes or seconds
|
||||
STRIP_RE = re.compile(f"{LITERAL_GROUP}|{LOCALE_GROUP}")
|
||||
TIMEDELTA_RE = re.compile(r'\[hh?\](:mm(:ss(\.0*)?)?)?|\[mm?\](:ss(\.0*)?)?|\[ss?\](\.0*)?', re.I)
|
||||
|
||||
|
||||
# Spec 18.8.31 numFmts
|
||||
# +ve;-ve;zero;text
|
||||
|
||||
def is_date_format(fmt):
|
||||
if fmt is None:
|
||||
return False
|
||||
fmt = fmt.split(";")[0] # only look at the first format
|
||||
fmt = STRIP_RE.sub("", fmt) # ignore some formats
|
||||
return re.search(r"[^\\][dmhysDMHYS]", fmt) is not None
|
||||
|
||||
|
||||
def is_timedelta_format(fmt):
|
||||
if fmt is None:
|
||||
return False
|
||||
fmt = fmt.split(";")[0] # only look at the first format
|
||||
return TIMEDELTA_RE.search(fmt) is not None
|
||||
|
||||
|
||||
def is_datetime(fmt):
|
||||
"""
|
||||
Return date, time or datetime
|
||||
"""
|
||||
if not is_date_format(fmt):
|
||||
return
|
||||
|
||||
DATE = TIME = False
|
||||
|
||||
if any((x in fmt for x in 'dy')):
|
||||
DATE = True
|
||||
if any((x in fmt for x in 'hs')):
|
||||
TIME = True
|
||||
|
||||
if DATE and TIME:
|
||||
return "datetime"
|
||||
if DATE:
|
||||
return "date"
|
||||
return "time"
|
||||
|
||||
|
||||
def is_builtin(fmt):
|
||||
return fmt in BUILTIN_FORMATS.values()
|
||||
|
||||
|
||||
def builtin_format_code(index):
|
||||
"""Return one of the standard format codes by index."""
|
||||
try:
|
||||
fmt = BUILTIN_FORMATS[index]
|
||||
except KeyError:
|
||||
fmt = None
|
||||
return fmt
|
||||
|
||||
|
||||
def builtin_format_id(fmt):
|
||||
"""Return the id of a standard style."""
|
||||
return BUILTIN_FORMATS_REVERSE.get(fmt)
|
||||
|
||||
|
||||
class NumberFormatDescriptor(String):
|
||||
|
||||
def __set__(self, instance, value):
|
||||
if value is None:
|
||||
value = FORMAT_GENERAL
|
||||
super(NumberFormatDescriptor, self).__set__(instance, value)
|
||||
|
||||
|
||||
class NumberFormat(Serialisable):
|
||||
|
||||
numFmtId = Integer()
|
||||
formatCode = String()
|
||||
|
||||
def __init__(self,
|
||||
numFmtId=None,
|
||||
formatCode=None,
|
||||
):
|
||||
self.numFmtId = numFmtId
|
||||
self.formatCode = formatCode
|
||||
|
||||
|
||||
class NumberFormatList(Serialisable):
|
||||
|
||||
count = Integer(allow_none=True)
|
||||
numFmt = Sequence(expected_type=NumberFormat)
|
||||
|
||||
__elements__ = ('numFmt',)
|
||||
__attrs__ = ("count",)
|
||||
|
||||
def __init__(self,
|
||||
count=None,
|
||||
numFmt=(),
|
||||
):
|
||||
self.numFmt = numFmt
|
||||
|
||||
|
||||
@property
|
||||
def count(self):
|
||||
return len(self.numFmt)
|
||||
|
||||
|
||||
def __getitem__(self, idx):
|
||||
return self.numFmt[idx]
|
17
.venv/Lib/site-packages/openpyxl/styles/protection.py
Normal file
17
.venv/Lib/site-packages/openpyxl/styles/protection.py
Normal file
@ -0,0 +1,17 @@
|
||||
# Copyright (c) 2010-2022 openpyxl
|
||||
|
||||
from openpyxl.descriptors import Bool
|
||||
from openpyxl.descriptors.serialisable import Serialisable
|
||||
|
||||
|
||||
class Protection(Serialisable):
|
||||
"""Protection options for use in styles."""
|
||||
|
||||
tagname = "protection"
|
||||
|
||||
locked = Bool()
|
||||
hidden = Bool()
|
||||
|
||||
def __init__(self, locked=True, hidden=False):
|
||||
self.locked = locked
|
||||
self.hidden = hidden
|
62
.venv/Lib/site-packages/openpyxl/styles/proxy.py
Normal file
62
.venv/Lib/site-packages/openpyxl/styles/proxy.py
Normal file
@ -0,0 +1,62 @@
|
||||
# Copyright (c) 2010-2022 openpyxl
|
||||
|
||||
from copy import copy
|
||||
|
||||
from openpyxl.compat import deprecated
|
||||
|
||||
|
||||
class StyleProxy(object):
|
||||
"""
|
||||
Proxy formatting objects so that they cannot be altered
|
||||
"""
|
||||
|
||||
__slots__ = ('__target')
|
||||
|
||||
def __init__(self, target):
|
||||
self.__target = target
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self.__target)
|
||||
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.__target, attr)
|
||||
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
if attr != "_StyleProxy__target":
|
||||
raise AttributeError("Style objects are immutable and cannot be changed."
|
||||
"Reassign the style with a copy")
|
||||
super(StyleProxy, self).__setattr__(attr, value)
|
||||
|
||||
|
||||
def __copy__(self):
|
||||
"""
|
||||
Return a copy of the proxied object.
|
||||
"""
|
||||
return copy(self.__target)
|
||||
|
||||
|
||||
def __add__(self, other):
|
||||
"""
|
||||
Add proxied object to another instance and return the combined object
|
||||
"""
|
||||
return self.__target + other
|
||||
|
||||
|
||||
@deprecated("Use copy(obj) or cell.obj = cell.obj + other")
|
||||
def copy(self, **kw):
|
||||
"""Return a copy of the proxied object. Keyword args will be passed through"""
|
||||
cp = copy(self.__target)
|
||||
for k, v in kw.items():
|
||||
setattr(cp, k, v)
|
||||
return cp
|
||||
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__target == other
|
||||
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
152
.venv/Lib/site-packages/openpyxl/styles/styleable.py
Normal file
152
.venv/Lib/site-packages/openpyxl/styles/styleable.py
Normal file
@ -0,0 +1,152 @@
|
||||
# Copyright (c) 2010-2022 openpyxl
|
||||
|
||||
from copy import copy
|
||||
from warnings import warn
|
||||
|
||||
from .numbers import (
|
||||
BUILTIN_FORMATS,
|
||||
BUILTIN_FORMATS_MAX_SIZE,
|
||||
BUILTIN_FORMATS_REVERSE,
|
||||
)
|
||||
from .proxy import StyleProxy
|
||||
from .cell_style import StyleArray
|
||||
from .named_styles import NamedStyle
|
||||
from .builtins import styles
|
||||
|
||||
|
||||
class StyleDescriptor(object):
|
||||
|
||||
def __init__(self, collection, key):
|
||||
self.collection = collection
|
||||
self.key = key
|
||||
|
||||
def __set__(self, instance, value):
|
||||
coll = getattr(instance.parent.parent, self.collection)
|
||||
if not getattr(instance, "_style"):
|
||||
instance._style = StyleArray()
|
||||
setattr(instance._style, self.key, coll.add(value))
|
||||
|
||||
|
||||
def __get__(self, instance, cls):
|
||||
coll = getattr(instance.parent.parent, self.collection)
|
||||
if not getattr(instance, "_style"):
|
||||
instance._style = StyleArray()
|
||||
idx = getattr(instance._style, self.key)
|
||||
return StyleProxy(coll[idx])
|
||||
|
||||
|
||||
class NumberFormatDescriptor(object):
|
||||
|
||||
key = "numFmtId"
|
||||
collection = '_number_formats'
|
||||
|
||||
def __set__(self, instance, value):
|
||||
coll = getattr(instance.parent.parent, self.collection)
|
||||
if value in BUILTIN_FORMATS_REVERSE:
|
||||
idx = BUILTIN_FORMATS_REVERSE[value]
|
||||
else:
|
||||
idx = coll.add(value) + BUILTIN_FORMATS_MAX_SIZE
|
||||
|
||||
if not getattr(instance, "_style"):
|
||||
instance._style = StyleArray()
|
||||
setattr(instance._style, self.key, idx)
|
||||
|
||||
|
||||
def __get__(self, instance, cls):
|
||||
if not getattr(instance, "_style"):
|
||||
instance._style = StyleArray()
|
||||
idx = getattr(instance._style, self.key)
|
||||
if idx < BUILTIN_FORMATS_MAX_SIZE:
|
||||
return BUILTIN_FORMATS.get(idx, "General")
|
||||
coll = getattr(instance.parent.parent, self.collection)
|
||||
return coll[idx - BUILTIN_FORMATS_MAX_SIZE]
|
||||
|
||||
|
||||
class NamedStyleDescriptor(object):
|
||||
|
||||
key = "xfId"
|
||||
collection = "_named_styles"
|
||||
|
||||
|
||||
def __set__(self, instance, value):
|
||||
if not getattr(instance, "_style"):
|
||||
instance._style = StyleArray()
|
||||
coll = getattr(instance.parent.parent, self.collection)
|
||||
if isinstance(value, NamedStyle):
|
||||
style = value
|
||||
if style not in coll:
|
||||
instance.parent.parent.add_named_style(style)
|
||||
elif value not in coll.names:
|
||||
if value in styles: # is it builtin?
|
||||
style = styles[value]
|
||||
if style not in coll:
|
||||
instance.parent.parent.add_named_style(style)
|
||||
else:
|
||||
raise ValueError("{0} is not a known style".format(value))
|
||||
else:
|
||||
style = coll[value]
|
||||
instance._style = copy(style.as_tuple())
|
||||
|
||||
|
||||
def __get__(self, instance, cls):
|
||||
if not getattr(instance, "_style"):
|
||||
instance._style = StyleArray()
|
||||
idx = getattr(instance._style, self.key)
|
||||
coll = getattr(instance.parent.parent, self.collection)
|
||||
return coll.names[idx]
|
||||
|
||||
|
||||
class StyleArrayDescriptor(object):
|
||||
|
||||
def __init__(self, key):
|
||||
self.key = key
|
||||
|
||||
def __set__(self, instance, value):
|
||||
if instance._style is None:
|
||||
instance._style = StyleArray()
|
||||
setattr(instance._style, self.key, value)
|
||||
|
||||
|
||||
def __get__(self, instance, cls):
|
||||
if instance._style is None:
|
||||
return False
|
||||
return bool(getattr(instance._style, self.key))
|
||||
|
||||
|
||||
class StyleableObject(object):
|
||||
"""
|
||||
Base class for styleble objects implementing proxy and lookup functions
|
||||
"""
|
||||
|
||||
font = StyleDescriptor('_fonts', "fontId")
|
||||
fill = StyleDescriptor('_fills', "fillId")
|
||||
border = StyleDescriptor('_borders', "borderId")
|
||||
number_format = NumberFormatDescriptor()
|
||||
protection = StyleDescriptor('_protections', "protectionId")
|
||||
alignment = StyleDescriptor('_alignments', "alignmentId")
|
||||
style = NamedStyleDescriptor()
|
||||
quotePrefix = StyleArrayDescriptor('quotePrefix')
|
||||
pivotButton = StyleArrayDescriptor('pivotButton')
|
||||
|
||||
__slots__ = ('parent', '_style')
|
||||
|
||||
def __init__(self, sheet, style_array=None):
|
||||
self.parent = sheet
|
||||
if style_array is not None:
|
||||
style_array = StyleArray(style_array)
|
||||
self._style = style_array
|
||||
|
||||
|
||||
@property
|
||||
def style_id(self):
|
||||
if self._style is None:
|
||||
self._style = StyleArray()
|
||||
return self.parent.parent._cell_styles.add(self._style)
|
||||
|
||||
|
||||
@property
|
||||
def has_style(self):
|
||||
if self._style is None:
|
||||
return False
|
||||
return any(self._style)
|
||||
|
263
.venv/Lib/site-packages/openpyxl/styles/stylesheet.py
Normal file
263
.venv/Lib/site-packages/openpyxl/styles/stylesheet.py
Normal file
@ -0,0 +1,263 @@
|
||||
# Copyright (c) 2010-2022 openpyxl
|
||||
|
||||
from warnings import warn
|
||||
|
||||
from openpyxl.descriptors.serialisable import Serialisable
|
||||
from openpyxl.descriptors import (
|
||||
Typed,
|
||||
)
|
||||
from openpyxl.descriptors.sequence import NestedSequence
|
||||
from openpyxl.descriptors.excel import ExtensionList
|
||||
from openpyxl.utils.indexed_list import IndexedList
|
||||
from openpyxl.xml.constants import ARC_STYLE, SHEET_MAIN_NS
|
||||
from openpyxl.xml.functions import fromstring
|
||||
|
||||
from .builtins import styles
|
||||
from .colors import ColorList, COLOR_INDEX
|
||||
from .differential import DifferentialStyle
|
||||
from .table import TableStyleList
|
||||
from .borders import Border
|
||||
from .fills import Fill
|
||||
from .fonts import Font
|
||||
from .numbers import (
|
||||
NumberFormatList,
|
||||
BUILTIN_FORMATS,
|
||||
BUILTIN_FORMATS_MAX_SIZE,
|
||||
BUILTIN_FORMATS_REVERSE,
|
||||
is_date_format,
|
||||
is_timedelta_format,
|
||||
builtin_format_code
|
||||
)
|
||||
from .named_styles import (
|
||||
_NamedCellStyleList
|
||||
)
|
||||
from .cell_style import CellStyle, CellStyleList
|
||||
|
||||
|
||||
class Stylesheet(Serialisable):
|
||||
|
||||
tagname = "styleSheet"
|
||||
|
||||
numFmts = Typed(expected_type=NumberFormatList)
|
||||
fonts = NestedSequence(expected_type=Font, count=True)
|
||||
fills = NestedSequence(expected_type=Fill, count=True)
|
||||
borders = NestedSequence(expected_type=Border, count=True)
|
||||
cellStyleXfs = Typed(expected_type=CellStyleList)
|
||||
cellXfs = Typed(expected_type=CellStyleList)
|
||||
cellStyles = Typed(expected_type=_NamedCellStyleList)
|
||||
dxfs = NestedSequence(expected_type=DifferentialStyle, count=True)
|
||||
tableStyles = Typed(expected_type=TableStyleList, allow_none=True)
|
||||
colors = Typed(expected_type=ColorList, allow_none=True)
|
||||
extLst = Typed(expected_type=ExtensionList, allow_none=True)
|
||||
|
||||
__elements__ = ('numFmts', 'fonts', 'fills', 'borders', 'cellStyleXfs',
|
||||
'cellXfs', 'cellStyles', 'dxfs', 'tableStyles', 'colors')
|
||||
|
||||
def __init__(self,
|
||||
numFmts=None,
|
||||
fonts=(),
|
||||
fills=(),
|
||||
borders=(),
|
||||
cellStyleXfs=None,
|
||||
cellXfs=None,
|
||||
cellStyles=None,
|
||||
dxfs=(),
|
||||
tableStyles=None,
|
||||
colors=None,
|
||||
extLst=None,
|
||||
):
|
||||
if numFmts is None:
|
||||
numFmts = NumberFormatList()
|
||||
self.numFmts = numFmts
|
||||
self.number_formats = IndexedList()
|
||||
self.fonts = fonts
|
||||
self.fills = fills
|
||||
self.borders = borders
|
||||
if cellStyleXfs is None:
|
||||
cellStyleXfs = CellStyleList()
|
||||
self.cellStyleXfs = cellStyleXfs
|
||||
if cellXfs is None:
|
||||
cellXfs = CellStyleList()
|
||||
self.cellXfs = cellXfs
|
||||
if cellStyles is None:
|
||||
cellStyles = _NamedCellStyleList()
|
||||
self.cellStyles = cellStyles
|
||||
|
||||
self.dxfs = dxfs
|
||||
self.tableStyles = tableStyles
|
||||
self.colors = colors
|
||||
|
||||
self.cell_styles = self.cellXfs._to_array()
|
||||
self.alignments = self.cellXfs.alignments
|
||||
self.protections = self.cellXfs.prots
|
||||
self._normalise_numbers()
|
||||
self.named_styles = self._merge_named_styles()
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_tree(cls, node):
|
||||
# strip all attribs
|
||||
attrs = dict(node.attrib)
|
||||
for k in attrs:
|
||||
del node.attrib[k]
|
||||
return super(Stylesheet, cls).from_tree(node)
|
||||
|
||||
|
||||
def _merge_named_styles(self):
|
||||
"""
|
||||
Merge named style names "cellStyles" with their associated styles
|
||||
"cellStyleXfs"
|
||||
"""
|
||||
named_styles = self.cellStyles.names
|
||||
|
||||
for style in named_styles:
|
||||
self._expand_named_style(style)
|
||||
|
||||
return named_styles
|
||||
|
||||
|
||||
def _expand_named_style(self, named_style):
|
||||
"""
|
||||
Bind format definitions for a named style from the associated style
|
||||
record
|
||||
"""
|
||||
xf = self.cellStyleXfs[named_style.xfId]
|
||||
named_style.font = self.fonts[xf.fontId]
|
||||
named_style.fill = self.fills[xf.fillId]
|
||||
named_style.border = self.borders[xf.borderId]
|
||||
if xf.numFmtId < BUILTIN_FORMATS_MAX_SIZE:
|
||||
formats = BUILTIN_FORMATS
|
||||
else:
|
||||
formats = self.custom_formats
|
||||
if xf.numFmtId in formats:
|
||||
named_style.number_format = formats[xf.numFmtId]
|
||||
if xf.alignment:
|
||||
named_style.alignment = xf.alignment
|
||||
if xf.protection:
|
||||
named_style.protection = xf.protection
|
||||
|
||||
|
||||
def _split_named_styles(self, wb):
|
||||
"""
|
||||
Convert NamedStyle into separate CellStyle and Xf objects
|
||||
"""
|
||||
for style in wb._named_styles:
|
||||
self.cellStyles.cellStyle.append(style.as_name())
|
||||
self.cellStyleXfs.xf.append(style.as_xf())
|
||||
|
||||
|
||||
@property
|
||||
def custom_formats(self):
|
||||
return dict([(n.numFmtId, n.formatCode) for n in self.numFmts.numFmt])
|
||||
|
||||
|
||||
def _normalise_numbers(self):
|
||||
"""
|
||||
Rebase custom numFmtIds with a floor of 164 when reading stylesheet
|
||||
And index datetime formats
|
||||
"""
|
||||
date_formats = set()
|
||||
timedelta_formats = set()
|
||||
custom = self.custom_formats
|
||||
formats = self.number_formats
|
||||
for idx, style in enumerate(self.cell_styles):
|
||||
if style.numFmtId in custom:
|
||||
fmt = custom[style.numFmtId]
|
||||
if fmt in BUILTIN_FORMATS_REVERSE: # remove builtins
|
||||
style.numFmtId = BUILTIN_FORMATS_REVERSE[fmt]
|
||||
else:
|
||||
style.numFmtId = formats.add(fmt) + BUILTIN_FORMATS_MAX_SIZE
|
||||
else:
|
||||
fmt = builtin_format_code(style.numFmtId)
|
||||
if is_date_format(fmt):
|
||||
# Create an index of which styles refer to datetimes
|
||||
date_formats.add(idx)
|
||||
if is_timedelta_format(fmt):
|
||||
# Create an index of which styles refer to timedeltas
|
||||
timedelta_formats.add(idx)
|
||||
self.date_formats = date_formats
|
||||
self.timedelta_formats = timedelta_formats
|
||||
|
||||
|
||||
def to_tree(self, tagname=None, idx=None, namespace=None):
|
||||
tree = super(Stylesheet, self).to_tree(tagname, idx, namespace)
|
||||
tree.set("xmlns", SHEET_MAIN_NS)
|
||||
return tree
|
||||
|
||||
|
||||
def apply_stylesheet(archive, wb):
|
||||
"""
|
||||
Add styles to workbook if present
|
||||
"""
|
||||
try:
|
||||
src = archive.read(ARC_STYLE)
|
||||
except KeyError:
|
||||
return wb
|
||||
|
||||
node = fromstring(src)
|
||||
stylesheet = Stylesheet.from_tree(node)
|
||||
|
||||
if stylesheet.cell_styles:
|
||||
|
||||
wb._borders = IndexedList(stylesheet.borders)
|
||||
wb._fonts = IndexedList(stylesheet.fonts)
|
||||
wb._fills = IndexedList(stylesheet.fills)
|
||||
wb._differential_styles.styles = stylesheet.dxfs
|
||||
wb._number_formats = stylesheet.number_formats
|
||||
wb._protections = stylesheet.protections
|
||||
wb._alignments = stylesheet.alignments
|
||||
wb._table_styles = stylesheet.tableStyles
|
||||
|
||||
# need to overwrite openpyxl defaults in case workbook has different ones
|
||||
wb._cell_styles = stylesheet.cell_styles
|
||||
wb._named_styles = stylesheet.named_styles
|
||||
wb._date_formats = stylesheet.date_formats
|
||||
wb._timedelta_formats = stylesheet.timedelta_formats
|
||||
|
||||
for ns in wb._named_styles:
|
||||
ns.bind(wb)
|
||||
|
||||
else:
|
||||
warn("Workbook contains no stylesheet, using openpyxl's defaults")
|
||||
|
||||
if not wb._named_styles:
|
||||
normal = styles['Normal']
|
||||
wb.add_named_style(normal)
|
||||
warn("Workbook contains no default style, apply openpyxl's default")
|
||||
|
||||
if stylesheet.colors is not None:
|
||||
wb._colors = stylesheet.colors.index
|
||||
|
||||
|
||||
def write_stylesheet(wb):
|
||||
stylesheet = Stylesheet()
|
||||
stylesheet.fonts = wb._fonts
|
||||
stylesheet.fills = wb._fills
|
||||
stylesheet.borders = wb._borders
|
||||
stylesheet.dxfs = wb._differential_styles.styles
|
||||
stylesheet.colors = ColorList(indexedColors=wb._colors)
|
||||
|
||||
from .numbers import NumberFormat
|
||||
fmts = []
|
||||
for idx, code in enumerate(wb._number_formats, BUILTIN_FORMATS_MAX_SIZE):
|
||||
fmt = NumberFormat(idx, code)
|
||||
fmts.append(fmt)
|
||||
|
||||
stylesheet.numFmts.numFmt = fmts
|
||||
|
||||
xfs = []
|
||||
for style in wb._cell_styles:
|
||||
xf = CellStyle.from_array(style)
|
||||
|
||||
if style.alignmentId:
|
||||
xf.alignment = wb._alignments[style.alignmentId]
|
||||
|
||||
if style.protectionId:
|
||||
xf.protection = wb._protections[style.protectionId]
|
||||
xfs.append(xf)
|
||||
stylesheet.cellXfs = CellStyleList(xf=xfs)
|
||||
|
||||
stylesheet._split_named_styles(wb)
|
||||
stylesheet.tableStyles = wb._table_styles
|
||||
|
||||
return stylesheet.to_tree()
|
94
.venv/Lib/site-packages/openpyxl/styles/table.py
Normal file
94
.venv/Lib/site-packages/openpyxl/styles/table.py
Normal file
@ -0,0 +1,94 @@
|
||||
# Copyright (c) 2010-2022 openpyxl
|
||||
|
||||
from openpyxl.descriptors.serialisable import Serialisable
|
||||
from openpyxl.descriptors import (
|
||||
Typed,
|
||||
Float,
|
||||
Bool,
|
||||
Set,
|
||||
Integer,
|
||||
NoneSet,
|
||||
String,
|
||||
Sequence
|
||||
)
|
||||
|
||||
from .colors import Color
|
||||
|
||||
|
||||
class TableStyleElement(Serialisable):
|
||||
|
||||
tagname = "tableStyleElement"
|
||||
|
||||
type = Set(values=(['wholeTable', 'headerRow', 'totalRow', 'firstColumn',
|
||||
'lastColumn', 'firstRowStripe', 'secondRowStripe', 'firstColumnStripe',
|
||||
'secondColumnStripe', 'firstHeaderCell', 'lastHeaderCell',
|
||||
'firstTotalCell', 'lastTotalCell', 'firstSubtotalColumn',
|
||||
'secondSubtotalColumn', 'thirdSubtotalColumn', 'firstSubtotalRow',
|
||||
'secondSubtotalRow', 'thirdSubtotalRow', 'blankRow',
|
||||
'firstColumnSubheading', 'secondColumnSubheading',
|
||||
'thirdColumnSubheading', 'firstRowSubheading', 'secondRowSubheading',
|
||||
'thirdRowSubheading', 'pageFieldLabels', 'pageFieldValues']))
|
||||
size = Integer(allow_none=True)
|
||||
dxfId = Integer(allow_none=True)
|
||||
|
||||
def __init__(self,
|
||||
type=None,
|
||||
size=None,
|
||||
dxfId=None,
|
||||
):
|
||||
self.type = type
|
||||
self.size = size
|
||||
self.dxfId = dxfId
|
||||
|
||||
|
||||
class TableStyle(Serialisable):
|
||||
|
||||
tagname = "tableStyle"
|
||||
|
||||
name = String()
|
||||
pivot = Bool(allow_none=True)
|
||||
table = Bool(allow_none=True)
|
||||
count = Integer(allow_none=True)
|
||||
tableStyleElement = Sequence(expected_type=TableStyleElement, allow_none=True)
|
||||
|
||||
__elements__ = ('tableStyleElement',)
|
||||
|
||||
def __init__(self,
|
||||
name=None,
|
||||
pivot=None,
|
||||
table=None,
|
||||
count=None,
|
||||
tableStyleElement=(),
|
||||
):
|
||||
self.name = name
|
||||
self.pivot = pivot
|
||||
self.table = table
|
||||
self.count = count
|
||||
self.tableStyleElement = tableStyleElement
|
||||
|
||||
|
||||
class TableStyleList(Serialisable):
|
||||
|
||||
tagname = "tableStyles"
|
||||
|
||||
defaultTableStyle = String(allow_none=True)
|
||||
defaultPivotStyle = String(allow_none=True)
|
||||
tableStyle = Sequence(expected_type=TableStyle, allow_none=True)
|
||||
|
||||
__elements__ = ('tableStyle',)
|
||||
__attrs__ = ("count", "defaultTableStyle", "defaultPivotStyle")
|
||||
|
||||
def __init__(self,
|
||||
count=None,
|
||||
defaultTableStyle="TableStyleMedium9",
|
||||
defaultPivotStyle="PivotStyleLight16",
|
||||
tableStyle=(),
|
||||
):
|
||||
self.defaultTableStyle = defaultTableStyle
|
||||
self.defaultPivotStyle = defaultPivotStyle
|
||||
self.tableStyle = tableStyle
|
||||
|
||||
|
||||
@property
|
||||
def count(self):
|
||||
return len(self.tableStyle)
|
Reference in New Issue
Block a user