3

I have about 20 columns of data, each ~20,000 rows. All calculations are done in Matlab, and then charts are created.

Unfortunately, I need the whole output to be in an Excel File, including several editable charts. Using xlswrite I created the Excel file, now I am struggling to create the charts the way I want.

I tried using this code, but got several errors and wasn't able to choose the X and Y Values of the chart.

 Excel = actxserver('Excel.Application');  
 WB = Excel.workbooks.Open('D:\...\Test.xlsx');
 Charts = WB.Charts; 
 Chart = invoke(Charts,'Add');
 invoke(Chart, 'SetSourceData', Excel.Range('Sheet1!$B$2:$B$16')); %% here an error occurs 

The error:

Error using COM.Excel_Application/Range. Object returned error code: 0x800A03EC"

I have not been able to find any solutions for adding charts through the ActiveX object in Matlab. Any posts I have found on this topic were outdated or unhelpful.


Summary, my question is:

  • How can I insert charts in Excel using Matlab (using a general, up to date ActiveX code structure).
  • How can I select the columns for the XValue and the YValues (Range)
  • How can I get access to chart titles, axes, line appearance and legend

I'm using Excel 2016 and Matlab R2017a.


Edit:

during this week I developed a own solution, that still doesn't work perfectly but is close to what I want. Could you have a look on this code too please:

Important is only chart1. I wanted to set the XValue to column B and the YValues to column H - P of the sheet1 (Tabelle1).

%%%%% general Code to insert a Chart in Excel using Matlab %%%%%
%% start Excel and open Workbook
excel = actxserver('Excel.Application');
wb = excel.Workbooks.Open('C:\...\Test.xlsx');
%% makes the created sheet visible
excel.Visible = true;
%% add 1. Chart
chart1 = wb.Charts.Add;
%% set source data 
chart1.SetSourceData(wb.Worksheets.Item('Tabelle1').Range('$B:$B, $H:$P')); % 'Tabelle1' is the german equal to sheet1, my excel is german
%% Name chart sheet
chart1.Name = '1. TestChart';
%% Set chart title,  see https://msdn.microsoft.com/en-us/library/office/ff196832.aspx
chart1.HasTitle = true;
chart1.ChartTitle.Text = 'Test Title';
%% Set chart types,  see https://msdn.microsoft.com/en-us/library/office/ff837417.aspx
chart1.ChartType = 'xlXYScatterSmoothNoMarkers';
%% Set chart legend, see https://msdn.microsoft.com/en-us/library/office/ff821884.aspx
chart1.HasLegend = true;
%% Set Axes Titles
chart1.Axes(1).HasTitle = true;
chart1.Axes(1).AxisTitle.Text = 'Time [s]'; % XAxes
chart1.Axes(2).HasTitle = true;
chart1.Axes(2).AxisTitle.Text = 'Temperature[°C]'; %YAxes
%% add 2nd chart
chart2 = wb.Charts.Add([], chart1); %place after chart1
chart2.SetSourceData(wb.Worksheets.Item('Tabelle1').Range('$B:$B, $Q:$Q'));
% ... same procedure as above

%% use to quit all open Excel processes
% excel.Quit;

another Error occurs:

Error using Interface.000208D8_0000_0000_C000_000000000046/Range

Error: Object returned error code: 0x800A03EC

Error in CodeTestmy (line 13) chart1.SetSourceData(wb.Worksheets.Item('Tabelle1').Range('$B:$B, $H:$P'));


for now this question is nearly answered. For further bug related answers have a look at a related question

Community
  • 1
  • 1
Kilian Weber
  • 160
  • 1
  • 8
  • A good solution for this would be to create a VBA macro *within* Excel, which is very easy to call from Matlab. Creating charts exactly how you like is well documented in Excel VBA – Wolfie Jun 13 '17 at 13:09
  • I have no experiance with VBA macros in Excel. Can you explain your idea a little bit more? A have a running Matlab GUI Programm and I also have a working export ro excel button,wich is missing only the charts. I don't want to spend any more time in Excel. It should be a one click solution via the Matlab GUI. Customers need the editable Excel File only ... – Kilian Weber Jun 13 '17 at 13:18
  • If you have a template Excel file which the data goes into, then open it up, press Alt + F11 to open the VBA editor. Go to Insert > Module, and create a new sub. [Here are some Microsoft docs](https://msdn.microsoft.com/en-us/library/office/aa203725(v=office.11).aspx) for then creating a chart in VBA. This can be called from your ActiveX object using `ExcelObj.Run('MyMacroName', parameter1, parameter2, ...)`. If this sounds like a viable option then I can provide more details in an answer. If not then I'm not sure how to do it directly from Matlab so will keep an eye on this! – Wolfie Jun 13 '17 at 13:25
  • Thank you for your help! I'll keep that in mind and come back to you when Im not able to find a direct solution. – Kilian Weber Jun 13 '17 at 13:31

1 Answers1

0

As I suggested in the comments, a better documented method for doing this would be to use VBA. As it happens, the VBA documentation can actually be used to take most of the guesswork out of interacting with the COM object directly from Matlab, as the syntax is similar.

Here is some updated Matlab code which achieves your three points. I have included links to the MSDN documentation where appropriate:

% Start Excel and open workbook
Excel = actxserver('Excel.Application');  
WB = Excel.Workbooks.Open('C:\...\test.xlsx');
% Show the workbook
Excel.visible = 1;
% Add chart
Chart = invoke(WB.Charts,'Add');
% Get Sheet object
SheetObj = Excel.Worksheets.get('Item', 'Sheet1');
% Name chart sheet
Chart.Name = 'TestChart';

% Set source data range of chart
% X and Y data can also be set to Matlab arrays, by Srs.Values and Srs.XValues, ensuring equal length
  % Y data
Srs = Chart.SeriesCollection.Add(SheetObj.Range('B2:B16'));
  % X data, could be a Matlab array of correct length
Srs.XValues = SheetObj.Range('A2:A16');
  % Series name
Srs.Name = 'Test Series';

% For chart types,  see https://msdn.microsoft.com/en-us/library/office/ff837417.aspx
Chart.ChartType = 'xlXYScatterSmooth'; 
% Set chart title,  see https://msdn.microsoft.com/en-us/library/office/ff196832.aspx
Chart.HasTitle = true;
Chart.ChartTitle.Text = 'Test Title';
% Set chart legend, see https://msdn.microsoft.com/en-us/library/office/ff821884.aspx
Chart.HasLegend = true;

Your error:

You were encountering an error because you were trying to access the Range object of the Excel application. This doesn't exist! All Range objects belong to a Sheet object, which is what I retrieve first in the above code.


Creating many series:

You say you have many columns of data, here is a way to include them through a loop. It also finds the last used row in each column.

% ... CREATE WORKBOOK / CHART AS BEFORE ...
%
Chart.Name = 'TestChart';

% Set source data range of chart, do X and Y data for each series    
columns = 2:4;
colnames = {'xdata', 'my series 1', 'my series 2', 'my series 3'};
for col = columns
      % Get Excel column *letter* from column *number*
    colchar = strrep([char(96+floor((col-1)/26)) char(97+rem(col-1,26))],char(96),'');
      % Last row of data, see https://msdn.microsoft.com/en-us/library/office/ff839539.aspx
      % Data must be contiguous (no gaps / blank cells)
    lastrow = num2str(SheetObj.Range([colchar, '2']).End('xlDown').Row);
      % Y data, creating range strings by concatenation of column character and row number
    Srs = Chart.SeriesCollection.Add(SheetObj.Range([colchar, '2:', colchar, lastrow]));
      % X data, same approach is used for last row, but only column = 1 = "A" 
    Srs.XValues = SheetObj.Range(['A2:A', lastrow]);
     % Set column name, to use the first row do
     % Srs.Name = SheetObj.Range([colchar, '1']);
    Srs.Name = colnames{col};
end

Chart.ChartType = 'xlXYScatterSmooth'; 
%
% ... TITLE / LEGEND AS BEFORE ...

Output chart sheet and input "Sheet1":

example


Edit:

In the above, I loop over the column numbers to populate the y data. If you know the column letter then you can just loop over colchar instead of creating it.

% There are shortcuts to creating a cell array of consecutive letters, 
% Like columnletters = cellstr(('H':'J')');
for colchar = {'H', 'I', 'J'};
    % ... same as the above for loop, but don't need to get colchar from col
    lastrow = num2str(SheetObj.Range([colchar, '2']).End('xlDown').Row);
    Srs = Chart.SeriesCollection.Add(SheetObj.Range([colchar, '2:', colchar, lastrow]));
    Srs.XValues = SheetObj.Range(['A2:A', lastrow]);
    Srs.Name = SheetObj.Range([colchar, '1']);

end
Wolfie
  • 27,562
  • 7
  • 28
  • 55
  • Thank you! But I'm still concerned about the X-Values. As in this code the `'B2:B16'`only sets the Y-Values and automaticly takes the first column of the skript as the X-Values. – Kilian Weber Jun 14 '17 at 06:29
  • and a second question: Do you know a simple way to slesect the Y-Value-Data like this: `('Range', 'B2:Bend')` (determine the size of the column) (maybe wihtout using ActiveX/COM or xlsread)? – Kilian Weber Jun 14 '17 at 06:58
  • What sort of chart are you trying to make? – Wolfie Jun 14 '17 at 07:14
  • Several `'xlXYScatterSmooth'` Charts with up to 20 columns of Y-Values. Each up to 20.000 rows long. The size varies with the different data. The first Coulmn is the time [s] , the second the time [h] I want to plot over the second solumn as X-Value. The Code got to be useable for all sizes of data (as far as Excel supports it) and the X-Values should be choosable freely if possible or be set to the second column for all. – Kilian Weber Jun 14 '17 at 07:40
  • Please see my edit, I have simplified the `Range` accessing, as you didn't need to use `get`. I've also shown you how to specify `XValues`, a neater way to create a new series, using that to create many series, and one method to get the last row in a given column. If this answers your question, please consider marking this answer as accepted! – Wolfie Jun 14 '17 at 08:31
  • There is my Code so far... it still doesn't work perfectly and I don't understand all of it: here my code: [link](https://drive.google.com/open?id=0B9dePeiwtdEOR3JQTmRYemU2QXc) here is the the file I need to work with: [link](https://drive.google.com/open?id=0B9dePeiwtdEOTXFBZHNsTjFvMXM) I only want to use sheet 1 (Tabelle 1) time [h] -column is the XValue in the first chart I want to have column H - P as YValues in one Chart int he second only Q always use every row and the header as Name – Kilian Weber Jun 14 '17 at 11:38
  • **Concerning your code alone:** 1. My first row is a header row, can I just use them somehow as names instead of `colnames = {'xdata', 'my series 1', 'my series 2', 'my series 3'};` 2. Can you make it more clear, where I choose the YValues? Like `YValues = (sheet1,B2:B,D2:D)` (set row B and D of sheet 1 as YValues) – Kilian Weber Jun 14 '17 at 11:56
  • I cannot see that link due to my web filters, **please just edit the relevant parts of code into your question above**. I have updated my code to include **1.** how to use column headers as legend (see commented line in loop example). **2.** How to set your y values from column letters (see edit example at bottom). The y values are chosen on the line `Srs = Chart.SeriesCollection.Add(...)`, where the range string `[colchar, '2:', colchar, lastrow]` equals something like `'B2:B16'`. Please set up the example sheet as pictured and try tweaking things, that way you will learn... – Wolfie Jun 14 '17 at 13:22
  • I'm still working on getting your code to work on my sheets... During that time I delveloped my own code, ***as edited into my question above.*** Could you have a look ar that too please. – Kilian Weber Jun 19 '17 at 06:11
  • You have got too many quote marks, `Range(''$B:$B, $H:$P'')); ` should look like `Range('$B:$B, $H:$P'));`... – Wolfie Jun 19 '17 at 07:26
  • sry, I copied it wrong.... I used `Range('$B:$B, $H:$P'));` ... Still the same error – Kilian Weber Jun 19 '17 at 08:06
  • Why don't you assign the sheet to a variable like I clearly demonstrate (`SheetObj = Excel.Worksheets.get('Item', 'Sheet1');`)? Then accessing the ranges is simple as shown... – Wolfie Jun 19 '17 at 08:18
  • I was working with your code and it worked so far, still it was a little buggy. When editing the .xlsx file in Excel itself, saving and closing it and than returning to matlab and working with this file again a bug occured. The Chart not only had the "my series n" lines but also for each column a line, that I didn't adress... – Kilian Weber Jun 19 '17 at 11:54
  • It's like playing the lottery... Sometimes it gets it right... I use the same file again and it doesn't. It adds all existig columns too. (despite adding the ones I chose and named) – Kilian Weber Jun 19 '17 at 12:16
  • @Kilian, I think my answer fits your original question, it's not ideal to keep editing things and expanding the scope etc... I hope that if you found this answer helpful for the above question that you mark it as accepted. If you have more issues then I encourage you to post a new question with a specific issue so it's nice and clear. This will benefit both yourself and future visitors :) – Wolfie Jun 19 '17 at 12:33
  • 1
    Thank you @Wolfie ... I accepted your answer... Further spoecific questions on your answer are [here](https://stackoverflow.com/questions/44632268/unsolved-bug-in-creating-charts-in-excel-freely-using-matlab) and to my own answer option [here](https://stackoverflow.com/questions/44636144/error-while-choosing-range-for-excel-chart-using-matlab) I'm looking forward to your help to finally get this done – Kilian Weber Jun 20 '17 at 05:42
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/147126/discussion-between-kilian-weber-and-wolfie). – Kilian Weber Jun 20 '17 at 07:18