Finally! :sparkles:Python 3 port of openpyxl

This commit is contained in:
Kenneth Reitz
2011-05-13 00:28:50 -04:00
parent 2b36d71554
commit a60e2f132e
29 changed files with 1657 additions and 1651 deletions
+2 -2
View File
@@ -335,7 +335,7 @@ class Cell(object):
@property
def style(self):
"""Returns the :class:`openpyxl.style.Style` object for this cell"""
"""Returns the :class:`.style.Style` object for this cell"""
return self.parent.get_style(self.get_coordinate())
@property
@@ -367,7 +367,7 @@ class Cell(object):
:param column: number of columns to offset
:type column: int
:rtype: :class:`openpyxl.cell.Cell`
:rtype: :class:`.cell.Cell`
"""
offset_column = get_column_letter(column_index_from_string(
column = self.column) + column)
+340 -340
View File
@@ -1,340 +1,340 @@
'''
Copyright (c) 2010 openpyxl
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@license: http://www.opensource.org/licenses/mit-license.php
@author: Eric Gazoni
'''
import math
from .style import NumberFormat
from .drawing import Drawing, Shape
from .shared.units import pixels_to_EMU, short_color
from .cell import get_column_letter
class Axis(object):
POSITION_BOTTOM = 'b'
POSITION_LEFT = 'l'
ORIENTATION_MIN_MAX = "minMax"
def __init__(self):
self.orientation = self.ORIENTATION_MIN_MAX
self.number_format = NumberFormat()
for attr in ('position','tick_label_position','crosses',
'auto','label_align','label_offset','cross_between'):
setattr(self, attr, None)
self.min = 0
self.max = None
self.unit = None
@classmethod
def default_category(cls):
""" default values for category axes """
ax = Axis()
ax.id = 60871424
ax.cross = 60873344
ax.position = Axis.POSITION_BOTTOM
ax.tick_label_position = 'nextTo'
ax.crosses = "autoZero"
ax.auto = True
ax.label_align = 'ctr'
ax.label_offset = 100
return ax
@classmethod
def default_value(cls):
""" default values for value axes """
ax = Axis()
ax.id = 60873344
ax.cross = 60871424
ax.position = Axis.POSITION_LEFT
ax.major_gridlines = None
ax.tick_label_position = 'nextTo'
ax.crosses = 'autoZero'
ax.auto = False
ax.cross_between = 'between'
return ax
class Reference(object):
""" a simple wrapper around a serie of reference data """
def __init__(self, sheet, pos1, pos2=None):
self.sheet = sheet
self.pos1 = pos1
self.pos2 = pos2
def get_type(self):
if isinstance(self.cache[0], str):
return 'str'
else:
return 'num'
def _get_ref(self):
""" format excel reference notation """
if self.pos2:
return '%s!$%s$%s:$%s$%s' % (self.sheet.title,
get_column_letter(self.pos1[1]+1), self.pos1[0]+1,
get_column_letter(self.pos2[1]+1), self.pos2[0]+1)
else:
return '%s!$%s$%s' % (self.sheet.title,
get_column_letter(self.pos1[1]+1), self.pos1[0]+1)
def _get_cache(self):
""" read data in sheet - to be used at writing time """
cache = []
if self.pos2:
for row in range(self.pos1[0], self.pos2[0]+1):
for col in range(self.pos1[1], self.pos2[1]+1):
cache.append(self.sheet.cell(row=row, column=col).value)
else:
cell = self.sheet.cell(row=self.pos1[0], column=self.pos1[1])
cache.append(cell.value)
return cache
class Serie(object):
""" a serie of data and possibly associated labels """
MARKER_NONE = 'none'
def __init__(self, values, labels=None, legend=None, color=None, xvalues=None):
self.marker = Serie.MARKER_NONE
self.values = values
self.xvalues = xvalues
self.labels = labels
self.legend = legend
self.error_bar = None
self._color = color
def _get_color(self):
return self._color
def _set_color(self, color):
self._color = short_color(color)
color = property(_get_color, _set_color)
def get_min_max(self):
if self.error_bar:
err_cache = self.error_bar.values._get_cache()
vals = [v + err_cache[i] \
for i,v in enumerate(self.values._get_cache())]
else:
vals = self.values._get_cache()
return min(vals), max(vals)
def __len__(self):
return len(self.values.cache)
class Legend(object):
def __init__(self):
self.position = 'r'
self.layout = None
class ErrorBar(object):
PLUS = 1
MINUS = 2
PLUS_MINUS = 3
def __init__(self, _type, values):
self.type = _type
self.values = values
class Chart(object):
""" raw chart class """
GROUPING_CLUSTERED = 'clustered'
GROUPING_STANDARD = 'standard'
BAR_CHART = 1
LINE_CHART = 2
SCATTER_CHART = 3
def __init__(self, _type, grouping):
self._series = []
# public api
self.type = _type
self.grouping = grouping
self.x_axis = Axis.default_category()
self.y_axis = Axis.default_value()
self.legend = Legend()
self.lang = 'fr-FR'
self.title = ''
self.print_margins = dict(b=.75, l=.7, r=.7, t=.75, header=0.3, footer=.3)
# the containing drawing
self.drawing = Drawing()
# the offset for the plot part in percentage of the drawing size
self.width = .6
self.height = .6
self.margin_top = self._get_max_margin_top()
self.margin_left = 0
# the user defined shapes
self._shapes = []
def add_serie(self, serie):
serie.id = len(self._series)
self._series.append(serie)
self._compute_min_max()
if not None in [s.xvalues for s in self._series]:
self._compute_xmin_xmax()
def add_shape(self, shape):
shape._chart = self
self._shapes.append(shape)
def get_x_units(self):
""" calculate one unit for x axis in EMU """
return max([len(s.values._get_cache()) for s in self._series])
def get_y_units(self):
""" calculate one unit for y axis in EMU """
dh = pixels_to_EMU(self.drawing.height)
return (dh * self.height) / self.y_axis.max
def get_y_chars(self):
""" estimate nb of chars for y axis """
_max = max([max([x for x in s.values._get_cache() if x is not None]) for s in self._series])
return len(str(int(_max)))
def _compute_min_max(self):
""" compute y axis limits and units """
maxi = max([max([x for x in s.values._get_cache() if x is not None]) for s in self._series])
mul = None
if maxi < 1:
s = str(maxi).split('.')[1]
mul = 10
for x in s:
if x == '0':
mul *= 10
else:
break
maxi = maxi * mul
maxi = math.ceil(maxi * 1.1)
sz = len(str(int(maxi))) - 1
unit = math.ceil(math.ceil(maxi / pow(10, sz)) * pow(10, sz-1))
maxi = math.ceil(maxi/unit) * unit
if mul is not None:
maxi = maxi/mul
unit = unit/mul
if maxi / unit > 9:
# no more that 10 ticks
unit *= 2
self.y_axis.max = maxi
self.y_axis.unit = unit
def _compute_xmin_xmax(self):
""" compute x axis limits and units """
maxi = max([max([x for x in s.xvalues._get_cache() if x is not None]) for s in self._series])
mul = None
if maxi < 1:
s = str(maxi).split('.')[1]
mul = 10
for x in s:
if x == '0':
mul *= 10
else:
break
maxi = maxi * mul
maxi = math.ceil(maxi * 1.1)
sz = len(str(int(maxi))) - 1
unit = math.ceil(math.ceil(maxi / pow(10, sz)) * pow(10, sz-1))
maxi = math.ceil(maxi/unit) * unit
if mul is not None:
maxi = maxi/mul
unit = unit/mul
if maxi / unit > 9:
# no more that 10 ticks
unit *= 2
self.x_axis.max = maxi
self.x_axis.unit = unit
def _get_max_margin_top(self):
mb = Shape.FONT_HEIGHT + Shape.MARGIN_BOTTOM
plot_height = self.drawing.height * self.height
return float(self.drawing.height - plot_height - mb)/self.drawing.height
def _get_min_margin_left(self):
ml = (self.get_y_chars() * Shape.FONT_WIDTH) + Shape.MARGIN_LEFT
return float(ml)/self.drawing.width
def _get_margin_top(self):
""" get margin in percent """
return min(self.margin_top, self._get_max_margin_top())
def _get_margin_left(self):
return max(self._get_min_margin_left(), self.margin_left)
class BarChart(Chart):
def __init__(self):
super(BarChart, self).__init__(Chart.BAR_CHART, Chart.GROUPING_CLUSTERED)
class LineChart(Chart):
def __init__(self):
super(LineChart, self).__init__(Chart.LINE_CHART, Chart.GROUPING_STANDARD)
class ScatterChart(Chart):
def __init__(self):
super(ScatterChart, self).__init__(Chart.SCATTER_CHART, Chart.GROUPING_STANDARD)
'''
Copyright (c) 2010 openpyxl
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@license: http://www.opensource.org/licenses/mit-license.php
@author: Eric Gazoni
'''
import math
from .style import NumberFormat
from .drawing import Drawing, Shape
from .shared.units import pixels_to_EMU, short_color
from .cell import get_column_letter
class Axis(object):
POSITION_BOTTOM = 'b'
POSITION_LEFT = 'l'
ORIENTATION_MIN_MAX = "minMax"
def __init__(self):
self.orientation = self.ORIENTATION_MIN_MAX
self.number_format = NumberFormat()
for attr in ('position','tick_label_position','crosses',
'auto','label_align','label_offset','cross_between'):
setattr(self, attr, None)
self.min = 0
self.max = None
self.unit = None
@classmethod
def default_category(cls):
""" default values for category axes """
ax = Axis()
ax.id = 60871424
ax.cross = 60873344
ax.position = Axis.POSITION_BOTTOM
ax.tick_label_position = 'nextTo'
ax.crosses = "autoZero"
ax.auto = True
ax.label_align = 'ctr'
ax.label_offset = 100
return ax
@classmethod
def default_value(cls):
""" default values for value axes """
ax = Axis()
ax.id = 60873344
ax.cross = 60871424
ax.position = Axis.POSITION_LEFT
ax.major_gridlines = None
ax.tick_label_position = 'nextTo'
ax.crosses = 'autoZero'
ax.auto = False
ax.cross_between = 'between'
return ax
class Reference(object):
""" a simple wrapper around a serie of reference data """
def __init__(self, sheet, pos1, pos2=None):
self.sheet = sheet
self.pos1 = pos1
self.pos2 = pos2
def get_type(self):
if isinstance(self.cache[0], str):
return 'str'
else:
return 'num'
def _get_ref(self):
""" format excel reference notation """
if self.pos2:
return '%s!$%s$%s:$%s$%s' % (self.sheet.title,
get_column_letter(self.pos1[1]+1), self.pos1[0]+1,
get_column_letter(self.pos2[1]+1), self.pos2[0]+1)
else:
return '%s!$%s$%s' % (self.sheet.title,
get_column_letter(self.pos1[1]+1), self.pos1[0]+1)
def _get_cache(self):
""" read data in sheet - to be used at writing time """
cache = []
if self.pos2:
for row in range(self.pos1[0], self.pos2[0]+1):
for col in range(self.pos1[1], self.pos2[1]+1):
cache.append(self.sheet.cell(row=row, column=col).value)
else:
cell = self.sheet.cell(row=self.pos1[0], column=self.pos1[1])
cache.append(cell.value)
return cache
class Serie(object):
""" a serie of data and possibly associated labels """
MARKER_NONE = 'none'
def __init__(self, values, labels=None, legend=None, color=None, xvalues=None):
self.marker = Serie.MARKER_NONE
self.values = values
self.xvalues = xvalues
self.labels = labels
self.legend = legend
self.error_bar = None
self._color = color
def _get_color(self):
return self._color
def _set_color(self, color):
self._color = short_color(color)
color = property(_get_color, _set_color)
def get_min_max(self):
if self.error_bar:
err_cache = self.error_bar.values._get_cache()
vals = [v + err_cache[i] \
for i,v in enumerate(self.values._get_cache())]
else:
vals = self.values._get_cache()
return min(vals), max(vals)
def __len__(self):
return len(self.values.cache)
class Legend(object):
def __init__(self):
self.position = 'r'
self.layout = None
class ErrorBar(object):
PLUS = 1
MINUS = 2
PLUS_MINUS = 3
def __init__(self, _type, values):
self.type = _type
self.values = values
class Chart(object):
""" raw chart class """
GROUPING_CLUSTERED = 'clustered'
GROUPING_STANDARD = 'standard'
BAR_CHART = 1
LINE_CHART = 2
SCATTER_CHART = 3
def __init__(self, _type, grouping):
self._series = []
# public api
self.type = _type
self.grouping = grouping
self.x_axis = Axis.default_category()
self.y_axis = Axis.default_value()
self.legend = Legend()
self.lang = 'fr-FR'
self.title = ''
self.print_margins = dict(b=.75, l=.7, r=.7, t=.75, header=0.3, footer=.3)
# the containing drawing
self.drawing = Drawing()
# the offset for the plot part in percentage of the drawing size
self.width = .6
self.height = .6
self.margin_top = self._get_max_margin_top()
self.margin_left = 0
# the user defined shapes
self._shapes = []
def add_serie(self, serie):
serie.id = len(self._series)
self._series.append(serie)
self._compute_min_max()
if not None in [s.xvalues for s in self._series]:
self._compute_xmin_xmax()
def add_shape(self, shape):
shape._chart = self
self._shapes.append(shape)
def get_x_units(self):
""" calculate one unit for x axis in EMU """
return max([len(s.values._get_cache()) for s in self._series])
def get_y_units(self):
""" calculate one unit for y axis in EMU """
dh = pixels_to_EMU(self.drawing.height)
return (dh * self.height) / self.y_axis.max
def get_y_chars(self):
""" estimate nb of chars for y axis """
_max = max([max(s.values._get_cache()) for s in self._series])
return len(str(int(_max)))
def _compute_min_max(self):
""" compute y axis limits and units """
maxi = max([max(s.values._get_cache()) for s in self._series])
mul = None
if maxi < 1:
s = str(maxi).split('.')[1]
mul = 10
for x in s:
if x == '0':
mul *= 10
else:
break
maxi = maxi * mul
maxi = math.ceil(maxi * 1.1)
sz = len(str(int(maxi))) - 1
unit = math.ceil(math.ceil(maxi / pow(10, sz)) * pow(10, sz-1))
maxi = math.ceil(maxi/unit) * unit
if mul is not None:
maxi = maxi/mul
unit = unit/mul
if maxi / unit > 9:
# no more that 10 ticks
unit *= 2
self.y_axis.max = maxi
self.y_axis.unit = unit
def _compute_xmin_xmax(self):
""" compute x axis limits and units """
maxi = max([max(s.xvalues._get_cache()) for s in self._series])
mul = None
if maxi < 1:
s = str(maxi).split('.')[1]
mul = 10
for x in s:
if x == '0':
mul *= 10
else:
break
maxi = maxi * mul
maxi = math.ceil(maxi * 1.1)
sz = len(str(int(maxi))) - 1
unit = math.ceil(math.ceil(maxi / pow(10, sz)) * pow(10, sz-1))
maxi = math.ceil(maxi/unit) * unit
if mul is not None:
maxi = maxi/mul
unit = unit/mul
if maxi / unit > 9:
# no more that 10 ticks
unit *= 2
self.x_axis.max = maxi
self.x_axis.unit = unit
def _get_max_margin_top(self):
mb = Shape.FONT_HEIGHT + Shape.MARGIN_BOTTOM
plot_height = self.drawing.height * self.height
return float(self.drawing.height - plot_height - mb)/self.drawing.height
def _get_min_margin_left(self):
ml = (self.get_y_chars() * Shape.FONT_WIDTH) + Shape.MARGIN_LEFT
return float(ml)/self.drawing.width
def _get_margin_top(self):
""" get margin in percent """
return min(self.margin_top, self._get_max_margin_top())
def _get_margin_left(self):
return max(self._get_min_margin_left(), self.margin_left)
class BarChart(Chart):
def __init__(self):
super(BarChart, self).__init__(Chart.BAR_CHART, Chart.GROUPING_CLUSTERED)
class LineChart(Chart):
def __init__(self):
super(LineChart, self).__init__(Chart.LINE_CHART, Chart.GROUPING_STANDARD)
class ScatterChart(Chart):
def __init__(self):
super(ScatterChart, self).__init__(Chart.SCATTER_CHART, Chart.GROUPING_STANDARD)
+402 -402
View File
@@ -1,402 +1,402 @@
'''
Copyright (c) 2010 openpyxl
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@license: http://www.opensource.org/licenses/mit-license.php
@author: Eric Gazoni
'''
import math
from .style import Color
from .shared.units import pixels_to_EMU, EMU_to_pixels, short_color
class Shadow(object):
SHADOW_BOTTOM = 'b'
SHADOW_BOTTOM_LEFT = 'bl'
SHADOW_BOTTOM_RIGHT = 'br'
SHADOW_CENTER = 'ctr'
SHADOW_LEFT = 'l'
SHADOW_TOP = 't'
SHADOW_TOP_LEFT = 'tl'
SHADOW_TOP_RIGHT = 'tr'
def __init__(self):
self.visible = False
self.blurRadius = 6
self.distance = 2
self.direction = 0
self.alignment = self.SHADOW_BOTTOM_RIGHT
self.color = Color(Color.BLACK)
self.alpha = 50
class Drawing(object):
""" a drawing object - eg container for shapes or charts
we assume user specifies dimensions in pixels; units are
converted to EMU in the drawing part
"""
count = 0
def __init__(self):
self.name = ''
self.description = ''
self.coordinates = ((1,2), (16,8))
self.left = 0
self.top = 0
self._width = EMU_to_pixels(200000)
self._height = EMU_to_pixels(1828800)
self.resize_proportional = False
self.rotation = 0
# self.shadow = Shadow()
def _set_width(self, w):
if self.resize_proportional and w:
ratio = self._height / self._width
self._height = round(ratio * w)
self._width = w
def _get_width(self):
return self._width
width = property(_get_width, _set_width)
def _set_height(self, h):
if self.resize_proportional and h:
ratio = self._width / self._height
self._width = round(ratio * h)
self._height = h
def _get_height(self):
return self._height
height = property(_get_height, _set_height)
def set_dimension(self, w=0, h=0):
xratio = w / self._width
yratio = h / self._height
if self.resize_proportional and w and h:
if (xratio * self._height) < h:
self._height = math.ceil(xratio * self._height)
self._width = width
else:
self._width = math.ceil(yratio * self._width)
self._height = height
def get_emu_dimensions(self):
""" return (x, y, w, h) in EMU """
return (pixels_to_EMU(self.left), pixels_to_EMU(self.top),
pixels_to_EMU(self._width), pixels_to_EMU(self._height))
class Shape(object):
""" a drawing inside a chart
coordiantes are specified by the user in the axis units
"""
MARGIN_LEFT = 6 + 13 + 1
MARGIN_BOTTOM = 17 + 11
FONT_WIDTH = 7
FONT_HEIGHT = 8
ROUND_RECT = 'roundRect'
RECT = 'rect'
# other shapes to define :
'''
"line"
"lineInv"
"triangle"
"rtTriangle"
"diamond"
"parallelogram"
"trapezoid"
"nonIsoscelesTrapezoid"
"pentagon"
"hexagon"
"heptagon"
"octagon"
"decagon"
"dodecagon"
"star4"
"star5"
"star6"
"star7"
"star8"
"star10"
"star12"
"star16"
"star24"
"star32"
"roundRect"
"round1Rect"
"round2SameRect"
"round2DiagRect"
"snipRoundRect"
"snip1Rect"
"snip2SameRect"
"snip2DiagRect"
"plaque"
"ellipse"
"teardrop"
"homePlate"
"chevron"
"pieWedge"
"pie"
"blockArc"
"donut"
"noSmoking"
"rightArrow"
"leftArrow"
"upArrow"
"downArrow"
"stripedRightArrow"
"notchedRightArrow"
"bentUpArrow"
"leftRightArrow"
"upDownArrow"
"leftUpArrow"
"leftRightUpArrow"
"quadArrow"
"leftArrowCallout"
"rightArrowCallout"
"upArrowCallout"
"downArrowCallout"
"leftRightArrowCallout"
"upDownArrowCallout"
"quadArrowCallout"
"bentArrow"
"uturnArrow"
"circularArrow"
"leftCircularArrow"
"leftRightCircularArrow"
"curvedRightArrow"
"curvedLeftArrow"
"curvedUpArrow"
"curvedDownArrow"
"swooshArrow"
"cube"
"can"
"lightningBolt"
"heart"
"sun"
"moon"
"smileyFace"
"irregularSeal1"
"irregularSeal2"
"foldedCorner"
"bevel"
"frame"
"halfFrame"
"corner"
"diagStripe"
"chord"
"arc"
"leftBracket"
"rightBracket"
"leftBrace"
"rightBrace"
"bracketPair"
"bracePair"
"straightConnector1"
"bentConnector2"
"bentConnector3"
"bentConnector4"
"bentConnector5"
"curvedConnector2"
"curvedConnector3"
"curvedConnector4"
"curvedConnector5"
"callout1"
"callout2"
"callout3"
"accentCallout1"
"accentCallout2"
"accentCallout3"
"borderCallout1"
"borderCallout2"
"borderCallout3"
"accentBorderCallout1"
"accentBorderCallout2"
"accentBorderCallout3"
"wedgeRectCallout"
"wedgeRoundRectCallout"
"wedgeEllipseCallout"
"cloudCallout"
"cloud"
"ribbon"
"ribbon2"
"ellipseRibbon"
"ellipseRibbon2"
"leftRightRibbon"
"verticalScroll"
"horizontalScroll"
"wave"
"doubleWave"
"plus"
"flowChartProcess"
"flowChartDecision"
"flowChartInputOutput"
"flowChartPredefinedProcess"
"flowChartInternalStorage"
"flowChartDocument"
"flowChartMultidocument"
"flowChartTerminator"
"flowChartPreparation"
"flowChartManualInput"
"flowChartManualOperation"
"flowChartConnector"
"flowChartPunchedCard"
"flowChartPunchedTape"
"flowChartSummingJunction"
"flowChartOr"
"flowChartCollate"
"flowChartSort"
"flowChartExtract"
"flowChartMerge"
"flowChartOfflineStorage"
"flowChartOnlineStorage"
"flowChartMagneticTape"
"flowChartMagneticDisk"
"flowChartMagneticDrum"
"flowChartDisplay"
"flowChartDelay"
"flowChartAlternateProcess"
"flowChartOffpageConnector"
"actionButtonBlank"
"actionButtonHome"
"actionButtonHelp"
"actionButtonInformation"
"actionButtonForwardNext"
"actionButtonBackPrevious"
"actionButtonEnd"
"actionButtonBeginning"
"actionButtonReturn"
"actionButtonDocument"
"actionButtonSound"
"actionButtonMovie"
"gear6"
"gear9"
"funnel"
"mathPlus"
"mathMinus"
"mathMultiply"
"mathDivide"
"mathEqual"
"mathNotEqual"
"cornerTabs"
"squareTabs"
"plaqueTabs"
"chartX"
"chartStar"
"chartPlus"
'''
def __init__(self, coordinates=((0,0), (1,1)), text=None, scheme="accent1"):
self.coordinates = coordinates # in axis unit
self.text = text
self.scheme = scheme
self.style = Shape.RECT
self._border_width = 3175 # in EMU
self._border_color = Color.BLACK[2:] #"F3B3C5"
self._color = Color.WHITE[2:]
self._text_color = Color.BLACK[2:]
def _get_border_color(self):
return self._border_color
def _set_border_color(self, color):
self._border_color = short_color(color)
border_color = property(_get_border_color, _set_border_color)
def _get_color(self):
return self._color
def _set_color(self, color):
self._color = short_color(color)
color = property(_get_color, _set_color)
def _get_text_color(self):
return self._text_color
def _set_text_color(self, color):
self._text_color = short_color(color)
text_color = property(_get_text_color, _set_text_color)
def _get_border_width(self):
return EMU_to_pixels(self._border_width)
def _set_border_width(self, w):
self._border_width = pixels_to_EMU(w)
print(self._border_width)
border_width = property(_get_border_width, _set_border_width)
def get_coordinates(self):
""" return shape coordinates in percentages (left, top, right, bottom) """
(x1, y1), (x2, y2) = self.coordinates
drawing_width = pixels_to_EMU(self._chart.drawing.width)
drawing_height = pixels_to_EMU(self._chart.drawing.height)
plot_width = drawing_width * self._chart.width
plot_height = drawing_height * self._chart.height
margin_left = self._chart._get_margin_left() * drawing_width
xunit = plot_width / self._chart.get_x_units()
margin_top = self._chart._get_margin_top() * drawing_height
yunit = self._chart.get_y_units()
x_start = (margin_left + (float(x1) * xunit)) / drawing_width
y_start = (margin_top + plot_height - (float(y1) * yunit)) / drawing_height
x_end = (margin_left + (float(x2) * xunit)) / drawing_width
y_end = (margin_top + plot_height - (float(y2) * yunit)) / drawing_height
def _norm_pct(pct):
""" force shapes to appear by truncating too large sizes """
if pct>1: pct = 1
elif pct<0: pct = 0
return pct
# allow user to specify y's in whatever order
# excel expect y_end to be lower
if y_end < y_start:
y_end, y_start = y_start, y_end
return (_norm_pct(x_start), _norm_pct(y_start),
_norm_pct(x_end), _norm_pct(y_end))
'''
Copyright (c) 2010 openpyxl
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@license: http://www.opensource.org/licenses/mit-license.php
@author: Eric Gazoni
'''
import math
from .style import Color
from .shared.units import pixels_to_EMU, EMU_to_pixels, short_color
class Shadow(object):
SHADOW_BOTTOM = 'b'
SHADOW_BOTTOM_LEFT = 'bl'
SHADOW_BOTTOM_RIGHT = 'br'
SHADOW_CENTER = 'ctr'
SHADOW_LEFT = 'l'
SHADOW_TOP = 't'
SHADOW_TOP_LEFT = 'tl'
SHADOW_TOP_RIGHT = 'tr'
def __init__(self):
self.visible = False
self.blurRadius = 6
self.distance = 2
self.direction = 0
self.alignment = self.SHADOW_BOTTOM_RIGHT
self.color = Color(Color.BLACK)
self.alpha = 50
class Drawing(object):
""" a drawing object - eg container for shapes or charts
we assume user specifies dimensions in pixels; units are
converted to EMU in the drawing part
"""
count = 0
def __init__(self):
self.name = ''
self.description = ''
self.coordinates = ((1,2), (16,8))
self.left = 0
self.top = 0
self._width = EMU_to_pixels(200000)
self._height = EMU_to_pixels(1828800)
self.resize_proportional = False
self.rotation = 0
# self.shadow = Shadow()
def _set_width(self, w):
if self.resize_proportional and w:
ratio = self._height / self._width
self._height = round(ratio * w)
self._width = w
def _get_width(self):
return self._width
width = property(_get_width, _set_width)
def _set_height(self, h):
if self.resize_proportional and h:
ratio = self._width / self._height
self._width = round(ratio * h)
self._height = h
def _get_height(self):
return self._height
height = property(_get_height, _set_height)
def set_dimension(self, w=0, h=0):
xratio = w / self._width
yratio = h / self._height
if self.resize_proportional and w and h:
if (xratio * self._height) < h:
self._height = math.ceil(xratio * self._height)
self._width = width
else:
self._width = math.ceil(yratio * self._width)
self._height = height
def get_emu_dimensions(self):
""" return (x, y, w, h) in EMU """
return (pixels_to_EMU(self.left), pixels_to_EMU(self.top),
pixels_to_EMU(self._width), pixels_to_EMU(self._height))
class Shape(object):
""" a drawing inside a chart
coordiantes are specified by the user in the axis units
"""
MARGIN_LEFT = 6 + 13 + 1
MARGIN_BOTTOM = 17 + 11
FONT_WIDTH = 7
FONT_HEIGHT = 8
ROUND_RECT = 'roundRect'
RECT = 'rect'
# other shapes to define :
'''
"line"
"lineInv"
"triangle"
"rtTriangle"
"diamond"
"parallelogram"
"trapezoid"
"nonIsoscelesTrapezoid"
"pentagon"
"hexagon"
"heptagon"
"octagon"
"decagon"
"dodecagon"
"star4"
"star5"
"star6"
"star7"
"star8"
"star10"
"star12"
"star16"
"star24"
"star32"
"roundRect"
"round1Rect"
"round2SameRect"
"round2DiagRect"
"snipRoundRect"
"snip1Rect"
"snip2SameRect"
"snip2DiagRect"
"plaque"
"ellipse"
"teardrop"
"homePlate"
"chevron"
"pieWedge"
"pie"
"blockArc"
"donut"
"noSmoking"
"rightArrow"
"leftArrow"
"upArrow"
"downArrow"
"stripedRightArrow"
"notchedRightArrow"
"bentUpArrow"
"leftRightArrow"
"upDownArrow"
"leftUpArrow"
"leftRightUpArrow"
"quadArrow"
"leftArrowCallout"
"rightArrowCallout"
"upArrowCallout"
"downArrowCallout"
"leftRightArrowCallout"
"upDownArrowCallout"
"quadArrowCallout"
"bentArrow"
"uturnArrow"
"circularArrow"
"leftCircularArrow"
"leftRightCircularArrow"
"curvedRightArrow"
"curvedLeftArrow"
"curvedUpArrow"
"curvedDownArrow"
"swooshArrow"
"cube"
"can"
"lightningBolt"
"heart"
"sun"
"moon"
"smileyFace"
"irregularSeal1"
"irregularSeal2"
"foldedCorner"
"bevel"
"frame"
"halfFrame"
"corner"
"diagStripe"
"chord"
"arc"
"leftBracket"
"rightBracket"
"leftBrace"
"rightBrace"
"bracketPair"
"bracePair"
"straightConnector1"
"bentConnector2"
"bentConnector3"
"bentConnector4"
"bentConnector5"
"curvedConnector2"
"curvedConnector3"
"curvedConnector4"
"curvedConnector5"
"callout1"
"callout2"
"callout3"
"accentCallout1"
"accentCallout2"
"accentCallout3"
"borderCallout1"
"borderCallout2"
"borderCallout3"
"accentBorderCallout1"
"accentBorderCallout2"
"accentBorderCallout3"
"wedgeRectCallout"
"wedgeRoundRectCallout"
"wedgeEllipseCallout"
"cloudCallout"
"cloud"
"ribbon"
"ribbon2"
"ellipseRibbon"
"ellipseRibbon2"
"leftRightRibbon"
"verticalScroll"
"horizontalScroll"
"wave"
"doubleWave"
"plus"
"flowChartProcess"
"flowChartDecision"
"flowChartInputOutput"
"flowChartPredefinedProcess"
"flowChartInternalStorage"
"flowChartDocument"
"flowChartMultidocument"
"flowChartTerminator"
"flowChartPreparation"
"flowChartManualInput"
"flowChartManualOperation"
"flowChartConnector"
"flowChartPunchedCard"
"flowChartPunchedTape"
"flowChartSummingJunction"
"flowChartOr"
"flowChartCollate"
"flowChartSort"
"flowChartExtract"
"flowChartMerge"
"flowChartOfflineStorage"
"flowChartOnlineStorage"
"flowChartMagneticTape"
"flowChartMagneticDisk"
"flowChartMagneticDrum"
"flowChartDisplay"
"flowChartDelay"
"flowChartAlternateProcess"
"flowChartOffpageConnector"
"actionButtonBlank"
"actionButtonHome"
"actionButtonHelp"
"actionButtonInformation"
"actionButtonForwardNext"
"actionButtonBackPrevious"
"actionButtonEnd"
"actionButtonBeginning"
"actionButtonReturn"
"actionButtonDocument"
"actionButtonSound"
"actionButtonMovie"
"gear6"
"gear9"
"funnel"
"mathPlus"
"mathMinus"
"mathMultiply"
"mathDivide"
"mathEqual"
"mathNotEqual"
"cornerTabs"
"squareTabs"
"plaqueTabs"
"chartX"
"chartStar"
"chartPlus"
'''
def __init__(self, coordinates=((0,0), (1,1)), text=None, scheme="accent1"):
self.coordinates = coordinates # in axis unit
self.text = text
self.scheme = scheme
self.style = Shape.RECT
self._border_width = 3175 # in EMU
self._border_color = Color.BLACK[2:] #"F3B3C5"
self._color = Color.WHITE[2:]
self._text_color = Color.BLACK[2:]
def _get_border_color(self):
return self._border_color
def _set_border_color(self, color):
self._border_color = short_color(color)
border_color = property(_get_border_color, _set_border_color)
def _get_color(self):
return self._color
def _set_color(self, color):
self._color = short_color(color)
color = property(_get_color, _set_color)
def _get_text_color(self):
return self._text_color
def _set_text_color(self, color):
self._text_color = short_color(color)
text_color = property(_get_text_color, _set_text_color)
def _get_border_width(self):
return EMU_to_pixels(self._border_width)
def _set_border_width(self, w):
self._border_width = pixels_to_EMU(w)
print(self._border_width)
border_width = property(_get_border_width, _set_border_width)
def get_coordinates(self):
""" return shape coordinates in percentages (left, top, right, bottom) """
(x1, y1), (x2, y2) = self.coordinates
drawing_width = pixels_to_EMU(self._chart.drawing.width)
drawing_height = pixels_to_EMU(self._chart.drawing.height)
plot_width = drawing_width * self._chart.width
plot_height = drawing_height * self._chart.height
margin_left = self._chart._get_margin_left() * drawing_width
xunit = plot_width / self._chart.get_x_units()
margin_top = self._chart._get_margin_top() * drawing_height
yunit = self._chart.get_y_units()
x_start = (margin_left + (float(x1) * xunit)) / drawing_width
y_start = (margin_top + plot_height - (float(y1) * yunit)) / drawing_height
x_end = (margin_left + (float(x2) * xunit)) / drawing_width
y_end = (margin_top + plot_height - (float(y2) * yunit)) / drawing_height
def _norm_pct(pct):
""" force shapes to appear by truncating too large sizes """
if pct>1: pct = 1
elif pct<0: pct = 0
return pct
# allow user to specify y's in whatever order
# excel expect y_end to be lower
if y_end < y_start:
y_end, y_start = y_start, y_end
return (_norm_pct(x_start), _norm_pct(y_start),
_norm_pct(x_end), _norm_pct(y_end))
+5 -5
View File
@@ -26,8 +26,8 @@
"""Imports for the openpyxl.reader namespace."""
# package imports
from ..reader import excel
from ..reader import strings
from ..reader import style
from ..reader import workbook
from ..reader import worksheet
from . import excel
from . import strings
from . import style
from . import workbook
from . import worksheet
+7 -7
View File
@@ -33,12 +33,12 @@ from ..shared.exc import OpenModeError, InvalidFileException
from ..shared.ooxml import ARC_SHARED_STRINGS, ARC_CORE, ARC_APP, \
ARC_WORKBOOK, PACKAGE_WORKSHEETS, ARC_STYLE
from ..workbook import Workbook
from ..reader.strings import read_string_table
from ..reader.style import read_style_table
from ..reader.workbook import read_sheets_titles, read_named_ranges, \
from .strings import read_string_table
from .style import read_style_table
from .workbook import read_sheets_titles, read_named_ranges, \
read_properties_core, get_sheet_ids
from ..reader.worksheet import read_worksheet
from ..reader.iter_worksheet import unpack_worksheet
from .worksheet import read_worksheet
from .iter_worksheet import unpack_worksheet
def load_workbook(filename, use_iterators = False):
"""Open the given filename and return the workbook
@@ -49,11 +49,11 @@ def load_workbook(filename, use_iterators = False):
:param use_iterators: use lazy load for cells
:type use_iterators: bool
:rtype: :class:`openpyxl.workbook.Workbook`
:rtype: :class:`..workbook.Workbook`
.. note::
When using lazy load, all worksheets will be :class:`openpyxl.reader.iter_worksheet.IterableWorksheet`
When using lazy load, all worksheets will be :class:`.iter_worksheet.IterableWorksheet`
and the returned workbook will be read-only.
"""
+343 -343
View File
@@ -1,343 +1,343 @@
# file openpyxl/reader/iter_worksheet.py
# Copyright (c) 2010 openpyxl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
""" Iterators-based worksheet reader
*Still very raw*
"""
from io import BytesIO as StringIO
import warnings
import operator
from functools import partial
from itertools import groupby
from ..worksheet import Worksheet
from ..cell import coordinate_from_string, get_column_letter, Cell
from ..reader.excel import get_sheet_ids
from ..reader.strings import read_string_table
from ..reader.style import read_style_table, NumberFormat
from ..shared.date_time import SharedDate
from ..reader.worksheet import read_dimension
from ..shared.ooxml import (MIN_COLUMN, MAX_COLUMN, PACKAGE_WORKSHEETS,
MAX_ROW, MIN_ROW, ARC_SHARED_STRINGS, ARC_APP, ARC_STYLE)
from xml.etree.cElementTree import iterparse
from zipfile import ZipFile
from .. import cell
import re
import tempfile
import zlib
import zipfile
import struct
TYPE_NULL = Cell.TYPE_NULL
MISSING_VALUE = None
RE_COORDINATE = re.compile('^([A-Z]+)([0-9]+)$')
SHARED_DATE = SharedDate()
_COL_CONVERSION_CACHE = dict((get_column_letter(i), i) for i in range(1, 18279))
def column_index_from_string(str_col, _col_conversion_cache=_COL_CONVERSION_CACHE):
# we use a function argument to get indexed name lookup
return _col_conversion_cache[str_col]
del _COL_CONVERSION_CACHE
RAW_ATTRIBUTES = ['row', 'column', 'coordinate', 'internal_value', 'data_type', 'style_id', 'number_format']
try:
from collections import namedtuple
BaseRawCell = namedtuple('RawCell', RAW_ATTRIBUTES)
except ImportError:
warnings.warn("""Unable to import 'namedtuple' module, this may cause memory issues when using optimized reader. Please upgrade your Python installation to 2.6+""")
class BaseRawCell(object):
def __init__(self, *args):
assert len(args)==len(RAW_ATTRIBUTES)
for attr, val in zip(RAW_ATTRIBUTES, args):
setattr(self, attr, val)
def _replace(self, **kwargs):
self.__dict__.update(kwargs)
return self
class RawCell(BaseRawCell):
"""Optimized version of the :class:`openpyxl.cell.Cell`, using named tuples.
Useful attributes are:
* row
* column
* coordinate
* internal_value
You can also access if needed:
* data_type
* number_format
"""
@property
def is_date(self):
res = (self.data_type == Cell.TYPE_NUMERIC
and self.number_format is not None
and ('d' in self.number_format
or 'm' in self.number_format
or 'y' in self.number_format
or 'h' in self.number_format
or 's' in self.number_format
))
return res
def iter_rows(workbook_name, sheet_name, xml_source, range_string = '', row_offset = 0, column_offset = 0):
archive = get_archive_file(workbook_name)
source = xml_source
if range_string:
min_col, min_row, max_col, max_row = get_range_boundaries(range_string, row_offset, column_offset)
else:
min_col, min_row, max_col, max_row = read_dimension(xml_source = source)
min_col = column_index_from_string(min_col)
max_col = column_index_from_string(max_col) + 1
max_row += 6
try:
string_table = read_string_table(archive.read(ARC_SHARED_STRINGS))
except KeyError:
string_table = {}
style_table = read_style_table(archive.read(ARC_STYLE))
source.seek(0)
p = iterparse(source)
return get_squared_range(p, min_col, min_row, max_col, max_row, string_table, style_table)
def get_rows(p, min_column = MIN_COLUMN, min_row = MIN_ROW, max_column = MAX_COLUMN, max_row = MAX_ROW):
return groupby(get_cells(p, min_row, min_column, max_row, max_column), operator.attrgetter('row'))
def get_cells(p, min_row, min_col, max_row, max_col, _re_coordinate=RE_COORDINATE):
for _event, element in p:
if element.tag == '{http://schemas.openxmlformats.org/spreadsheetml/2006/main}c':
coord = element.get('r')
column_str, row = _re_coordinate.match(coord).groups()
row = int(row)
column = column_index_from_string(column_str)
if min_col <= column <= max_col and min_row <= row <= max_row:
data_type = element.get('t', 'n')
style_id = element.get('s')
value = element.findtext('{http://schemas.openxmlformats.org/spreadsheetml/2006/main}v')
yield RawCell(row, column_str, coord, value, data_type, style_id, None)
if element.tag == '{http://schemas.openxmlformats.org/spreadsheetml/2006/main}v':
continue
element.clear()
def get_range_boundaries(range_string, row = 0, column = 0):
if ':' in range_string:
min_range, max_range = range_string.split(':')
min_col, min_row = coordinate_from_string(min_range)
max_col, max_row = coordinate_from_string(max_range)
min_col = column_index_from_string(min_col) + column
max_col = column_index_from_string(max_col) + column
min_row += row
max_row += row
else:
min_col, min_row = coordinate_from_string(range_string)
min_col = column_index_from_string(min_col)
max_col = min_col + 1
max_row = min_row
return (min_col, min_row, max_col, max_row)
def get_archive_file(archive_name):
return ZipFile(archive_name, 'r')
def get_xml_source(archive_file, sheet_name):
return archive_file.read('%s/%s' % (PACKAGE_WORKSHEETS, sheet_name))
def get_missing_cells(row, columns):
return dict([(column, RawCell(row, column, '%s%s' % (column, row), MISSING_VALUE, TYPE_NULL, None, None)) for column in columns])
def get_squared_range(p, min_col, min_row, max_col, max_row, string_table, style_table):
expected_columns = [get_column_letter(ci) for ci in range(min_col, max_col)]
current_row = min_row
for row, cells in get_rows(p, min_row = min_row, max_row = max_row, min_column = min_col, max_column = max_col):
full_row = []
if current_row < row:
for gap_row in range(current_row, row):
dummy_cells = get_missing_cells(gap_row, expected_columns)
yield tuple([dummy_cells[column] for column in expected_columns])
current_row = row
temp_cells = list(cells)
retrieved_columns = dict([(c.column, c) for c in temp_cells])
missing_columns = list(set(expected_columns) - set(retrieved_columns.keys()))
replacement_columns = get_missing_cells(row, missing_columns)
for column in expected_columns:
if column in retrieved_columns:
cell = retrieved_columns[column]
if cell.style_id is not None:
style = style_table[int(cell.style_id)]
cell = cell._replace(number_format = style.number_format.format_code) #pylint: disable-msg=W0212
if cell.internal_value is not None:
if cell.data_type == Cell.TYPE_STRING:
cell = cell._replace(internal_value = string_table[int(cell.internal_value)]) #pylint: disable-msg=W0212
elif cell.data_type == Cell.TYPE_BOOL:
cell = cell._replace(internal_value = cell.internal_value == 'True')
elif cell.is_date:
cell = cell._replace(internal_value = SHARED_DATE.from_julian(float(cell.internal_value)))
elif cell.data_type == Cell.TYPE_NUMERIC:
cell = cell._replace(internal_value = float(cell.internal_value))
full_row.append(cell)
else:
full_row.append(replacement_columns[column])
current_row = row + 1
yield tuple(full_row)
#------------------------------------------------------------------------------
class IterableWorksheet(Worksheet):
def __init__(self, parent_workbook, title, workbook_name,
sheet_codename, xml_source):
Worksheet.__init__(self, parent_workbook, title)
self._workbook_name = workbook_name
self._sheet_codename = sheet_codename
self._xml_source = xml_source
def iter_rows(self, range_string = '', row_offset = 0, column_offset = 0):
""" Returns a squared range based on the `range_string` parameter,
using generators.
:param range_string: range of cells (e.g. 'A1:C4')
:type range_string: string
:param row: row index of the cell (e.g. 4)
:type row: int
:param column: column index of the cell (e.g. 3)
:type column: int
:rtype: generator
"""
return iter_rows(workbook_name = self._workbook_name,
sheet_name = self._sheet_codename,
xml_source = self._xml_source,
range_string = range_string,
row_offset = row_offset,
column_offset = column_offset)
def cell(self, *args, **kwargs):
raise NotImplementedError("use 'iter_rows()' instead")
def range(self, *args, **kwargs):
raise NotImplementedError("use 'iter_rows()' instead")
def unpack_worksheet(archive, filename):
temp_file = tempfile.TemporaryFile(mode='r+', prefix='openpyxl.', suffix='.unpack.temp')
zinfo = archive.getinfo(filename)
if zinfo.compress_type == zipfile.ZIP_STORED:
decoder = None
elif zinfo.compress_type == zipfile.ZIP_DEFLATED:
decoder = zlib.decompressobj(-zlib.MAX_WBITS)
else:
raise zipfile.BadZipFile("Unrecognized compression method")
archive.fp.seek(_get_file_offset(archive, zinfo))
bytes_to_read = zinfo.compress_size
while True:
buff = archive.fp.read(min(bytes_to_read, 102400))
if not buff:
break
bytes_to_read -= len(buff)
if decoder:
buff = decoder.decompress(buff)
temp_file.write(buff)
if decoder:
temp_file.write(decoder.decompress('Z'))
return temp_file
def _get_file_offset(archive, zinfo):
try:
return zinfo.file_offset
except AttributeError:
# From http://stackoverflow.com/questions/3781261/how-to-simulate-zipfile-open-in-python-2-5
# Seek over the fixed size fields to the "file name length" field in
# the file header (26 bytes). Unpack this and the "extra field length"
# field ourselves as info.extra doesn't seem to be the correct length.
archive.fp.seek(zinfo.header_offset + 26)
file_name_len, extra_len = struct.unpack("<HH", archive.fp.read(4))
return zinfo.header_offset + 30 + file_name_len + extra_len
# file openpyxl/reader/iter_worksheet.py
# Copyright (c) 2010 openpyxl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
""" Iterators-based worksheet reader
*Still very raw*
"""
from io import StringIO
import warnings
import operator
from functools import partial
from itertools import groupby
from ..worksheet import Worksheet
from ..cell import coordinate_from_string, get_column_letter, Cell
from .excel import get_sheet_ids
from .strings import read_string_table
from .style import read_style_table, NumberFormat
from ..shared.date_time import SharedDate
from .worksheet import read_dimension
from ..shared.ooxml import (MIN_COLUMN, MAX_COLUMN, PACKAGE_WORKSHEETS,
MAX_ROW, MIN_ROW, ARC_SHARED_STRINGS, ARC_APP, ARC_STYLE)
from xml.etree.cElementTree import iterparse
from zipfile import ZipFile
from .. import cell
import re
import tempfile
import zlib
import zipfile
import struct
TYPE_NULL = Cell.TYPE_NULL
MISSING_VALUE = None
RE_COORDINATE = re.compile('^([A-Z]+)([0-9]+)$')
SHARED_DATE = SharedDate()
_COL_CONVERSION_CACHE = dict((get_column_letter(i), i) for i in range(1, 18279))
def column_index_from_string(str_col, _col_conversion_cache=_COL_CONVERSION_CACHE):
# we use a function argument to get indexed name lookup
return _col_conversion_cache[str_col]
del _COL_CONVERSION_CACHE
RAW_ATTRIBUTES = ['row', 'column', 'coordinate', 'internal_value', 'data_type', 'style_id', 'number_format']
try:
from collections import namedtuple
BaseRawCell = namedtuple('RawCell', RAW_ATTRIBUTES)
except ImportError:
warnings.warn("""Unable to import 'namedtuple' module, this may cause memory issues when using optimized reader. Please upgrade your Python installation to 2.6+""")
class BaseRawCell(object):
def __init__(self, *args):
assert len(args)==len(RAW_ATTRIBUTES)
for attr, val in zip(RAW_ATTRIBUTES, args):
setattr(self, attr, val)
def _replace(self, **kwargs):
self.__dict__.update(kwargs)
return self
class RawCell(BaseRawCell):
"""Optimized version of the :class:`..cell.Cell`, using named tuples.
Useful attributes are:
* row
* column
* coordinate
* internal_value
You can also access if needed:
* data_type
* number_format
"""
@property
def is_date(self):
res = (self.data_type == Cell.TYPE_NUMERIC
and self.number_format is not None
and ('d' in self.number_format
or 'm' in self.number_format
or 'y' in self.number_format
or 'h' in self.number_format
or 's' in self.number_format
))
return res
def iter_rows(workbook_name, sheet_name, xml_source, range_string = '', row_offset = 0, column_offset = 0):
archive = get_archive_file(workbook_name)
source = xml_source
if range_string:
min_col, min_row, max_col, max_row = get_range_boundaries(range_string, row_offset, column_offset)
else:
min_col, min_row, max_col, max_row = read_dimension(xml_source = source)
min_col = column_index_from_string(min_col)
max_col = column_index_from_string(max_col) + 1
max_row += 6
try:
string_table = read_string_table(archive.read(ARC_SHARED_STRINGS))
except KeyError:
string_table = {}
style_table = read_style_table(archive.read(ARC_STYLE))
source.seek(0)
p = iterparse(source)
return get_squared_range(p, min_col, min_row, max_col, max_row, string_table, style_table)
def get_rows(p, min_column = MIN_COLUMN, min_row = MIN_ROW, max_column = MAX_COLUMN, max_row = MAX_ROW):
return groupby(get_cells(p, min_row, min_column, max_row, max_column), operator.attrgetter('row'))
def get_cells(p, min_row, min_col, max_row, max_col, _re_coordinate=RE_COORDINATE):
for _event, element in p:
if element.tag == '{http://schemas.openxmlformats.org/spreadsheetml/2006/main}c':
coord = element.get('r')
column_str, row = _re_coordinate.match(coord).groups()
row = int(row)
column = column_index_from_string(column_str)
if min_col <= column <= max_col and min_row <= row <= max_row:
data_type = element.get('t', 'n')
style_id = element.get('s')
value = element.findtext('{http://schemas.openxmlformats.org/spreadsheetml/2006/main}v')
yield RawCell(row, column_str, coord, value, data_type, style_id, None)
if element.tag == '{http://schemas.openxmlformats.org/spreadsheetml/2006/main}v':
continue
element.clear()
def get_range_boundaries(range_string, row = 0, column = 0):
if ':' in range_string:
min_range, max_range = range_string.split(':')
min_col, min_row = coordinate_from_string(min_range)
max_col, max_row = coordinate_from_string(max_range)
min_col = column_index_from_string(min_col) + column
max_col = column_index_from_string(max_col) + column
min_row += row
max_row += row
else:
min_col, min_row = coordinate_from_string(range_string)
min_col = column_index_from_string(min_col)
max_col = min_col + 1
max_row = min_row
return (min_col, min_row, max_col, max_row)
def get_archive_file(archive_name):
return ZipFile(archive_name, 'r')
def get_xml_source(archive_file, sheet_name):
return archive_file.read('%s/%s' % (PACKAGE_WORKSHEETS, sheet_name))
def get_missing_cells(row, columns):
return dict([(column, RawCell(row, column, '%s%s' % (column, row), MISSING_VALUE, TYPE_NULL, None, None)) for column in columns])
def get_squared_range(p, min_col, min_row, max_col, max_row, string_table, style_table):
expected_columns = [get_column_letter(ci) for ci in range(min_col, max_col)]
current_row = min_row
for row, cells in get_rows(p, min_row = min_row, max_row = max_row, min_column = min_col, max_column = max_col):
full_row = []
if current_row < row:
for gap_row in range(current_row, row):
dummy_cells = get_missing_cells(gap_row, expected_columns)
yield tuple([dummy_cells[column] for column in expected_columns])
current_row = row
temp_cells = list(cells)
retrieved_columns = dict([(c.column, c) for c in temp_cells])
missing_columns = list(set(expected_columns) - set(retrieved_columns.keys()))
replacement_columns = get_missing_cells(row, missing_columns)
for column in expected_columns:
if column in retrieved_columns:
cell = retrieved_columns[column]
if cell.style_id is not None:
style = style_table[int(cell.style_id)]
cell = cell._replace(number_format = style.number_format.format_code) #pylint: disable-msg=W0212
if cell.internal_value is not None:
if cell.data_type == Cell.TYPE_STRING:
cell = cell._replace(internal_value = string_table[int(cell.internal_value)]) #pylint: disable-msg=W0212
elif cell.data_type == Cell.TYPE_BOOL:
cell = cell._replace(internal_value = cell.internal_value == 'True')
elif cell.is_date:
cell = cell._replace(internal_value = SHARED_DATE.from_julian(float(cell.internal_value)))
elif cell.data_type == Cell.TYPE_NUMERIC:
cell = cell._replace(internal_value = float(cell.internal_value))
full_row.append(cell)
else:
full_row.append(replacement_columns[column])
current_row = row + 1
yield tuple(full_row)
#------------------------------------------------------------------------------
class IterableWorksheet(Worksheet):
def __init__(self, parent_workbook, title, workbook_name,
sheet_codename, xml_source):
Worksheet.__init__(self, parent_workbook, title)
self._workbook_name = workbook_name
self._sheet_codename = sheet_codename
self._xml_source = xml_source
def iter_rows(self, range_string = '', row_offset = 0, column_offset = 0):
""" Returns a squared range based on the `range_string` parameter,
using generators.
:param range_string: range of cells (e.g. 'A1:C4')
:type range_string: string
:param row: row index of the cell (e.g. 4)
:type row: int
:param column: column index of the cell (e.g. 3)
:type column: int
:rtype: generator
"""
return iter_rows(workbook_name = self._workbook_name,
sheet_name = self._sheet_codename,
xml_source = self._xml_source,
range_string = range_string,
row_offset = row_offset,
column_offset = column_offset)
def cell(self, *args, **kwargs):
raise NotImplementedError("use 'iter_rows()' instead")
def range(self, *args, **kwargs):
raise NotImplementedError("use 'iter_rows()' instead")
def unpack_worksheet(archive, filename):
temp_file = tempfile.TemporaryFile(mode='r+', prefix='openpyxl.', suffix='.unpack.temp')
zinfo = archive.getinfo(filename)
if zinfo.compress_type == zipfile.ZIP_STORED:
decoder = None
elif zinfo.compress_type == zipfile.ZIP_DEFLATED:
decoder = zlib.decompressobj(-zlib.MAX_WBITS)
else:
raise zipfile.BadZipFile("Unrecognized compression method")
archive.fp.seek(_get_file_offset(archive, zinfo))
bytes_to_read = zinfo.compress_size
while True:
buff = archive.fp.read(min(bytes_to_read, 102400))
if not buff:
break
bytes_to_read -= len(buff)
if decoder:
buff = decoder.decompress(buff)
temp_file.write(buff)
if decoder:
temp_file.write(decoder.decompress('Z'))
return temp_file
def _get_file_offset(archive, zinfo):
try:
return zinfo.file_offset
except AttributeError:
# From http://stackoverflow.com/questions/3781261/how-to-simulate-zipfile-open-in-python-2-5
# Seek over the fixed size fields to the "file name length" field in
# the file header (26 bytes). Unpack this and the "extra field length"
# field ourselves as info.extra doesn't seem to be the correct length.
archive.fp.seek(zinfo.header_offset + 26)
file_name_len, extra_len = struct.unpack("<HH", archive.fp.read(4))
return zinfo.header_offset + 30 + file_name_len + extra_len
@@ -30,7 +30,8 @@ try:
from xml.etree.cElementTree import iterparse
except ImportError:
from xml.etree.ElementTree import iterparse
from io import BytesIO as StringIO
from io import StringIO
# package imports
from ..cell import Cell, coordinate_from_string
@@ -54,8 +55,12 @@ def read_dimension(xml_source):
if element.tag == '{http://schemas.openxmlformats.org/spreadsheetml/2006/main}dimension':
ref = element.get('ref')
if ':' in ref:
min_range, max_range = ref.split(':')
else:
min_range = max_range = ref
min_range, max_range = ref.split(':')
min_col, min_row = coordinate_from_string(min_range)
max_col, max_row = coordinate_from_string(max_range)
@@ -66,9 +71,9 @@ def read_dimension(xml_source):
return None
def filter_cells(pair):
(event, element) = pair
def filter_cells(xxx_todo_changeme):
(event, element) = xxx_todo_changeme
return element.tag == '{http://schemas.openxmlformats.org/spreadsheetml/2006/main}c'
def fast_parse(ws, xml_source, string_table, style_table):
+6 -6
View File
@@ -23,11 +23,11 @@
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
"""Imports for the openpyxl.shared namespace."""
"""Imports for the . namespace."""
# package imports
from ..shared import date_time
from ..shared import exc
from ..shared import ooxml
from ..shared import password_hasher
from ..shared import xmltools
from . import date_time
from . import exc
from . import ooxml
from . import password_hasher
from . import xmltools
@@ -26,7 +26,7 @@
"""Manage Excel date weirdness."""
# Python stdlib imports
from __future__ import division
from math import floor
import calendar
import datetime
@@ -48,7 +48,7 @@ def datetime_to_W3CDTF(dt):
def W3CDTF_to_datetime(formatted_string):
"""Convert from a timestamp string to a datetime object."""
match = re.match(RE_W3CDTF,formatted_string)
digits = map(int, match.groups()[:6])
digits = list(map(int, match.groups()[:6]))
return datetime.datetime(*digits)
+1 -1
View File
@@ -340,7 +340,7 @@ class NumberFormat(HashableObject):
"""Check if a format code is a standard format code."""
if format is None:
format = self._format_code
return format in self._BUILTIN_FORMATS.values()
return format in list(self._BUILTIN_FORMATS.values())
def builtin_format_id(self, format):
"""Return the id of a standard style."""
+2 -2
View File
@@ -24,12 +24,12 @@
# @author: Eric Gazoni
# Python stdlib imports
from __future__ import with_statement
import os
import os.path
import shutil
import difflib
from io import BytesIO as StringIO
from io import StringIO
from pprint import pprint
from tempfile import gettempdir
+1 -1
View File
@@ -134,7 +134,7 @@ class TestCellValueTypes():
def check_error():
eq_(self.cell.TYPE_ERROR, self.cell.data_type)
for error_string in self.cell.ERROR_CODES.keys():
for error_string in list(self.cell.ERROR_CODES.keys()):
self.cell.value = error_string
yield check_error
+131 -131
View File
@@ -1,131 +1,131 @@
# file openpyxl/tests/test_chart.py
# Copyright (c) 2010 openpyxl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
from nose.tools import eq_
from openpyxl.tests.helper import get_xml
from openpyxl.shared.xmltools import Element
from openpyxl.writer.charts import ChartWriter
from openpyxl.workbook import Workbook
from openpyxl.chart import BarChart, ScatterChart, Serie, Reference
from openpyxl.style import Color
class TestChartWriter(object):
def setUp(self):
wb = Workbook()
ws = wb.get_active_sheet()
ws.title = 'data'
for i in range(10):
ws.cell(row = i, column = 0).value = i
self.chart = BarChart()
self.chart.title = 'TITLE'
self.chart.add_serie(Serie(Reference(ws, (0, 0), (10, 0))))
self.chart._series[-1].color = Color.GREEN
self.cw = ChartWriter(self.chart)
self.root = Element('test')
def test_write_title(self):
self.cw._write_title(self.root)
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:title><c:tx><c:rich><a:bodyPr /><a:lstStyle /><a:p><a:pPr><a:defRPr /></a:pPr><a:r><a:rPr lang="fr-FR" /><a:t>TITLE</a:t></a:r></a:p></c:rich></c:tx><c:layout /></c:title></test>')
def test_write_xaxis(self):
self.cw._write_axis(self.root, self.chart.x_axis, 'c:catAx')
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:catAx><c:axId val="60871424" /><c:scaling><c:orientation val="minMax" /></c:scaling><c:axPos val="b" /><c:tickLblPos val="nextTo" /><c:crossAx val="60873344" /><c:crosses val="autoZero" /><c:auto val="1" /><c:lblAlgn val="ctr" /><c:lblOffset val="100" /></c:catAx></test>')
def test_write_yaxis(self):
self.cw._write_axis(self.root, self.chart.y_axis, 'c:valAx')
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:valAx><c:axId val="60873344" /><c:scaling><c:orientation val="minMax" /><c:max val="10.0" /><c:min val="0" /></c:scaling><c:axPos val="l" /><c:majorGridlines /><c:numFmt formatCode="General" sourceLinked="1" /><c:tickLblPos val="nextTo" /><c:crossAx val="60871424" /><c:crosses val="autoZero" /><c:crossBetween val="between" /><c:majorUnit val="2.0" /></c:valAx></test>')
def test_write_series(self):
self.cw._write_series(self.root)
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:ser><c:idx val="0" /><c:order val="0" /><c:spPr><a:solidFill><a:srgbClr val="00FF00" /></a:solidFill><a:ln><a:solidFill><a:srgbClr val="00FF00" /></a:solidFill></a:ln></c:spPr><c:marker><c:symbol val="none" /></c:marker><c:val><c:numRef><c:f>data!$A$1:$A$11</c:f><c:numCache><c:formatCode>General</c:formatCode><c:ptCount val="11" /><c:pt idx="0"><c:v>0</c:v></c:pt><c:pt idx="1"><c:v>1</c:v></c:pt><c:pt idx="2"><c:v>2</c:v></c:pt><c:pt idx="3"><c:v>3</c:v></c:pt><c:pt idx="4"><c:v>4</c:v></c:pt><c:pt idx="5"><c:v>5</c:v></c:pt><c:pt idx="6"><c:v>6</c:v></c:pt><c:pt idx="7"><c:v>7</c:v></c:pt><c:pt idx="8"><c:v>8</c:v></c:pt><c:pt idx="9"><c:v>9</c:v></c:pt><c:pt idx="10"><c:v>None</c:v></c:pt></c:numCache></c:numRef></c:val></c:ser></test>')
def test_write_legend(self):
self.cw._write_legend(self.root)
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:legend><c:legendPos val="r" /><c:layout /></c:legend></test>')
def test_write_print_settings(self):
self.cw._write_print_settings(self.root)
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:printSettings><c:headerFooter /><c:pageMargins b="0.75" footer="0.3" header="0.3" l="0.7" r="0.7" t="0.75" /><c:pageSetup /></c:printSettings></test>')
def test_write_chart(self):
self.cw._write_chart(self.root)
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:chart><c:title><c:tx><c:rich><a:bodyPr /><a:lstStyle /><a:p><a:pPr><a:defRPr /></a:pPr><a:r><a:rPr lang="fr-FR" /><a:t>TITLE</a:t></a:r></a:p></c:rich></c:tx><c:layout /></c:title><c:plotArea><c:layout><c:manualLayout><c:layoutTarget val="inner" /><c:xMode val="edge" /><c:yMode val="edge" /><c:x val="1.28571428571" /><c:y val="0.2125" /><c:w val="0.6" /><c:h val="0.6" /></c:manualLayout></c:layout><c:barChart><c:barDir val="col" /><c:grouping val="clustered" /><c:ser><c:idx val="0" /><c:order val="0" /><c:spPr><a:solidFill><a:srgbClr val="00FF00" /></a:solidFill><a:ln><a:solidFill><a:srgbClr val="00FF00" /></a:solidFill></a:ln></c:spPr><c:marker><c:symbol val="none" /></c:marker><c:val><c:numRef><c:f>data!$A$1:$A$11</c:f><c:numCache><c:formatCode>General</c:formatCode><c:ptCount val="11" /><c:pt idx="0"><c:v>0</c:v></c:pt><c:pt idx="1"><c:v>1</c:v></c:pt><c:pt idx="2"><c:v>2</c:v></c:pt><c:pt idx="3"><c:v>3</c:v></c:pt><c:pt idx="4"><c:v>4</c:v></c:pt><c:pt idx="5"><c:v>5</c:v></c:pt><c:pt idx="6"><c:v>6</c:v></c:pt><c:pt idx="7"><c:v>7</c:v></c:pt><c:pt idx="8"><c:v>8</c:v></c:pt><c:pt idx="9"><c:v>9</c:v></c:pt><c:pt idx="10"><c:v>None</c:v></c:pt></c:numCache></c:numRef></c:val></c:ser><c:marker val="1" /><c:axId val="60871424" /><c:axId val="60873344" /></c:barChart><c:catAx><c:axId val="60871424" /><c:scaling><c:orientation val="minMax" /></c:scaling><c:axPos val="b" /><c:tickLblPos val="nextTo" /><c:crossAx val="60873344" /><c:crosses val="autoZero" /><c:auto val="1" /><c:lblAlgn val="ctr" /><c:lblOffset val="100" /></c:catAx><c:valAx><c:axId val="60873344" /><c:scaling><c:orientation val="minMax" /><c:max val="10.0" /><c:min val="0" /></c:scaling><c:axPos val="l" /><c:majorGridlines /><c:numFmt formatCode="General" sourceLinked="1" /><c:tickLblPos val="nextTo" /><c:crossAx val="60871424" /><c:crosses val="autoZero" /><c:crossBetween val="between" /><c:majorUnit val="2.0" /></c:valAx></c:plotArea><c:legend><c:legendPos val="r" /><c:layout /></c:legend><c:plotVisOnly val="1" /></c:chart></test>')
class TestScatterChartWriter(object):
def setUp(self):
wb = Workbook()
ws = wb.get_active_sheet()
ws.title = 'data'
for i in range(10):
ws.cell(row = i, column = 0).value = i
ws.cell(row = i, column = 1).value = i
self.scatterchart = ScatterChart()
self.scatterchart.add_serie(Serie(Reference(ws, (0, 0), (10, 0)),
xvalues = Reference(ws, (0, 1), (10, 1))))
self.cw = ChartWriter(self.scatterchart)
self.root = Element('test')
def test_write_xaxis(self):
self.cw._write_axis(self.root, self.scatterchart.x_axis, 'c:valAx')
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:valAx><c:axId val="60871424" /><c:scaling><c:orientation val="minMax" /><c:max val="10.0" /><c:min val="0" /></c:scaling><c:axPos val="b" /><c:majorGridlines /><c:numFmt formatCode="General" sourceLinked="1" /><c:tickLblPos val="nextTo" /><c:crossAx val="60873344" /><c:crosses val="autoZero" /><c:auto val="1" /><c:lblAlgn val="ctr" /><c:lblOffset val="100" /><c:crossBetween val="midCat" /><c:majorUnit val="2.0" /></c:valAx></test>')
def test_write_yaxis(self):
self.cw._write_axis(self.root, self.scatterchart.y_axis, 'c:valAx')
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:valAx><c:axId val="60873344" /><c:scaling><c:orientation val="minMax" /><c:max val="10.0" /><c:min val="0" /></c:scaling><c:axPos val="l" /><c:majorGridlines /><c:numFmt formatCode="General" sourceLinked="1" /><c:tickLblPos val="nextTo" /><c:crossAx val="60871424" /><c:crosses val="autoZero" /><c:crossBetween val="midCat" /><c:majorUnit val="2.0" /></c:valAx></test>')
def test_write_series(self):
self.cw._write_series(self.root)
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:ser><c:idx val="0" /><c:order val="0" /><c:marker><c:symbol val="none" /></c:marker><c:xVal><c:numRef><c:f>data!$B$1:$B$11</c:f><c:numCache><c:formatCode>General</c:formatCode><c:ptCount val="11" /><c:pt idx="0"><c:v>0</c:v></c:pt><c:pt idx="1"><c:v>1</c:v></c:pt><c:pt idx="2"><c:v>2</c:v></c:pt><c:pt idx="3"><c:v>3</c:v></c:pt><c:pt idx="4"><c:v>4</c:v></c:pt><c:pt idx="5"><c:v>5</c:v></c:pt><c:pt idx="6"><c:v>6</c:v></c:pt><c:pt idx="7"><c:v>7</c:v></c:pt><c:pt idx="8"><c:v>8</c:v></c:pt><c:pt idx="9"><c:v>9</c:v></c:pt><c:pt idx="10"><c:v>None</c:v></c:pt></c:numCache></c:numRef></c:xVal><c:yVal><c:numRef><c:f>data!$A$1:$A$11</c:f><c:numCache><c:formatCode>General</c:formatCode><c:ptCount val="11" /><c:pt idx="0"><c:v>0</c:v></c:pt><c:pt idx="1"><c:v>1</c:v></c:pt><c:pt idx="2"><c:v>2</c:v></c:pt><c:pt idx="3"><c:v>3</c:v></c:pt><c:pt idx="4"><c:v>4</c:v></c:pt><c:pt idx="5"><c:v>5</c:v></c:pt><c:pt idx="6"><c:v>6</c:v></c:pt><c:pt idx="7"><c:v>7</c:v></c:pt><c:pt idx="8"><c:v>8</c:v></c:pt><c:pt idx="9"><c:v>9</c:v></c:pt><c:pt idx="10"><c:v>None</c:v></c:pt></c:numCache></c:numRef></c:yVal></c:ser></test>')
def test_write_legend(self):
self.cw._write_legend(self.root)
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:legend><c:legendPos val="r" /><c:layout /></c:legend></test>')
def test_write_print_settings(self):
self.cw._write_print_settings(self.root)
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:printSettings><c:headerFooter /><c:pageMargins b="0.75" footer="0.3" header="0.3" l="0.7" r="0.7" t="0.75" /><c:pageSetup /></c:printSettings></test>')
def test_write_chart(self):
self.cw._write_chart(self.root)
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:chart><c:plotArea><c:layout><c:manualLayout><c:layoutTarget val="inner" /><c:xMode val="edge" /><c:yMode val="edge" /><c:x val="1.28571428571" /><c:y val="0.2125" /><c:w val="0.6" /><c:h val="0.6" /></c:manualLayout></c:layout><c:scatterChart><c:scatterStyle val="lineMarker" /><c:ser><c:idx val="0" /><c:order val="0" /><c:marker><c:symbol val="none" /></c:marker><c:xVal><c:numRef><c:f>data!$B$1:$B$11</c:f><c:numCache><c:formatCode>General</c:formatCode><c:ptCount val="11" /><c:pt idx="0"><c:v>0</c:v></c:pt><c:pt idx="1"><c:v>1</c:v></c:pt><c:pt idx="2"><c:v>2</c:v></c:pt><c:pt idx="3"><c:v>3</c:v></c:pt><c:pt idx="4"><c:v>4</c:v></c:pt><c:pt idx="5"><c:v>5</c:v></c:pt><c:pt idx="6"><c:v>6</c:v></c:pt><c:pt idx="7"><c:v>7</c:v></c:pt><c:pt idx="8"><c:v>8</c:v></c:pt><c:pt idx="9"><c:v>9</c:v></c:pt><c:pt idx="10"><c:v>None</c:v></c:pt></c:numCache></c:numRef></c:xVal><c:yVal><c:numRef><c:f>data!$A$1:$A$11</c:f><c:numCache><c:formatCode>General</c:formatCode><c:ptCount val="11" /><c:pt idx="0"><c:v>0</c:v></c:pt><c:pt idx="1"><c:v>1</c:v></c:pt><c:pt idx="2"><c:v>2</c:v></c:pt><c:pt idx="3"><c:v>3</c:v></c:pt><c:pt idx="4"><c:v>4</c:v></c:pt><c:pt idx="5"><c:v>5</c:v></c:pt><c:pt idx="6"><c:v>6</c:v></c:pt><c:pt idx="7"><c:v>7</c:v></c:pt><c:pt idx="8"><c:v>8</c:v></c:pt><c:pt idx="9"><c:v>9</c:v></c:pt><c:pt idx="10"><c:v>None</c:v></c:pt></c:numCache></c:numRef></c:yVal></c:ser><c:marker val="1" /><c:axId val="60871424" /><c:axId val="60873344" /></c:scatterChart><c:valAx><c:axId val="60871424" /><c:scaling><c:orientation val="minMax" /><c:max val="10.0" /><c:min val="0" /></c:scaling><c:axPos val="b" /><c:majorGridlines /><c:numFmt formatCode="General" sourceLinked="1" /><c:tickLblPos val="nextTo" /><c:crossAx val="60873344" /><c:crosses val="autoZero" /><c:auto val="1" /><c:lblAlgn val="ctr" /><c:lblOffset val="100" /><c:crossBetween val="midCat" /><c:majorUnit val="2.0" /></c:valAx><c:valAx><c:axId val="60873344" /><c:scaling><c:orientation val="minMax" /><c:max val="10.0" /><c:min val="0" /></c:scaling><c:axPos val="l" /><c:majorGridlines /><c:numFmt formatCode="General" sourceLinked="1" /><c:tickLblPos val="nextTo" /><c:crossAx val="60871424" /><c:crosses val="autoZero" /><c:crossBetween val="midCat" /><c:majorUnit val="2.0" /></c:valAx></c:plotArea><c:legend><c:legendPos val="r" /><c:layout /></c:legend><c:plotVisOnly val="1" /></c:chart></test>')
# file openpyxl/tests/test_chart.py
# Copyright (c) 2010 openpyxl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
from nose.tools import eq_
from openpyxl.tests.helper import get_xml
from openpyxl.shared.xmltools import Element
from openpyxl.writer.charts import ChartWriter
from openpyxl.workbook import Workbook
from openpyxl.chart import BarChart, ScatterChart, Serie, Reference
from openpyxl.style import Color
class TestChartWriter(object):
def setUp(self):
wb = Workbook()
ws = wb.get_active_sheet()
ws.title = 'data'
for i in range(10):
ws.cell(row = i, column = 0).value = i
self.chart = BarChart()
self.chart.title = 'TITLE'
self.chart.add_serie(Serie(Reference(ws, (0, 0), (10, 0))))
self.chart._series[-1].color = Color.GREEN
self.cw = ChartWriter(self.chart)
self.root = Element('test')
def test_write_title(self):
self.cw._write_title(self.root)
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:title><c:tx><c:rich><a:bodyPr /><a:lstStyle /><a:p><a:pPr><a:defRPr /></a:pPr><a:r><a:rPr lang="fr-FR" /><a:t>TITLE</a:t></a:r></a:p></c:rich></c:tx><c:layout /></c:title></test>')
def test_write_xaxis(self):
self.cw._write_axis(self.root, self.chart.x_axis, 'c:catAx')
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:catAx><c:axId val="60871424" /><c:scaling><c:orientation val="minMax" /></c:scaling><c:axPos val="b" /><c:tickLblPos val="nextTo" /><c:crossAx val="60873344" /><c:crosses val="autoZero" /><c:auto val="1" /><c:lblAlgn val="ctr" /><c:lblOffset val="100" /></c:catAx></test>')
def test_write_yaxis(self):
self.cw._write_axis(self.root, self.chart.y_axis, 'c:valAx')
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:valAx><c:axId val="60873344" /><c:scaling><c:orientation val="minMax" /><c:max val="10.0" /><c:min val="0" /></c:scaling><c:axPos val="l" /><c:majorGridlines /><c:numFmt formatCode="General" sourceLinked="1" /><c:tickLblPos val="nextTo" /><c:crossAx val="60871424" /><c:crosses val="autoZero" /><c:crossBetween val="between" /><c:majorUnit val="2.0" /></c:valAx></test>')
def test_write_series(self):
self.cw._write_series(self.root)
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:ser><c:idx val="0" /><c:order val="0" /><c:spPr><a:solidFill><a:srgbClr val="00FF00" /></a:solidFill><a:ln><a:solidFill><a:srgbClr val="00FF00" /></a:solidFill></a:ln></c:spPr><c:marker><c:symbol val="none" /></c:marker><c:val><c:numRef><c:f>data!$A$1:$A$11</c:f><c:numCache><c:formatCode>General</c:formatCode><c:ptCount val="11" /><c:pt idx="0"><c:v>0</c:v></c:pt><c:pt idx="1"><c:v>1</c:v></c:pt><c:pt idx="2"><c:v>2</c:v></c:pt><c:pt idx="3"><c:v>3</c:v></c:pt><c:pt idx="4"><c:v>4</c:v></c:pt><c:pt idx="5"><c:v>5</c:v></c:pt><c:pt idx="6"><c:v>6</c:v></c:pt><c:pt idx="7"><c:v>7</c:v></c:pt><c:pt idx="8"><c:v>8</c:v></c:pt><c:pt idx="9"><c:v>9</c:v></c:pt><c:pt idx="10"><c:v>None</c:v></c:pt></c:numCache></c:numRef></c:val></c:ser></test>')
def test_write_legend(self):
self.cw._write_legend(self.root)
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:legend><c:legendPos val="r" /><c:layout /></c:legend></test>')
def test_write_print_settings(self):
self.cw._write_print_settings(self.root)
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:printSettings><c:headerFooter /><c:pageMargins b="0.75" footer="0.3" header="0.3" l="0.7" r="0.7" t="0.75" /><c:pageSetup /></c:printSettings></test>')
def test_write_chart(self):
self.cw._write_chart(self.root)
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:chart><c:title><c:tx><c:rich><a:bodyPr /><a:lstStyle /><a:p><a:pPr><a:defRPr /></a:pPr><a:r><a:rPr lang="fr-FR" /><a:t>TITLE</a:t></a:r></a:p></c:rich></c:tx><c:layout /></c:title><c:plotArea><c:layout><c:manualLayout><c:layoutTarget val="inner" /><c:xMode val="edge" /><c:yMode val="edge" /><c:x val="1.28571428571" /><c:y val="0.2125" /><c:w val="0.6" /><c:h val="0.6" /></c:manualLayout></c:layout><c:barChart><c:barDir val="col" /><c:grouping val="clustered" /><c:ser><c:idx val="0" /><c:order val="0" /><c:spPr><a:solidFill><a:srgbClr val="00FF00" /></a:solidFill><a:ln><a:solidFill><a:srgbClr val="00FF00" /></a:solidFill></a:ln></c:spPr><c:marker><c:symbol val="none" /></c:marker><c:val><c:numRef><c:f>data!$A$1:$A$11</c:f><c:numCache><c:formatCode>General</c:formatCode><c:ptCount val="11" /><c:pt idx="0"><c:v>0</c:v></c:pt><c:pt idx="1"><c:v>1</c:v></c:pt><c:pt idx="2"><c:v>2</c:v></c:pt><c:pt idx="3"><c:v>3</c:v></c:pt><c:pt idx="4"><c:v>4</c:v></c:pt><c:pt idx="5"><c:v>5</c:v></c:pt><c:pt idx="6"><c:v>6</c:v></c:pt><c:pt idx="7"><c:v>7</c:v></c:pt><c:pt idx="8"><c:v>8</c:v></c:pt><c:pt idx="9"><c:v>9</c:v></c:pt><c:pt idx="10"><c:v>None</c:v></c:pt></c:numCache></c:numRef></c:val></c:ser><c:marker val="1" /><c:axId val="60871424" /><c:axId val="60873344" /></c:barChart><c:catAx><c:axId val="60871424" /><c:scaling><c:orientation val="minMax" /></c:scaling><c:axPos val="b" /><c:tickLblPos val="nextTo" /><c:crossAx val="60873344" /><c:crosses val="autoZero" /><c:auto val="1" /><c:lblAlgn val="ctr" /><c:lblOffset val="100" /></c:catAx><c:valAx><c:axId val="60873344" /><c:scaling><c:orientation val="minMax" /><c:max val="10.0" /><c:min val="0" /></c:scaling><c:axPos val="l" /><c:majorGridlines /><c:numFmt formatCode="General" sourceLinked="1" /><c:tickLblPos val="nextTo" /><c:crossAx val="60871424" /><c:crosses val="autoZero" /><c:crossBetween val="between" /><c:majorUnit val="2.0" /></c:valAx></c:plotArea><c:legend><c:legendPos val="r" /><c:layout /></c:legend><c:plotVisOnly val="1" /></c:chart></test>')
class TestScatterChartWriter(object):
def setUp(self):
wb = Workbook()
ws = wb.get_active_sheet()
ws.title = 'data'
for i in range(10):
ws.cell(row = i, column = 0).value = i
ws.cell(row = i, column = 1).value = i
self.scatterchart = ScatterChart()
self.scatterchart.add_serie(Serie(Reference(ws, (0, 0), (10, 0)),
xvalues = Reference(ws, (0, 1), (10, 1))))
self.cw = ChartWriter(self.scatterchart)
self.root = Element('test')
def test_write_xaxis(self):
self.cw._write_axis(self.root, self.scatterchart.x_axis, 'c:valAx')
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:valAx><c:axId val="60871424" /><c:scaling><c:orientation val="minMax" /><c:max val="10.0" /><c:min val="0" /></c:scaling><c:axPos val="b" /><c:majorGridlines /><c:numFmt formatCode="General" sourceLinked="1" /><c:tickLblPos val="nextTo" /><c:crossAx val="60873344" /><c:crosses val="autoZero" /><c:auto val="1" /><c:lblAlgn val="ctr" /><c:lblOffset val="100" /><c:crossBetween val="midCat" /><c:majorUnit val="2.0" /></c:valAx></test>')
def test_write_yaxis(self):
self.cw._write_axis(self.root, self.scatterchart.y_axis, 'c:valAx')
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:valAx><c:axId val="60873344" /><c:scaling><c:orientation val="minMax" /><c:max val="10.0" /><c:min val="0" /></c:scaling><c:axPos val="l" /><c:majorGridlines /><c:numFmt formatCode="General" sourceLinked="1" /><c:tickLblPos val="nextTo" /><c:crossAx val="60871424" /><c:crosses val="autoZero" /><c:crossBetween val="midCat" /><c:majorUnit val="2.0" /></c:valAx></test>')
def test_write_series(self):
self.cw._write_series(self.root)
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:ser><c:idx val="0" /><c:order val="0" /><c:marker><c:symbol val="none" /></c:marker><c:xVal><c:numRef><c:f>data!$B$1:$B$11</c:f><c:numCache><c:formatCode>General</c:formatCode><c:ptCount val="11" /><c:pt idx="0"><c:v>0</c:v></c:pt><c:pt idx="1"><c:v>1</c:v></c:pt><c:pt idx="2"><c:v>2</c:v></c:pt><c:pt idx="3"><c:v>3</c:v></c:pt><c:pt idx="4"><c:v>4</c:v></c:pt><c:pt idx="5"><c:v>5</c:v></c:pt><c:pt idx="6"><c:v>6</c:v></c:pt><c:pt idx="7"><c:v>7</c:v></c:pt><c:pt idx="8"><c:v>8</c:v></c:pt><c:pt idx="9"><c:v>9</c:v></c:pt><c:pt idx="10"><c:v>None</c:v></c:pt></c:numCache></c:numRef></c:xVal><c:yVal><c:numRef><c:f>data!$A$1:$A$11</c:f><c:numCache><c:formatCode>General</c:formatCode><c:ptCount val="11" /><c:pt idx="0"><c:v>0</c:v></c:pt><c:pt idx="1"><c:v>1</c:v></c:pt><c:pt idx="2"><c:v>2</c:v></c:pt><c:pt idx="3"><c:v>3</c:v></c:pt><c:pt idx="4"><c:v>4</c:v></c:pt><c:pt idx="5"><c:v>5</c:v></c:pt><c:pt idx="6"><c:v>6</c:v></c:pt><c:pt idx="7"><c:v>7</c:v></c:pt><c:pt idx="8"><c:v>8</c:v></c:pt><c:pt idx="9"><c:v>9</c:v></c:pt><c:pt idx="10"><c:v>None</c:v></c:pt></c:numCache></c:numRef></c:yVal></c:ser></test>')
def test_write_legend(self):
self.cw._write_legend(self.root)
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:legend><c:legendPos val="r" /><c:layout /></c:legend></test>')
def test_write_print_settings(self):
self.cw._write_print_settings(self.root)
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:printSettings><c:headerFooter /><c:pageMargins b="0.75" footer="0.3" header="0.3" l="0.7" r="0.7" t="0.75" /><c:pageSetup /></c:printSettings></test>')
def test_write_chart(self):
self.cw._write_chart(self.root)
eq_(get_xml(self.root), '<?xml version=\'1.0\' encoding=\'UTF-8\'?><test><c:chart><c:plotArea><c:layout><c:manualLayout><c:layoutTarget val="inner" /><c:xMode val="edge" /><c:yMode val="edge" /><c:x val="1.28571428571" /><c:y val="0.2125" /><c:w val="0.6" /><c:h val="0.6" /></c:manualLayout></c:layout><c:scatterChart><c:scatterStyle val="lineMarker" /><c:ser><c:idx val="0" /><c:order val="0" /><c:marker><c:symbol val="none" /></c:marker><c:xVal><c:numRef><c:f>data!$B$1:$B$11</c:f><c:numCache><c:formatCode>General</c:formatCode><c:ptCount val="11" /><c:pt idx="0"><c:v>0</c:v></c:pt><c:pt idx="1"><c:v>1</c:v></c:pt><c:pt idx="2"><c:v>2</c:v></c:pt><c:pt idx="3"><c:v>3</c:v></c:pt><c:pt idx="4"><c:v>4</c:v></c:pt><c:pt idx="5"><c:v>5</c:v></c:pt><c:pt idx="6"><c:v>6</c:v></c:pt><c:pt idx="7"><c:v>7</c:v></c:pt><c:pt idx="8"><c:v>8</c:v></c:pt><c:pt idx="9"><c:v>9</c:v></c:pt><c:pt idx="10"><c:v>None</c:v></c:pt></c:numCache></c:numRef></c:xVal><c:yVal><c:numRef><c:f>data!$A$1:$A$11</c:f><c:numCache><c:formatCode>General</c:formatCode><c:ptCount val="11" /><c:pt idx="0"><c:v>0</c:v></c:pt><c:pt idx="1"><c:v>1</c:v></c:pt><c:pt idx="2"><c:v>2</c:v></c:pt><c:pt idx="3"><c:v>3</c:v></c:pt><c:pt idx="4"><c:v>4</c:v></c:pt><c:pt idx="5"><c:v>5</c:v></c:pt><c:pt idx="6"><c:v>6</c:v></c:pt><c:pt idx="7"><c:v>7</c:v></c:pt><c:pt idx="8"><c:v>8</c:v></c:pt><c:pt idx="9"><c:v>9</c:v></c:pt><c:pt idx="10"><c:v>None</c:v></c:pt></c:numCache></c:numRef></c:yVal></c:ser><c:marker val="1" /><c:axId val="60871424" /><c:axId val="60873344" /></c:scatterChart><c:valAx><c:axId val="60871424" /><c:scaling><c:orientation val="minMax" /><c:max val="10.0" /><c:min val="0" /></c:scaling><c:axPos val="b" /><c:majorGridlines /><c:numFmt formatCode="General" sourceLinked="1" /><c:tickLblPos val="nextTo" /><c:crossAx val="60873344" /><c:crosses val="autoZero" /><c:auto val="1" /><c:lblAlgn val="ctr" /><c:lblOffset val="100" /><c:crossBetween val="midCat" /><c:majorUnit val="2.0" /></c:valAx><c:valAx><c:axId val="60873344" /><c:scaling><c:orientation val="minMax" /><c:max val="10.0" /><c:min val="0" /></c:scaling><c:axPos val="l" /><c:majorGridlines /><c:numFmt formatCode="General" sourceLinked="1" /><c:tickLblPos val="nextTo" /><c:crossAx val="60871424" /><c:crosses val="autoZero" /><c:crossBetween val="midCat" /><c:majorUnit val="2.0" /></c:valAx></c:plotArea><c:legend><c:legendPos val="r" /><c:layout /></c:legend><c:plotVisOnly val="1" /></c:chart></test>')
+112 -112
View File
@@ -1,112 +1,112 @@
# file openpyxl/tests/test_iter.py
# Copyright (c) 2010 openpyxl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
from nose.tools import eq_, raises, assert_raises
import os.path as osp
from openpyxl.tests.helper import DATADIR
from openpyxl.reader.iter_worksheet import get_range_boundaries
from openpyxl.reader.excel import load_workbook
import datetime
class TestWorksheet(object):
workbook_name = osp.join(DATADIR, 'genuine', 'empty.xlsx')
class TestText(TestWorksheet):
sheet_name = 'Sheet1 - Text'
expected = [['This is cell A1 in Sheet 1', None, None, None, None, None, None],
[None, None, None, None, None, None, None],
[None, None, None, None, None, None, None],
[None, None, None, None, None, None, None],
[None, None, None, None, None, None, 'This is cell G5'], ]
def test_read_fast_integrated(self):
wb = load_workbook(filename = self.workbook_name, use_iterators = True)
ws = wb.get_sheet_by_name(name = self.sheet_name)
for row, expected_row in zip(ws.iter_rows(), self.expected):
row_values = [x.internal_value for x in row]
eq_(row_values, expected_row)
def test_get_boundaries_range(self):
eq_(get_range_boundaries('C1:C4'), (3, 1, 3, 4))
def test_get_boundaries_one(self):
eq_(get_range_boundaries('C1'), (3, 1, 4, 1))
def test_read_single_cell_range(self):
wb = load_workbook(filename = self.workbook_name, use_iterators = True)
ws = wb.get_sheet_by_name(name = self.sheet_name)
eq_('This is cell A1 in Sheet 1', list(ws.iter_rows('A1'))[0][0].internal_value)
class TestIntegers(TestWorksheet):
sheet_name = 'Sheet2 - Numbers'
expected = [[x + 1] for x in range(30)]
query_range = 'D1:E30'
def test_read_fast_integrated(self):
wb = load_workbook(filename = self.workbook_name, use_iterators = True)
ws = wb.get_sheet_by_name(name = self.sheet_name)
for row, expected_row in zip(ws.iter_rows(self.query_range), self.expected):
row_values = [x.internal_value for x in row]
eq_(row_values, expected_row)
class TestFloats(TestWorksheet):
sheet_name = 'Sheet2 - Numbers'
query_range = 'K1:L30'
expected = expected = [[(x + 1) / 100.0] for x in range(30)]
class TestDates(TestWorksheet):
sheet_name = 'Sheet4 - Dates'
def test_read_single_cell_date(self):
wb = load_workbook(filename = self.workbook_name, use_iterators = True)
ws = wb.get_sheet_by_name(name = self.sheet_name)
eq_(datetime.datetime(1973, 5, 20), list(ws.iter_rows('A1'))[0][0].internal_value)
eq_(datetime.datetime(1973, 5, 20, 9, 15, 2), list(ws.iter_rows('C1'))[0][0].internal_value)
# file openpyxl/tests/test_iter.py
# Copyright (c) 2010 openpyxl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# @license: http://www.opensource.org/licenses/mit-license.php
# @author: Eric Gazoni
from nose.tools import eq_, raises, assert_raises
import os.path as osp
from openpyxl.tests.helper import DATADIR
from openpyxl.reader.iter_worksheet import get_range_boundaries
from openpyxl.reader.excel import load_workbook
import datetime
class TestWorksheet(object):
workbook_name = osp.join(DATADIR, 'genuine', 'empty.xlsx')
class TestText(TestWorksheet):
sheet_name = 'Sheet1 - Text'
expected = [['This is cell A1 in Sheet 1', None, None, None, None, None, None],
[None, None, None, None, None, None, None],
[None, None, None, None, None, None, None],
[None, None, None, None, None, None, None],
[None, None, None, None, None, None, 'This is cell G5'], ]
def test_read_fast_integrated(self):
wb = load_workbook(filename = self.workbook_name, use_iterators = True)
ws = wb.get_sheet_by_name(name = self.sheet_name)
for row, expected_row in zip(ws.iter_rows(), self.expected):
row_values = [x.internal_value for x in row]
eq_(row_values, expected_row)
def test_get_boundaries_range(self):
eq_(get_range_boundaries('C1:C4'), (3, 1, 3, 4))
def test_get_boundaries_one(self):
eq_(get_range_boundaries('C1'), (3, 1, 4, 1))
def test_read_single_cell_range(self):
wb = load_workbook(filename = self.workbook_name, use_iterators = True)
ws = wb.get_sheet_by_name(name = self.sheet_name)
eq_('This is cell A1 in Sheet 1', list(ws.iter_rows('A1'))[0][0].internal_value)
class TestIntegers(TestWorksheet):
sheet_name = 'Sheet2 - Numbers'
expected = [[x + 1] for x in range(30)]
query_range = 'D1:E30'
def test_read_fast_integrated(self):
wb = load_workbook(filename = self.workbook_name, use_iterators = True)
ws = wb.get_sheet_by_name(name = self.sheet_name)
for row, expected_row in zip(ws.iter_rows(self.query_range), self.expected):
row_values = [x.internal_value for x in row]
eq_(row_values, expected_row)
class TestFloats(TestWorksheet):
sheet_name = 'Sheet2 - Numbers'
query_range = 'K1:L30'
expected = expected = [[(x + 1) / 100.0] for x in range(30)]
class TestDates(TestWorksheet):
sheet_name = 'Sheet4 - Dates'
def test_read_single_cell_date(self):
wb = load_workbook(filename = self.workbook_name, use_iterators = True)
ws = wb.get_sheet_by_name(name = self.sheet_name)
eq_(datetime.datetime(1973, 5, 20), list(ws.iter_rows('A1'))[0][0].internal_value)
eq_(datetime.datetime(1973, 5, 20, 9, 15, 2), list(ws.iter_rows('C1'))[0][0].internal_value)
+1 -1
View File
@@ -24,7 +24,7 @@
# @author: Eric Gazoni
# Python stdlib imports
from __future__ import with_statement
import os.path
# package imports
@@ -24,7 +24,7 @@
# @author: Eric Gazoni
# Python stdlib imports
from __future__ import with_statement
import os.path
# 3rd-party imports
@@ -24,7 +24,7 @@
# @author: Eric Gazoni
# Python stdlib imports
from __future__ import with_statement
from datetime import datetime, date
# 3rd party imports
@@ -57,7 +57,7 @@ class TestNumberFormat(object):
date_pairs= (
(40167, datetime(2009, 12, 20)),
(21980, datetime(1960, 03, 05)),
(21980, datetime(1960, 0o3, 0o5)),
)
for count, dt in date_pairs:
@@ -128,7 +128,7 @@ class TestNumberFormat(object):
def check_bad_date(year, month, day):
assert_raises(ValueError, self.sd.to_julian, year, month, day)
bad_dates = ((1776, 07, 04), (1899, 12, 31), )
bad_dates = ((1776, 0o7, 0o4), (1899, 12, 31), )
for year, month, day in bad_dates:
yield check_bad_date, year, month, day
Binary file not shown.
+1 -1
View File
@@ -24,7 +24,7 @@
# @author: Eric Gazoni
# Python stdlib imports
from __future__ import with_statement
import os.path
# 3rd party imports
@@ -24,7 +24,7 @@
# @author: Eric Gazoni
# Python stdlib imports
from __future__ import with_statement
import os.path
# 3rd party imports
@@ -24,7 +24,7 @@
# @author: Eric Gazoni
# Python stdlib imports
from __future__ import with_statement
import os.path
import datetime
@@ -24,8 +24,8 @@
# @author: Eric Gazoni
# Python stdlib imports
from __future__ import with_statement
from io import BytesIO as StringIO
from io import StringIO
import os.path
# 3rd party imports
+4 -4
View File
@@ -187,7 +187,7 @@ class Worksheet(object):
"""Represents a worksheet.
Do not create worksheets yourself,
use :func:`openpyxl.workbook.Workbook.create_sheet` instead
use :func:`.workbook.Workbook.create_sheet` instead
"""
BREAK_NONE = 0
@@ -241,7 +241,7 @@ class Worksheet(object):
def get_cell_collection(self):
"""Return an unordered list of the cells in this worksheet."""
return self._cells.values()
return list(self._cells.values())
def _set_title(self, value):
"""Set a sheet title, ensuring it is valid."""
@@ -325,7 +325,7 @@ class Worksheet(object):
:raise: InsufficientCoordinatesException when coordinate or (row and column) are not given
:rtype: :class:`openpyxl.cell.Cell`
:rtype: :class:`.cell.Cell`
"""
if not coordinate:
@@ -390,7 +390,7 @@ class Worksheet(object):
:param column: number of columns to offset
:type column: int
:rtype: tuples of tuples of :class:`openpyxl.cell.Cell`
:rtype: tuples of tuples of :class:`.cell.Cell`
"""
if ':' in range_string:
+262 -261
View File
@@ -1,261 +1,262 @@
# coding=UTF-8
'''
Copyright (c) 2010 openpyxl
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@license: http://www.opensource.org/licenses/mit-license.php
@author: Eric Gazoni
'''
from ..shared.xmltools import Element, SubElement, get_document_content
from ..chart import Chart, ErrorBar
class ChartWriter(object):
def __init__(self, chart):
self.chart = chart
def write(self):
""" write a chart """
root = Element('c:chartSpace',
{'xmlns:c':"http://schemas.openxmlformats.org/drawingml/2006/chart",
'xmlns:a':"http://schemas.openxmlformats.org/drawingml/2006/main",
'xmlns:r':"http://schemas.openxmlformats.org/officeDocument/2006/relationships"})
SubElement(root, 'c:lang', {'val':self.chart.lang})
self._write_chart(root)
self._write_print_settings(root)
self._write_shapes(root)
return get_document_content(root)
def _write_chart(self, root):
chart = self.chart
ch = SubElement(root, 'c:chart')
self._write_title(ch)
plot_area = SubElement(ch, 'c:plotArea')
layout = SubElement(plot_area, 'c:layout')
mlayout = SubElement(layout, 'c:manualLayout')
SubElement(mlayout, 'c:layoutTarget', {'val':'inner'})
SubElement(mlayout, 'c:xMode', {'val':'edge'})
SubElement(mlayout, 'c:yMode', {'val':'edge'})
SubElement(mlayout, 'c:x', {'val':str(chart._get_margin_left())})
SubElement(mlayout, 'c:y', {'val':str(chart._get_margin_top())})
SubElement(mlayout, 'c:w', {'val':str(chart.width)})
SubElement(mlayout, 'c:h', {'val':str(chart.height)})
if chart.type == Chart.SCATTER_CHART:
subchart = SubElement(plot_area, 'c:scatterChart')
SubElement(subchart, 'c:scatterStyle', {'val':str('lineMarker')})
else:
if chart.type == Chart.BAR_CHART:
subchart = SubElement(plot_area, 'c:barChart')
SubElement(subchart, 'c:barDir', {'val':'col'})
else:
subchart = SubElement(plot_area, 'c:lineChart')
SubElement(subchart, 'c:grouping', {'val':chart.grouping})
self._write_series(subchart)
SubElement(subchart, 'c:marker', {'val':'1'})
SubElement(subchart, 'c:axId', {'val':str(chart.x_axis.id)})
SubElement(subchart, 'c:axId', {'val':str(chart.y_axis.id)})
if chart.type == Chart.SCATTER_CHART:
self._write_axis(plot_area, chart.x_axis, 'c:valAx')
else:
self._write_axis(plot_area, chart.x_axis, 'c:catAx')
self._write_axis(plot_area, chart.y_axis, 'c:valAx')
self._write_legend(ch)
SubElement(ch, 'c:plotVisOnly', {'val':'1'})
def _write_title(self, chart):
if self.chart.title != '':
title = SubElement(chart, 'c:title')
tx = SubElement(title, 'c:tx')
rich = SubElement(tx, 'c:rich')
SubElement(rich, 'a:bodyPr')
SubElement(rich, 'a:lstStyle')
p = SubElement(rich, 'a:p')
pPr = SubElement(p, 'a:pPr')
SubElement(pPr, 'a:defRPr')
r = SubElement(p, 'a:r')
SubElement(r, 'a:rPr', {'lang':self.chart.lang})
t = SubElement(r, 'a:t').text = self.chart.title
SubElement(title, 'c:layout')
def _write_axis(self, plot_area, axis, label):
ax = SubElement(plot_area, label)
SubElement(ax, 'c:axId', {'val':str(axis.id)})
scaling = SubElement(ax, 'c:scaling')
SubElement(scaling, 'c:orientation', {'val':axis.orientation})
if label == 'c:valAx':
SubElement(scaling, 'c:max', {'val':str(axis.max)})
SubElement(scaling, 'c:min', {'val':str(axis.min)})
SubElement(ax, 'c:axPos', {'val':axis.position})
if label == 'c:valAx':
SubElement(ax, 'c:majorGridlines')
SubElement(ax, 'c:numFmt', {'formatCode':"General", 'sourceLinked':'1'})
SubElement(ax, 'c:tickLblPos', {'val':axis.tick_label_position})
SubElement(ax, 'c:crossAx', {'val':str(axis.cross)})
SubElement(ax, 'c:crosses', {'val':axis.crosses})
if axis.auto:
SubElement(ax, 'c:auto', {'val':'1'})
if axis.label_align:
SubElement(ax, 'c:lblAlgn', {'val':axis.label_align})
if axis.label_offset:
SubElement(ax, 'c:lblOffset', {'val':str(axis.label_offset)})
if label == 'c:valAx':
if self.chart.type == Chart.SCATTER_CHART:
SubElement(ax, 'c:crossBetween', {'val':'midCat'})
else:
SubElement(ax, 'c:crossBetween', {'val':'between'})
SubElement(ax, 'c:majorUnit', {'val':str(axis.unit)})
def _write_series(self, subchart):
for i, serie in enumerate(self.chart._series):
ser = SubElement(subchart, 'c:ser')
SubElement(ser, 'c:idx', {'val':str(i)})
SubElement(ser, 'c:order', {'val':str(i)})
if serie.legend:
tx = SubElement(ser, 'c:tx')
self._write_serial(tx, serie.legend)
if serie.color:
sppr = SubElement(ser, 'c:spPr')
if self.chart.type == Chart.BAR_CHART:
# fill color
fillc = SubElement(sppr, 'a:solidFill')
SubElement(fillc, 'a:srgbClr', {'val':serie.color})
# edge color
ln = SubElement(sppr, 'a:ln')
fill = SubElement(ln, 'a:solidFill')
SubElement(fill, 'a:srgbClr', {'val':serie.color})
if serie.error_bar:
self._write_error_bar(ser, serie)
marker = SubElement(ser, 'c:marker')
SubElement(marker, 'c:symbol', {'val':serie.marker})
if serie.labels:
cat = SubElement(ser, 'c:cat')
self._write_serial(cat, serie.labels)
if self.chart.type == Chart.SCATTER_CHART:
if serie.xvalues:
xval = SubElement(ser, 'c:xVal')
self._write_serial(xval, serie.xvalues)
yval = SubElement(ser, 'c:yVal')
self._write_serial(yval, serie.values)
else:
val = SubElement(ser, 'c:val')
self._write_serial(val, serie.values)
def _write_serial(self, node, serie, literal=False):
cache = serie._get_cache()
if isinstance(cache[0], str):
typ = 'str'
else:
typ = 'num'
if not literal:
if typ == 'num':
ref = SubElement(node, 'c:numRef')
else:
ref = SubElement(node, 'c:strRef')
SubElement(ref, 'c:f').text = serie._get_ref()
if typ == 'num':
data = SubElement(ref, 'c:numCache')
else:
data = SubElement(ref, 'c:strCache')
else:
data = SubElement(node, 'c:numLit')
if typ == 'num':
SubElement(data, 'c:formatCode').text = 'General'
if literal:
values = (1,)
else:
values = cache
SubElement(data, 'c:ptCount', {'val':str(len(values))})
for j, val in enumerate(values):
point = SubElement(data, 'c:pt', {'idx':str(j)})
SubElement(point, 'c:v').text = str(val)
def _write_error_bar(self, node, serie):
flag = {ErrorBar.PLUS_MINUS:'both',
ErrorBar.PLUS:'plus',
ErrorBar.MINUS:'minus'}
eb = SubElement(node, 'c:errBars')
SubElement(eb, 'c:errBarType', {'val':flag[serie.error_bar.type]})
SubElement(eb, 'c:errValType', {'val':'cust'})
plus = SubElement(eb, 'c:plus')
self._write_serial(plus, serie.error_bar.values,
literal=(serie.error_bar.type==ErrorBar.MINUS))
minus = SubElement(eb, 'c:minus')
self._write_serial(minus, serie.error_bar.values,
literal=(serie.error_bar.type==ErrorBar.PLUS))
def _write_legend(self, chart):
legend = SubElement(chart, 'c:legend')
SubElement(legend, 'c:legendPos', {'val':self.chart.legend.position})
SubElement(legend, 'c:layout')
def _write_print_settings(self, root):
settings = SubElement(root, 'c:printSettings')
SubElement(settings, 'c:headerFooter')
margins = dict([(k, str(v)) for (k,v) in self.chart.print_margins.items()])
SubElement(settings, 'c:pageMargins', margins)
SubElement(settings, 'c:pageSetup')
def _write_shapes(self, root):
if self.chart._shapes:
SubElement(root, 'c:userShapes', {'r:id':'rId1'})
def write_rels(self, drawing_id):
root = Element('Relationships', {'xmlns' : 'http://schemas.openxmlformats.org/package/2006/relationships'})
attrs = {'Id' : 'rId1',
'Type' : 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartUserShapes',
'Target' : '../drawings/drawing%s.xml' % drawing_id }
SubElement(root, 'Relationship', attrs)
return get_document_content(root)
# coding=UTF-8
'''
Copyright (c) 2010 openpyxl
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@license: http://www.opensource.org/licenses/mit-license.php
@author: Eric Gazoni
'''
from ..shared.xmltools import Element, SubElement, get_document_content
from ..chart import Chart, ErrorBar
class ChartWriter(object):
def __init__(self, chart):
self.chart = chart
def write(self):
""" write a chart """
root = Element('c:chartSpace',
{'xmlns:c':"http://schemas.openxmlformats.org/drawingml/2006/chart",
'xmlns:a':"http://schemas.openxmlformats.org/drawingml/2006/main",
'xmlns:r':"http://schemas.openxmlformats.org/officeDocument/2006/relationships"})
SubElement(root, 'c:lang', {'val':self.chart.lang})
self._write_chart(root)
self._write_print_settings(root)
self._write_shapes(root)
return get_document_content(root)
def _write_chart(self, root):
chart = self.chart
ch = SubElement(root, 'c:chart')
self._write_title(ch)
plot_area = SubElement(ch, 'c:plotArea')
layout = SubElement(plot_area, 'c:layout')
mlayout = SubElement(layout, 'c:manualLayout')
SubElement(mlayout, 'c:layoutTarget', {'val':'inner'})
SubElement(mlayout, 'c:xMode', {'val':'edge'})
SubElement(mlayout, 'c:yMode', {'val':'edge'})
SubElement(mlayout, 'c:x', {'val':str(chart._get_margin_left())})
SubElement(mlayout, 'c:y', {'val':str(chart._get_margin_top())})
SubElement(mlayout, 'c:w', {'val':str(chart.width)})
SubElement(mlayout, 'c:h', {'val':str(chart.height)})
if chart.type == Chart.SCATTER_CHART:
subchart = SubElement(plot_area, 'c:scatterChart')
SubElement(subchart, 'c:scatterStyle', {'val':str('lineMarker')})
else:
if chart.type == Chart.BAR_CHART:
subchart = SubElement(plot_area, 'c:barChart')
SubElement(subchart, 'c:barDir', {'val':'col'})
else:
subchart = SubElement(plot_area, 'c:lineChart')
SubElement(subchart, 'c:grouping', {'val':chart.grouping})
self._write_series(subchart)
SubElement(subchart, 'c:marker', {'val':'1'})
SubElement(subchart, 'c:axId', {'val':str(chart.x_axis.id)})
SubElement(subchart, 'c:axId', {'val':str(chart.y_axis.id)})
if chart.type == Chart.SCATTER_CHART:
self._write_axis(plot_area, chart.x_axis, 'c:valAx')
else:
self._write_axis(plot_area, chart.x_axis, 'c:catAx')
self._write_axis(plot_area, chart.y_axis, 'c:valAx')
self._write_legend(ch)
SubElement(ch, 'c:plotVisOnly', {'val':'1'})
def _write_title(self, chart):
if self.chart.title != '':
title = SubElement(chart, 'c:title')
tx = SubElement(title, 'c:tx')
rich = SubElement(tx, 'c:rich')
SubElement(rich, 'a:bodyPr')
SubElement(rich, 'a:lstStyle')
p = SubElement(rich, 'a:p')
pPr = SubElement(p, 'a:pPr')
SubElement(pPr, 'a:defRPr')
r = SubElement(p, 'a:r')
SubElement(r, 'a:rPr', {'lang':self.chart.lang})
t = SubElement(r, 'a:t').text = self.chart.title
SubElement(title, 'c:layout')
def _write_axis(self, plot_area, axis, label):
ax = SubElement(plot_area, label)
SubElement(ax, 'c:axId', {'val':str(axis.id)})
scaling = SubElement(ax, 'c:scaling')
SubElement(scaling, 'c:orientation', {'val':axis.orientation})
if label == 'c:valAx':
SubElement(scaling, 'c:max', {'val':str(axis.max)})
SubElement(scaling, 'c:min', {'val':str(axis.min)})
SubElement(ax, 'c:axPos', {'val':axis.position})
if label == 'c:valAx':
SubElement(ax, 'c:majorGridlines')
SubElement(ax, 'c:numFmt', {'formatCode':"General", 'sourceLinked':'1'})
SubElement(ax, 'c:tickLblPos', {'val':axis.tick_label_position})
SubElement(ax, 'c:crossAx', {'val':str(axis.cross)})
SubElement(ax, 'c:crosses', {'val':axis.crosses})
if axis.auto:
SubElement(ax, 'c:auto', {'val':'1'})
if axis.label_align:
SubElement(ax, 'c:lblAlgn', {'val':axis.label_align})
if axis.label_offset:
SubElement(ax, 'c:lblOffset', {'val':str(axis.label_offset)})
if label == 'c:valAx':
if self.chart.type == Chart.SCATTER_CHART:
SubElement(ax, 'c:crossBetween', {'val':'midCat'})
else:
SubElement(ax, 'c:crossBetween', {'val':'between'})
SubElement(ax, 'c:majorUnit', {'val':str(axis.unit)})
def _write_series(self, subchart):
for i, serie in enumerate(self.chart._series):
ser = SubElement(subchart, 'c:ser')
SubElement(ser, 'c:idx', {'val':str(i)})
SubElement(ser, 'c:order', {'val':str(i)})
if serie.legend:
tx = SubElement(ser, 'c:tx')
self._write_serial(tx, serie.legend)
if serie.color:
sppr = SubElement(ser, 'c:spPr')
if self.chart.type == Chart.BAR_CHART:
# fill color
fillc = SubElement(sppr, 'a:solidFill')
SubElement(fillc, 'a:srgbClr', {'val':serie.color})
# edge color
ln = SubElement(sppr, 'a:ln')
fill = SubElement(ln, 'a:solidFill')
SubElement(fill, 'a:srgbClr', {'val':serie.color})
if serie.error_bar:
self._write_error_bar(ser, serie)
marker = SubElement(ser, 'c:marker')
SubElement(marker, 'c:symbol', {'val':serie.marker})
if serie.labels:
cat = SubElement(ser, 'c:cat')
self._write_serial(cat, serie.labels)
if self.chart.type == Chart.SCATTER_CHART:
if serie.xvalues:
xval = SubElement(ser, 'c:xVal')
self._write_serial(xval, serie.xvalues)
yval = SubElement(ser, 'c:yVal')
self._write_serial(yval, serie.values)
else:
val = SubElement(ser, 'c:val')
self._write_serial(val, serie.values)
def _write_serial(self, node, serie, literal=False):
cache = serie._get_cache()
if isinstance(cache[0], str):
typ = 'str'
else:
typ = 'num'
if not literal:
if typ == 'num':
ref = SubElement(node, 'c:numRef')
else:
ref = SubElement(node, 'c:strRef')
SubElement(ref, 'c:f').text = serie._get_ref()
if typ == 'num':
data = SubElement(ref, 'c:numCache')
else:
data = SubElement(ref, 'c:strCache')
else:
data = SubElement(node, 'c:numLit')
if typ == 'num':
SubElement(data, 'c:formatCode').text = 'General'
if literal:
values = (1,)
else:
values = cache
SubElement(data, 'c:ptCount', {'val':str(len(values))})
for j, val in enumerate(values):
point = SubElement(data, 'c:pt', {'idx':str(j)})
SubElement(point, 'c:v').text = str(val)
def _write_error_bar(self, node, serie):
flag = {ErrorBar.PLUS_MINUS:'both',
ErrorBar.PLUS:'plus',
ErrorBar.MINUS:'minus'}
eb = SubElement(node, 'c:errBars')
SubElement(eb, 'c:errBarType', {'val':flag[serie.error_bar.type]})
SubElement(eb, 'c:errValType', {'val':'cust'})
plus = SubElement(eb, 'c:plus')
self._write_serial(plus, serie.error_bar.values,
literal=(serie.error_bar.type==ErrorBar.MINUS))
minus = SubElement(eb, 'c:minus')
self._write_serial(minus, serie.error_bar.values,
literal=(serie.error_bar.type==ErrorBar.PLUS))
def _write_legend(self, chart):
legend = SubElement(chart, 'c:legend')
SubElement(legend, 'c:legendPos', {'val':self.chart.legend.position})
SubElement(legend, 'c:layout')
def _write_print_settings(self, root):
settings = SubElement(root, 'c:printSettings')
SubElement(settings, 'c:headerFooter')
margins = dict([(k, str(v)) for (k,v) in self.chart.print_margins.items()])
SubElement(settings, 'c:pageMargins', margins)
SubElement(settings, 'c:pageSetup')
def _write_shapes(self, root):
if self.chart._shapes:
SubElement(root, 'c:userShapes', {'r:id':'rId1'})
def write_rels(self, drawing_id):
root = Element('Relationships', {'xmlns' : 'http://schemas.openxmlformats.org/package/2006/relationships'})
attrs = {'Id' : 'rId1',
'Type' : 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartUserShapes',
'Target' : '../drawings/drawing%s.xml' % drawing_id }
SubElement(root, 'Relationship', attrs)
return get_document_content(root)
@@ -66,7 +66,7 @@ class DumpWorksheet(Worksheet):
"""
.. warning::
You shouldn't initialize this yourself, use :class:`openpyxl.workbook.Workbook` constructor instead,
You shouldn't initialize this yourself, use :class:`..workbook.Workbook` constructor instead,
with `optimized_write = True`.
"""
@@ -77,9 +77,9 @@ class DumpWorksheet(Worksheet):
self._max_col = 0
self._max_row = 0
self._parent = parent_workbook
self._fileobj_header = NamedTemporaryFile(mode='r+', prefix='openpyxl.', suffix='.header', delete=False)
self._fileobj_content = NamedTemporaryFile(mode='r+', prefix='openpyxl.', suffix='.content', delete=False)
self._fileobj = NamedTemporaryFile(mode='w', prefix='openpyxl.', delete=False)
self._fileobj_header = NamedTemporaryFile(mode='r+', prefix='..', suffix='.header', delete=False)
self._fileobj_content = NamedTemporaryFile(mode='r+', prefix='..', suffix='.content', delete=False)
self._fileobj = NamedTemporaryFile(mode='w', prefix='..', delete=False)
self.doc = XMLGenerator(self._fileobj_content, 'utf-8')
self.header = XMLGenerator(self._fileobj_header, 'utf-8')
self.title = 'Sheet'
+8 -8
View File
@@ -27,22 +27,22 @@
# Python stdlib imports
from zipfile import ZipFile, ZIP_DEFLATED
from io import BytesIO as StringIO
from io import StringIO
# package imports
from ..shared.ooxml import ARC_SHARED_STRINGS, ARC_CONTENT_TYPES, \
ARC_ROOT_RELS, ARC_WORKBOOK_RELS, ARC_APP, ARC_CORE, ARC_THEME, \
ARC_STYLE, ARC_WORKBOOK, \
PACKAGE_WORKSHEETS, PACKAGE_DRAWINGS, PACKAGE_CHARTS
from ..writer.strings import create_string_table, write_string_table
from ..writer.workbook import write_content_types, write_root_rels, \
from .strings import create_string_table, write_string_table
from .workbook import write_content_types, write_root_rels, \
write_workbook_rels, write_properties_app, write_properties_core, \
write_workbook
from ..writer.theme import write_theme
from ..writer.styles import StyleWriter
from ..writer.drawings import DrawingWriter, ShapeWriter
from ..writer.charts import ChartWriter
from ..writer.worksheet import write_worksheet, write_worksheet_rels
from .theme import write_theme
from .styles import StyleWriter
from .drawings import DrawingWriter, ShapeWriter
from .charts import ChartWriter
from .worksheet import write_worksheet, write_worksheet_rels
class ExcelWriter(object):
+2 -2
View File
@@ -26,7 +26,7 @@
"""Write the shared string table."""
# Python stdlib imports
from io import BytesIO as StringIO
from io import StringIO
# package imports
from ..shared.xmltools import start_tag, end_tag, tag, XMLGenerator
@@ -49,7 +49,7 @@ def write_string_table(string_table):
start_tag(doc, 'sst', {'xmlns':
'http://schemas.openxmlformats.org/spreadsheetml/2006/main',
'uniqueCount': '%d' % len(string_table)})
strings_to_write = sorted(string_table.items(),
strings_to_write = sorted(iter(string_table.items()),
key=lambda pair: pair[1])
for key in [pair[0] for pair in strings_to_write]:
start_tag(doc, 'si')
+3 -3
View File
@@ -40,11 +40,11 @@ class StyleWriter(object):
def _get_style_list(self, workbook):
crc = {}
for worksheet in workbook.worksheets:
for style in worksheet._styles.values():
for style in list(worksheet._styles.values()):
crc[hash(style)] = style
self.style_table = dict([(style, i+1) \
for i, style in enumerate(crc.values())])
sorted_styles = sorted(self.style_table.items(), \
for i, style in enumerate(list(crc.values()))])
sorted_styles = sorted(iter(self.style_table.items()), \
key = lambda pair:pair[1])
return [s[0] for s in sorted_styles]
@@ -26,7 +26,7 @@
"""Write worksheets to xml representations."""
# Python stdlib imports
from io import BytesIO as StringIO # cStringIO doesn't handle unicode
from io import StringIO # cStringIO doesn't handle unicode
# package imports
from ..cell import coordinate_from_string, column_index_from_string