Skip to content

Commit

Permalink
Added support for non-contiguous chart ranges.
Browse files Browse the repository at this point in the history
Issue #44.
  • Loading branch information
jmcnamara committed Dec 30, 2013
1 parent 027c648 commit 07b1829
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 8 deletions.
7 changes: 7 additions & 0 deletions dev/docs/source/chart.rst
Expand Up @@ -223,6 +223,13 @@ More than one series can be added to a chart. In fact, some chart types such as
``stock`` require it. The series numbering and order in the Excel chart will
be the same as the order in which they are added in XlsxWriter.

It is also possible to specify non-contiguous ranges::

chart.add_series({
'categories': '=(Sheet1!$A$1:$A$9,Sheet1!$A$14:$A$25)',
'values': '=(Sheet1!$B$1:$B$9,Sheet1!$B$14:$B$25)',
})


chart.set_x_axis()
------------------
Expand Down
81 changes: 81 additions & 0 deletions xlsxwriter/test/comparison/test_chart_column07.py
@@ -0,0 +1,81 @@
###############################################################################
#
# Tests for XlsxWriter.
#
# Copyright (c), 2013, John McNamara, jmcnamara@cpan.org
#

import unittest
import os
from ...workbook import Workbook
from ..helperfunctions import _compare_xlsx_files


class TestCompareXLSXFiles(unittest.TestCase):
"""
Test file created by XlsxWriter against a file created by Excel.
"""

def setUp(self):
self.maxDiff = None

filename = 'chart_column07.xlsx'

test_dir = 'xlsxwriter/test/comparison/'
self.got_filename = test_dir + '_test_' + filename
self.exp_filename = test_dir + 'xlsx_files/' + filename

self.ignore_files = []
self.ignore_elements = {}

def test_create_file(self):
"""Test the creation of a simple XlsxWriter file."""
filename = self.got_filename

####################################################

workbook = Workbook(filename)

worksheet = workbook.add_worksheet()
chart = workbook.add_chart({'type': 'column'})

chart.axis_ids = [68810240, 68811776]

data = [
[1, 2, 3, 4, 5],
[2, 4, 6, 8, 10],
[3, 6, 9, 12, 15],

]

worksheet.write_column('A1', data[0])
worksheet.write_column('B1', data[1])
worksheet.write_column('C1', data[2])

chart.add_series({
'values': '=(Sheet1!$A$1:$A$2,Sheet1!$A$4:$A$5)',
'values_data': [1, 2, 4, 5],
})

worksheet.insert_chart('E9', chart)

workbook.close()

####################################################

got, exp = _compare_xlsx_files(self.got_filename,
self.exp_filename,
self.ignore_files,
self.ignore_elements)

self.assertEqual(got, exp)

def tearDown(self):
# Cleanup.
if os.path.exists(self.got_filename):
os.remove(self.got_filename)


if __name__ == '__main__':
unittest.main()
83 changes: 83 additions & 0 deletions xlsxwriter/test/comparison/test_chart_column08.py
@@ -0,0 +1,83 @@
###############################################################################
#
# Tests for XlsxWriter.
#
# Copyright (c), 2013, John McNamara, jmcnamara@cpan.org
#

import unittest
import os
from ...workbook import Workbook
from ..helperfunctions import _compare_xlsx_files


class TestCompareXLSXFiles(unittest.TestCase):
"""
Test file created by XlsxWriter against a file created by Excel.
"""

def setUp(self):
self.maxDiff = None

filename = 'chart_column08.xlsx'

test_dir = 'xlsxwriter/test/comparison/'
self.got_filename = test_dir + '_test_' + filename
self.exp_filename = test_dir + 'xlsx_files/' + filename

self.ignore_files = []
self.ignore_elements = {}

def test_create_file(self):
"""Test the creation of a simple XlsxWriter file."""
filename = self.got_filename

####################################################

workbook = Workbook(filename)

worksheet = workbook.add_worksheet()
chart = workbook.add_chart({'type': 'column'})

chart.axis_ids = [68809856, 68811392]

data = [
[1, 2, 3, 4, 5],
[2, 4, 6, 8, 10],
[3, 6, 9, 12, 15],

]

worksheet.write_column('A1', data[0])
worksheet.write_column('B1', data[1])
worksheet.write_column('C1', data[2])

chart.add_series({
'categories': '=(Sheet1!$A$1:$A$2,Sheet1!$A$4:$A$5)',
'values': '=(Sheet1!$B$1:$B$2,Sheet1!$B$4:$B$5)',
'categories_data': [1, 2, 4, 5],
'values_data': [2, 4, 8, 10],
})

worksheet.insert_chart('E9', chart)

workbook.close()

####################################################

got, exp = _compare_xlsx_files(self.got_filename,
self.exp_filename,
self.ignore_files,
self.ignore_elements)

self.assertEqual(got, exp)

def tearDown(self):
# Cleanup.
if os.path.exists(self.got_filename):
os.remove(self.got_filename)


if __name__ == '__main__':
unittest.main()
Binary file not shown.
Binary file not shown.
24 changes: 16 additions & 8 deletions xlsxwriter/workbook.py
Expand Up @@ -1038,22 +1038,30 @@ def _add_chart_data(self):
if sheetname is None:
continue

# Die if the name is unknown since it indicates a user error in
# a chart series formula.
# Handle non-contiguous ranges like:
# (Sheet1!$A$1:$A$2,Sheet1!$A$4:$A$5).
# We don't try to parse them. We just return an empty list.
if sheetname.startswith('('):
chart.formula_data[r_id] = []
seen_ranges[c_range] = []
continue

# Warn if the name is unknown since it indicates a user error
# in a chart series formula.
if not sheetname in worksheets:
warn("Unknown worksheet reference '%s' in range "
"'%s' passed to add_series()" % (sheetname, c_range))
chart.formula_data[r_id] = []
seen_ranges[c_range] = []
continue

# Find the worksheet object based on the sheet name.
worksheet = worksheets[sheetname]

# Get the data from the worksheet table.
data = worksheet._get_range_data(*cells)

# TODO
# # Ignore rich strings for now. Deparse later if necessary.
# if token =~ m{^<r>} and token =~ m{</r>$}:
# token = ''
# TODO. Handle SST string ids if required.

# Add the data to the chart.
chart.formula_data[r_id] = data
Expand All @@ -1069,13 +1077,13 @@ def _get_chart_range(self, c_range):
# TODO. Fix this to match from right.
pos = c_range.find('!')
if pos > 0:
sheetname, cells = c_range.split('!')
sheetname, cells = c_range.split('!', 1)
else:
return None

# Split the cell range into 2 cells or else use single cell for both.
if cells.find(':') > 0:
(cell_1, cell_2) = cells.split(':')
(cell_1, cell_2) = cells.split(':', 1)
else:
(cell_1, cell_2) = (cells, cells)

Expand Down

0 comments on commit 07b1829

Please sign in to comment.