I am trying to perform a cell merge on an Excel file from Matlab. I don't know anything about this, but tried to use some code I found elsewhere online and adapted it a bit:
Excel = actxserver('Excel.Application');
Workbooks = Excel.Workbooks;
Excel.Visible = 0;
Workbook = Excel.Workbooks.Open('C:\Users\path&filename*.xlsx');
for k=1:length(B)-1
rng = [ExcelCol((k-1)*MaxH+2),num2str(1),':',ExcelCol(k*MaxH+2),num2str(1)];
procrng = [rng{:}];
Range = Excel.Range(procrng);
Range.Select;
Range.MergeCells = True;
Range.HorizontalAlignment = xlCenter;
end
ExcelCol is a user-defined function I found online that converts column number to Excel alphabet notation. It works - no doubt about it.
But when I run & step through the code, I get an error at Range.MergeCells = True. I get: "Undefined function or variable True'.
Can you please help?
Use Range.MergeCells = true or Range.MergeCells = 1. True with a capital T is not a Matlab value.
You'll also need to use Range.HorizontalAlignment = -4108 instead of xlCenter because the ActiveX constants are not exposed to Matlab. There's no good way to get them programmatically; you need to build your own constant table, or do some deep digging with introspection.
You can also use .NET instead of ActiveX to script this Excel stuff from Matlab, and it exposes symbolic constants like xlCenter so you can use them (you just have to package-qualify them). But the using Excel automation through the Matlab/.NET interface has some nasty edge-case issues with moving 1-D vectors and having to explicitly cast objects to different interfaces. I can't recommend one over the other at this point.
Related
I'm using MATLAB 2017a and have been using xlswrite in the past to perform this operation. The problem I ran into was with execution speed and I was looking for a better way. So, I decided to use actxserver and write data using get(obj) from MATLAB and Range.Value from ActiveX. Here's what the code looks like:
e = actxserver('Excel.Application);
eWorkbook = e.Workbooks.Add;
e.Visible = 1;
eSheets = e.ActiveWorkbook.Sheets;
eSheet1 = eSheets.get('Item',1);
eSheet1.Activate;
A = ["Str1";"Str2";"Str3";];
eActivesheetRange = get(e.Activesheet, 'Range', 'A1:A3');
eActivesheetRange.Value = A;
This inocuous bit of code does not execute, nor does it throw a warning or error message.. Nothin'. In my mind, the eActivesheetRange evaluates to: Range("A1:A3") on the ActiveX side. Interestingly, if I replace
A = ["Str1";"Str2";"Str3";];
with
A = char(["Str1";"Str2";"Str3";]);
then the program writes the A char array to each cell in the eActivesheetRange Range.
Is there a way to call cells() using the MATLAB Range.Value connection? Would cells().Value be able to solve this problem?
I don't think writing to Excel using ActiveX is able to handle string types properly. In this case, you can make it work by simply converting your string array into a cell array of character vectors using cellstr. Changing your last line of code to the following works for me (in R2016b):
eActivesheetRange.Value = cellstr(A);
Replacing the last two lines with the following also works:
e.Activesheet.Range('A1:A3').Value = cellstr(A);
The solution to this is of course, a for loop.
alphacolumn=char(97:117);
% iterate through data array
for i=1:21
str=string(alphacolumn(i))+2;
str2=string(alphacolumn(i))+202;
write1=char(str+":"+str2);
if ~isreal(tsc{i,1})
T = (tsc{i,1});
for j = 1:length(T)
rrange = xl.ActiveWorkbook.Activesheet.Range(char(string(alphacolumn(i)) + string(j+1)));
xlcompatiblestring1 = char(string(T(j,:,:)));
rrange.Value= xlcompatiblestring1;
end
else
tsci=tsc{i,1};
% write data to xl target file
%xlswrite(xlfilepath,tsci,write1);
xlActivesheetRange = get(xl.Activesheet,'Range',write1);
xlActivesheetRange.Value = tsci;
end
end
I need little help again.
I have an excel file and I have to do some formatting for further processing.
I stuck at one point:
find a word / or string
get back the number how often it is used in the excel
use this number to put it into a loop
I hope this is possible with VBscript, because I cannot use VBA.
This is how the code looks like, but I doesn't work
Dim objExcel
Set Excel = CreateObject("Excel.Application")
Excel.Visible = True
excel.workbooks.open "C:\Users\............."
excel.Rows("1:34").Select
excel.Selection.Delete
excel.Columns("A:A").Select
excel.Selection.Delete
excel.Range("A1").Select
Number = excel.countIf "A:A", "Ent.Date"
for i = 1 to Number
excel.Cells.Find("Ent.Date").Activate
excel.Activecell.Offset(-1,0).Select
excel.Activecell.resize(RowSize +3).EntireRow.Select
excel.Selection.Delete
next
Please help me.
Thanks
You are close. CountIf() is a method of the WorksheetFunction class in your Excel application. So:
Number = Excel.WorksheetFunction.CountIf("A:A", "Ent.Date")
Unrelated. All of this .Active .Select is unnecessary. Humans Select and Activate, but your script doesn't need to.
Instead:
Set Excel = CreateObject("Excel.Application")
Excel.Visible = True
excel.workbooks.open "C:\Users\............."
excel.Rows("1:34").Delete
excel.Columns("A:A").Delete
Number = excel.WorksheetFunction.CountIf(Excel.Range("A:A"), "Ent.Date")
for i = 1 to Number
excel.Cells.Find("Ent.Date").Offset(-1,0).resize(RowSize +3).EntireRow.Delete
next
Also (and this might be more preference) instead of looping with For you could instead us a While loop:
Set Excel = CreateObject("Excel.Application")
Excel.Visible = True
excel.workbooks.open "C:\Users\............."
excel.Rows("1:34").Delete
excel.Columns("A:A").Delete
Do While excel.WorksheetFunction.CountIf(Excel.Range("A:A"), "Ent.Date") >= 1
excel.Cells.Find("Ent.Date").Offset(-1,0).resize(RowSize +3).EntireRow.Delete
Loop
That causes a little extra overhead since it runs that countif on each loop, but it feels more succinct and less potential for error since you seem to be resizing the range returned by Find() to be more rows and then deleting them all... which means you might be picking off rows that also contain your search criteria, which means you are looping too much in your For Loop. But... perhaps your sheet is set up in such a way that this condition doesn't happen.
I tried to create a file with decimal numbers on cells (F1:F4), but when i am going to insert a formula to the cell, it's show it like a string.
Code:
s1("F5").Formula = "=SUMME(F1:F4)"
s1("F5").Value = s1("F5").Formula
i tried:
s1("F5").Formula = "=SUMME(F1:F4)"
s1("F5").Formatting.HiddenFormula = False
s1("F5").Value = s1("F5").Formula
i tried:
s1("F5").Formula = "=SUMME(F1:F4)"
The result its the same, on cell F5, =SUMME(F1:F4)
always on string format.
Note : s1 = Sheet1
Try this:
s1.Cells("F5").Formula = "=SUM(F1:F4)"
You didn't set the range of the sheet. Also changed the SUMME to SUM, unless you have a User Defined Function (UDF) which is called SUMME or you are using Excel in a language where Summe = Sum.
EDIT:
After looking into the library you are using, I would try the following:
s1["F5"].Value = "=SUMME(F1:F4)";
I found the solution. With that library, it's not possible.
I used another library just to make the formula. Nix library it's good to add data to the cells.
I set out to write a simple function to determine the length of a string in points. Having googled around I decided to avoid the font metrics problem by having excel do the work for me.
Here is the code.
Option Explicit
Function txtWidthPts(MyText As String) As Integer
'A cell on a working worksheet has been named "WidthTest" for easy reference & to ensure data is not overwritten.
'set WidthTest word wrapping off so that strings placed in there aren't wrapped
Application.ScreenUpdating = False
With [WidthTest]
.WrapText = False
.Value = MyText
'autofit WidthTest
.Columns.AutoFit
'get the width of the column
txtWidthPts = .Width
.ClearContents
End With
End Function
I tested the function by placing it in a cell on a working worksheet thus:
=txtWidthPts("Test123")
When I have this working I will be using it in code not as a worksheet function.
My problem is that the function does not throw an error and stops execution on the line:
.Value = MyText
I have placed the code and name into an empty workbook to ensure no interaction with other workbook contents / code.
I have searched extensively and tried various suggestions (DoEvents, Application.Update = False, etc, etc.) to no result.
I have cleared all breakpoints, closed and opened the workbook & restarted. I have tested with options set to Break on All Errors.
No result.
I suspect I am missing something obvious but it has me beat at the moment.
Any and all suggestions will be most welcome.
So, #YowE3K was right on the money. After fixing the error in the original code (Corrected code above) this runs fine from vba. I knew I was missing something obvious.
Curiosity sub-question: the function works as desired and indeed, as #YowE3K observed, it does not modify the Excel environment. However the result returned is dependent on it appearing to have modified the Excel environment. Seriously WTF. Just wanting to understand.
Thanks again YoWE3K.
I need to import some Excel files in MATLAB and work on them. My problem is that each Excel file has 15 sheets and I don't know how to "number" each sheet so that I can make a loop or something similar (because I need to find the average on a certain column on each sheet).
I have already tried importing the data and building a loop but MATLAB registers the sheets as chars.
Use xlsinfo to get the sheet names, then use xlsread in a loop.
[status,sheets,xlFormat] = xlsfinfo(filename);
for sheetindex=1:numel(sheets)
[num,txt,raw]=xlsread(filename,sheets{sheetindex});
data{sheetindex}=num; %keep for example the numeric data to process it later outside the loop.
end
I 've just remembered that i posted this question almost 2 years ago, and since I figured it out, I thought that posting the answer could prove useful to someone in the future.
So to recap; I needed to import a single column from 4 excel files, with each file containing 15 worksheets. The columns were of variable lengths. I figured out two ways to do this. The first one is by using the xlsread function with the following syntax.
for count_p = 1:2
a = sprintf('control_group_%d.xls',count_p);
[status,sheets,xlFormat] = xlsfinfo(a);
for sheetindex=1:numel(sheets)
[num,txt,raw]=xlsread(a,sheets{sheetindex},'','basic');
data{sheetindex}=num;
FifthCol{count_p,sheetindex} = (data{sheetindex}(:,5));
end
end
for count_p = 3:4
a = sprintf('exercise_group_%d.xls',(count_p-2));
[status,sheets,xlFormat] = xlsfinfo(a);
for sheetindex=1:numel(sheets)
[num,txt,raw]=xlsread(a,sheets{sheetindex},'','basic');
data{sheetindex}=num;
FifthCol{count_p,sheetindex} = (data{sheetindex}(:,5));
end
end
The files where obviously named control_group_1, control_group_2 etc. I used the 'basic' input in xlsread, because I only needed the raw data from the files, and it proved to be much faster than using the full functionality of the function.
The second way to import the data, and the one that i ended up using, is building your own activeX server and running a single excelapplication on it. Xlsread "opens" and "closes" an activeX server each time it's called so it's rather time consuming (using the 'basic' input does not though). The code i used is the following.
Folder=cd(pwd); %getting the working directory
d = dir('*.xls'); %finding the xls files
N_File=numel(d); % Number of files
hexcel = actxserver ('Excel.Application'); %starting the activeX server
%and running an Excel
%Application on it
hexcel.DisplayAlerts = true;
for index = 1:N_File %Looping through the workbooks(xls files)
Wrkbk = hexcel.Workbooks.Open(fullfile(pwd, d(index).name)); %VBA
%functions
WorkName = Wrkbk.Name; %getting the workbook name %&commands
display(WorkName)
Sheets=Wrkbk.Sheets; %sheets handle
ShCo(index)=Wrkbk.Sheets.Count; %counting them for use in the next loop
for j = 1:ShCo(index) %looping through each sheet
itemm = hexcel.Sheets.Item(sprintf('sheet%d',j)); %VBA commands
itemm.Activate;
robj = itemm.Columns.End(4); %getting the column i needed
numrows = robj.row; %counting to the end of the column
dat_range = ['E1:E' num2str(numrows)]; %data range
rngObj = hexcel.Range(dat_range);
xldat{index, j} = cell2mat(rngObj.Value); %getting the data in a cell
end;
end
%invoke(hexcel);
Quit(hexcel);
delete(hexcel);