Initial chart error bars.

This commit is contained in:
John McNamara 2017-01-26 00:07:38 +00:00
parent 945f93599f
commit fbeeef7cc1
10 changed files with 459 additions and 22 deletions

4
.indent.pro vendored
View file

@ -54,6 +54,9 @@
-T lxw_chart_axis_tick_mark
-T lxw_chart_axis_tick_position
-T lxw_chart_blank
-T lxw_chart_error_bar_cap
-T lxw_chart_error_bar_direction
-T lxw_chart_error_bar_type
-T lxw_chart_fill
-T lxw_chart_font
-T lxw_chart_gridline
@ -108,6 +111,7 @@
-T lxw_row_t
-T lxw_selection
-T lxw_series_data_point
-T lxw_series_error_bar
-T lxw_series_range
-T lxw_sst
-T lxw_styles

View file

@ -569,28 +569,6 @@ typedef enum lxw_chart_axis_tick_mark {
LXW_CHART_AXIS_TICK_MARK_CROSSING
} lxw_chart_tick_mark;
/**
* @brief Define how blank values are displayed in a chart.
*/
typedef enum lxw_chart_blank {
/** Show empty chart cells as gaps in the data. The default. */
LXW_CHART_BLANKS_AS_GAP,
/** Show empty chart cells as zeros. */
LXW_CHART_BLANKS_AS_ZERO,
/** Show empty chart cells as connected. Only for charts with lines. */
LXW_CHART_BLANKS_AS_CONNECTED
} lxw_chart_blank;
enum lxw_chart_position {
LXW_CHART_AXIS_RIGHT,
LXW_CHART_AXIS_LEFT,
LXW_CHART_AXIS_TOP,
LXW_CHART_AXIS_BOTTOM
};
typedef struct lxw_series_range {
char *formula;
char *sheetname;
@ -776,6 +754,81 @@ typedef struct lxw_chart_point {
} lxw_chart_point;
/**
* @brief Define how blank values are displayed in a chart.
*/
typedef enum lxw_chart_blank {
/** Show empty chart cells as gaps in the data. The default. */
LXW_CHART_BLANKS_AS_GAP,
/** Show empty chart cells as zeros. */
LXW_CHART_BLANKS_AS_ZERO,
/** Show empty chart cells as connected. Only for charts with lines. */
LXW_CHART_BLANKS_AS_CONNECTED
} lxw_chart_blank;
enum lxw_chart_position {
LXW_CHART_AXIS_RIGHT,
LXW_CHART_AXIS_LEFT,
LXW_CHART_AXIS_TOP,
LXW_CHART_AXIS_BOTTOM
};
/**
* @brief Type/amount of data series error bar.
*/
typedef enum lxw_chart_error_bar_type {
/** Error bar type: Standard error. */
LXW_CHART_ERROR_BAR_TYPE_STD_ERROR,
/** Error bar type: Fixed value. */
LXW_CHART_ERROR_BAR_TYPE_FIXED,
/** Error bar type: Percentage. */
LXW_CHART_ERROR_BAR_TYPE_PERCENTAGE,
/** Error bar type: Standard deviation(s). */
LXW_CHART_ERROR_BAR_TYPE_STD_DEV
} lxw_chart_error_bar_type;
/**
* @brief Direction for a data series error bar.
*/
typedef enum lxw_chart_error_bar_direction {
/** Error bar extends in both directions. The default. */
LXW_CHART_ERROR_BAR_DIR_BOTH,
/** Error bar extends in positive direction. */
LXW_CHART_ERROR_BAR_DIR_PLUS,
/** Error bar extends in negative direction. */
LXW_CHART_ERROR_BAR_DIR_MINUS
} lxw_chart_error_bar_direction;
/**
* @brief End cap styles for a data series error bar.
*/
typedef enum lxw_chart_error_bar_cap {
/** Flat end cap. The default. */
LXW_CHART_ERROR_BAR_END_CAP,
/** No end cap. */
LXW_CHART_ERROR_BAR_NO_CAP
} lxw_chart_error_bar_cap;
typedef struct lxw_series_error_bar {
uint8_t type;
uint8_t direction;
uint8_t endcap;
uint8_t has_value;
double value;
lxw_chart_line *line;
} lxw_series_error_bar;
/**
* @brief Struct to represent an Excel chart data series.
*
@ -812,6 +865,11 @@ typedef struct lxw_chart_series {
char *label_num_format;
lxw_chart_font *label_font;
lxw_series_error_bar x_error_bar;
lxw_series_error_bar y_error_bar;
uint8_t has_x_error_bar;
uint8_t has_y_error_bar;
STAILQ_ENTRY (lxw_chart_series) list_pointers;
} lxw_chart_series;
@ -1699,6 +1757,9 @@ void chart_series_set_labels_num_format(lxw_chart_series *series,
void chart_series_set_labels_font(lxw_chart_series *series,
lxw_chart_font *font);
void chart_series_set_y_error_bars_line(lxw_chart_series *series,
lxw_chart_line *line);
/**
* @brief Set the name caption of the an axis.
*

View file

@ -110,6 +110,9 @@ _chart_series_free(lxw_chart_series *series)
_chart_free_range(series->title.range);
_chart_free_points(series);
free(series->x_error_bar.line);
free(series->y_error_bar.line);
free(series);
}
@ -2239,6 +2242,161 @@ _chart_write_d_lbls(lxw_chart *self, lxw_chart_series *series)
lxw_xml_end_tag(self->file, "c:dLbls");
}
/*
* Write the <c:val> element.
*/
STATIC void
_chart_write_error_val(lxw_chart *self, double value)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_DBL("val", value);
lxw_xml_empty_tag(self->file, "c:val", &attributes);
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <c:noEndCap> element.
*/
STATIC void
_chart_write_no_end_cap(lxw_chart *self)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
LXW_PUSH_ATTRIBUTES_STR("val", "1");
lxw_xml_empty_tag(self->file, "c:noEndCap", &attributes);
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <c:errValType> element.
*/
STATIC void
_chart_write_err_val_type(lxw_chart *self, uint8_t type)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
if (type == LXW_CHART_ERROR_BAR_TYPE_FIXED)
LXW_PUSH_ATTRIBUTES_STR("val", "fixedVal");
else if (type == LXW_CHART_ERROR_BAR_TYPE_PERCENTAGE)
LXW_PUSH_ATTRIBUTES_STR("val", "percentage");
else if (type == LXW_CHART_ERROR_BAR_TYPE_STD_DEV)
LXW_PUSH_ATTRIBUTES_STR("val", "stdDev");
else
LXW_PUSH_ATTRIBUTES_STR("val", "stdErr");
lxw_xml_empty_tag(self->file, "c:errValType", &attributes);
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <c:errBarType> element.
*/
STATIC void
_chart_write_err_bar_type(lxw_chart *self, uint8_t direction)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
if (direction == LXW_CHART_ERROR_BAR_DIR_PLUS)
LXW_PUSH_ATTRIBUTES_STR("val", "plus");
else if (direction == LXW_CHART_ERROR_BAR_DIR_MINUS)
LXW_PUSH_ATTRIBUTES_STR("val", "minus");
else
LXW_PUSH_ATTRIBUTES_STR("val", "both");
lxw_xml_empty_tag(self->file, "c:errBarType", &attributes);
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <c:errDir> element.
*/
STATIC void
_chart_write_err_dir(lxw_chart *self, uint8_t vertical)
{
struct xml_attribute_list attributes;
struct xml_attribute *attribute;
LXW_INIT_ATTRIBUTES();
if (vertical)
LXW_PUSH_ATTRIBUTES_STR("val", "y");
else
LXW_PUSH_ATTRIBUTES_STR("val", "n");
lxw_xml_empty_tag(self->file, "c:errDir", &attributes);
LXW_FREE_ATTRIBUTES();
}
/*
* Write the <c:errBars> element.
*/
STATIC void
_chart_write_err_bars(lxw_chart *self, uint8_t vertical,
lxw_chart_series *series)
{
lxw_series_error_bar *error_bar;
if (vertical)
error_bar = &series->y_error_bar;
else
error_bar = &series->x_error_bar;
lxw_xml_start_tag(self->file, "c:errBars", NULL);
/* Write the c:errDir element. */
_chart_write_err_dir(self, vertical);
/* Write the c:errBarType element. */
_chart_write_err_bar_type(self, error_bar->direction);
/* Write the c:errValType element. */
_chart_write_err_val_type(self, error_bar->type);
/* Write the c:noEndCap element. */
if (error_bar->endcap == LXW_CHART_ERROR_BAR_NO_CAP)
_chart_write_no_end_cap(self);
/* Write the c:val element. */
if (error_bar->has_value)
_chart_write_error_val(self, error_bar->value);
/* Write the c:spPr element. */
_chart_write_sp_pr(self, error_bar->line, NULL, NULL);
lxw_xml_end_tag(self->file, "c:errBars");
}
/*
* Write the <c:errBars> element.
*/
STATIC void
_chart_write_error_bars(lxw_chart *self, lxw_chart_series *series)
{
if (series->has_x_error_bar)
_chart_write_err_bars(self, LXW_FALSE, series);
if (series->has_y_error_bar)
_chart_write_err_bars(self, LXW_TRUE, series);
}
/*
* Write the <c:size> element.
*/
@ -2450,6 +2608,9 @@ _chart_write_ser(lxw_chart *self, lxw_chart_series *series)
/* Write the c:dLbls element. */
_chart_write_d_lbls(self, series);
/* Write the c:errBars element. */
_chart_write_error_bars(self, series);
/* Write the c:cat element. */
_chart_write_cat(self, series);
@ -2495,6 +2656,9 @@ _chart_write_xval_ser(lxw_chart *self, lxw_chart_series *series)
/* Write the c:dLbls element. */
_chart_write_d_lbls(self, series);
/* Write the c:errBars element. */
_chart_write_error_bars(self, series);
/* Write the c:xVal element. */
_chart_write_x_val(self, series);
@ -4947,6 +5111,22 @@ chart_series_set_labels_font(lxw_chart_series *series, lxw_chart_font *font)
series->label_font = _chart_convert_font_args(font);
}
/*
* Set a line type for a series marker.
*/
void
chart_series_set_y_error_bars_line(lxw_chart_series *series,
lxw_chart_line *line)
{
if (!line)
return;
/* Free any previously allocated resource. */
free(series->y_error_bar.line);
series->y_error_bar.line = _chart_convert_line_args(line);
}
/*
* Set an axis caption.
*/

View file

@ -0,0 +1,51 @@
/*****************************************************************************
* Test cases for libxlsxwriter.
*
* Test to compare output against Excel files.
*
* Copyright 2014-2017, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = new_workbook("test_chart_errorbars01.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_LINE);
/* For testing, copy the randomly generated axis ids in the target file. */
chart->axis_id_1 = 63386752;
chart->axis_id_2 = 63388288;
uint8_t data[5][3] = {
{1, 2, 3},
{2, 4, 6},
{3, 6, 9},
{4, 8, 12},
{5, 10, 15}
};
int row, col;
for (row = 0; row < 5; row++)
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
lxw_chart_series *series1 = chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$B$1:$B$5"
);
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$C$1:$C$5"
);
series1->has_y_error_bar = 1;
worksheet_insert_chart(worksheet, CELL("E9"), chart);
return workbook_close(workbook);
}

View file

@ -0,0 +1,62 @@
/*****************************************************************************
* Test cases for libxlsxwriter.
*
* Test to compare output against Excel files.
*
* Copyright 2014-2017, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = new_workbook("test_chart_errorbars02.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_LINE);
/* For testing, copy the randomly generated axis ids in the target file. */
chart->axis_id_1 = 63385984;
chart->axis_id_2 = 63387904;
uint8_t data[5][3] = {
{1, 2, 3},
{2, 4, 6},
{3, 6, 9},
{4, 8, 12},
{5, 10, 15}
};
int row, col;
for (row = 0; row < 5; row++)
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
lxw_chart_series *series1 = chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$B$1:$B$5"
);
lxw_chart_series *series2 = chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$C$1:$C$5"
);
series1->has_y_error_bar = 1;
series1->y_error_bar.type = LXW_CHART_ERROR_BAR_TYPE_FIXED;
series1->y_error_bar.direction = LXW_CHART_ERROR_BAR_DIR_MINUS;
series1->y_error_bar.endcap = LXW_CHART_ERROR_BAR_NO_CAP;
series1->y_error_bar.has_value = 1;
series1->y_error_bar.value = 2;
series2->has_y_error_bar = 1;
series2->y_error_bar.type = LXW_CHART_ERROR_BAR_TYPE_PERCENTAGE;
series2->y_error_bar.direction = LXW_CHART_ERROR_BAR_DIR_PLUS;
series2->y_error_bar.endcap = LXW_CHART_ERROR_BAR_END_CAP;
series2->y_error_bar.has_value = 1;
series2->y_error_bar.value = 5;
worksheet_insert_chart(worksheet, CELL("E9"), chart);
return workbook_close(workbook);
}

View file

@ -0,0 +1,56 @@
/*****************************************************************************
* Test cases for libxlsxwriter.
*
* Test to compare output against Excel files.
*
* Copyright 2014-2017, John McNamara, jmcnamara@cpan.org
*
*/
#include "xlsxwriter.h"
int main() {
lxw_workbook *workbook = new_workbook("test_chart_errorbars03.xlsx");
lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);
lxw_chart *chart = workbook_add_chart(workbook, LXW_CHART_LINE);
/* For testing, copy the randomly generated axis ids in the target file. */
chart->axis_id_1 = 52288896;
chart->axis_id_2 = 53605504;
uint8_t data[5][3] = {
{1, 2, 3},
{2, 4, 6},
{3, 6, 9},
{4, 8, 12},
{5, 10, 15}
};
int row, col;
for (row = 0; row < 5; row++)
for (col = 0; col < 3; col++)
worksheet_write_number(worksheet, row, col, data[row][col], NULL);
lxw_chart_series *series1 = chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$B$1:$B$5"
);
chart_add_series(chart,
"=Sheet1!$A$1:$A$5",
"=Sheet1!$C$1:$C$5"
);
lxw_chart_line line = {.color = LXW_COLOR_RED,
.dash_type = LXW_CHART_LINE_DASH_ROUND_DOT};
series1->has_y_error_bar = 1;
series1->y_error_bar.type = LXW_CHART_ERROR_BAR_TYPE_STD_ERROR;
chart_series_set_y_error_bars_line(series1, &line);
worksheet_insert_chart(worksheet, CELL("E9"), chart);
return workbook_close(workbook);
}

View file

@ -0,0 +1,23 @@
###############################################################################
#
# Tests for libxlsxwriter.
#
# Copyright 2014-2017, John McNamara, jmcnamara@cpan.org
#
import base_test_class
class TestCompareXLSXFiles(base_test_class.XLSXBaseTest):
"""
Test file created with libxlsxwriter against a file created by Excel.
"""
def test_chart_errorbars01(self):
self.run_exe_test('test_chart_errorbars01')
def test_chart_errorbars02(self):
self.run_exe_test('test_chart_errorbars02')
def test_chart_errorbars03(self):
self.run_exe_test('test_chart_errorbars03')

Binary file not shown.

Binary file not shown.

Binary file not shown.