Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(issue 900): implemented Range.add_plot() and add_table() #926

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions doc/source/changes/version_0_33.rst.inc
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ Miscellaneous improvements

* implemented :py:obj:`Axis.min()` and :py:obj:`Axis.max()` methods (closes :issue:`874`).

* implemented the :py:obj:`Range.add_plot()` method that allows to create graphs when using
`open_excel()` (closes :issue:`900`).

* implemented the :py:obj:`Range.add_table()` method.

Fixes
^^^^^

Expand Down
74 changes: 68 additions & 6 deletions larray/inout/xw_excel.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import os
import atexit

from typing import Union, Dict, Callable, Any

import numpy as np
try:
import xlwings as xw
Expand Down Expand Up @@ -618,6 +620,69 @@ def load(self, header=True, convert_float=True, nb_axes=None, index_col=None, fi
else:
return Array(list_data)

def add_table(self, array: Array):
self.xw_range.value = array.dump()

def add_plot(self, data_source: str, width: int=427, height: int=230, title: str=None, template: str=None,
min_y: Union[int, float]=None, max_y: Union[int, float]=None,
xticks_spacing: Union[int, float]=None, customize_func: Callable=None,
customize_kwargs: Dict[str, str]=None) -> Any:
from xlwings.constants import LegendPosition, ChartType, RowCol, AxisType, Constants, Direction
if customize_func is not None and not callable(customize_func):
raise TypeError(f"Expected a function for the argument 'customize_func'. "
f"Got object of type {type(customize_func).__name__} instead.")
if template is not None and not os.path.isfile(template):
raise ValueError(f"Could not find template file {template}")
title = str(title) if title is not None else None
sheet = self.sheet.xw_sheet.api
data_range = sheet.Range(data_source)
top_left_cell = data_range.Cells(1, 1)
# expand if current range is one cell
if data_range.Count == 1:
bottom_left_cell = data_range.End(Direction.xlDown)
bottom_right_cell = bottom_left_cell.End(Direction.xlToRight)
data_range = sheet.Range(top_left_cell, bottom_right_cell)
# horrible hack to make sure that Excel will consider the first column as the xticks
top_left_cell_value = top_left_cell.Value
top_left_cell.Value = ''
# start chart
sheet_charts = sheet.ChartObjects()
range_chart = self.xw_range.api
left, top = range_chart.Left, range_chart.Top
obj = sheet_charts.Add(left, top, width, height)
obj_chart = obj.Chart
obj_chart.SetSourceData(data_range)
obj_chart.ChartType = ChartType.xlLine
# title
if title is not None:
obj_chart.HasTitle = True
obj_chart.ChartTitle.Caption = title
# legend
obj_chart.Legend.Position = LegendPosition.xlLegendPositionBottom
# template
if template is not None:
obj_chart.ApplyChartTemplate(template)
# min - max on Y axis
if min_y is not None:
obj_chart.Axes(AxisType.xlValue).MinimumScale = min_y
if max_y is not None:
obj_chart.Axes(AxisType.xlValue).MaximumScale = max_y
# xticks_spacing
if xticks_spacing is not None:
obj_chart.Axes(AxisType.xlCategory).TickLabelSpacing = xticks_spacing
obj_chart.Axes(AxisType.xlCategory).TickMarkSpacing = xticks_spacing
obj_chart.Axes(AxisType.xlCategory).TickLabelPosition = Constants.xlLow
# user's function (to apply on remaining kwargs)
if customize_func is not None:
customize_func(obj_chart, **customize_kwargs)
# flagflip
nb_xticks = data_range.Rows.Count - 1
nb_series = data_range.Columns.Count - 1
if nb_series > 1 and nb_xticks == 1:
obj_chart.PlotBy = RowCol.xlRows
# see above
top_left_cell.Value = top_left_cell_value
return obj_chart

# XXX: deprecate this function?
def open_excel(filepath=None, overwrite_file=False, visible=None, silent=None, app=None, load_addins=None):
Expand Down Expand Up @@ -744,18 +809,15 @@ def open_excel(filepath=None, overwrite_file=False, visible=None, silent=None, a

Examples
--------
>>> arr = ndtest((3, 3))
>>> arr
a\b b0 b1 b2
a0 0 1 2
a1 3 4 5
a2 6 7 8
>>> arr, arr2 = ndtest((3, 3)), ndtest((2, 2))

create a new Excel file and save an array

>>> # to create a new Excel file, argument overwrite_file must be set to True
>>> with open_excel('excel_file.xlsx', overwrite_file=True) as wb: # doctest: +SKIP
... wb['arr'] = arr.dump()
... wb['arr']['A6'].add_table()
gdementen marked this conversation as resolved.
Show resolved Hide resolved
... wb['arr']['G1'].add_plot('A1', title='simple graph')
gdementen marked this conversation as resolved.
Show resolved Hide resolved
... wb.save()

read array from an Excel file
Expand Down
26 changes: 26 additions & 0 deletions larray/tests/test_excel.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,32 @@ def test_repr(self):
1 3 4 5"""


@needs_xlwings
def test_add_table():
arr = ndtest((3, 3))

with open_excel(filepath='test_add_table.xlsx', visible=False, overwrite_file=True) as wb:
sheet = wb[0]
sheet["B2"].add_table(arr)
wb.save()


@needs_xlwings
def test_add_plot():
demo = load_example_data('demography_eurostat')
population = demo.population
population_be = population['Belgium']
population_be_nan = population_be.astype(float)
population_be_nan[2013] = nan

with open_excel(filepath='test_add_plot.xlsx', visible=False, overwrite_file=True) as wb:
sheet = wb[0]
sheet["B2"] = population_be.dump()
sheet["B8"].add_plot("B2")
sheet["L8"].add_plot("B2:F4")
wb.save()


# ================ #
# Test ExcelReport #
# ================ #
Expand Down