mirror of
https://github.com/kennethreitz/tablib.git
synced 2026-06-05 23:10:17 +00:00
Finally! :sparkles:Python 3✨ port of openpyxl
This commit is contained in:
@@ -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
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
"""
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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>')
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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.
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user