I am cycling through various cells in Matlab by hand in a script (let\'s call it foo.m):
%%
%Code for cell 1
%%
%Code for cell 2
From the
In order to run a particular section from a file using command line, you can use
echodemo(filename, index of section)
MATLAB documentation
Dev-iL has provided a good answer using java etc... I will provide an alternative here which does not use java or the editor, but reads the file and evals the statements when requested.
For this to work it has a pre-requirement that the file has been saved (which I appreciate is not a pre-requisite of running cells/codeBlocks interactively).
Anyway I thought this was a "quirky" question and thought I'd add have a go at an answer.
Here is my script (cellScript.m) which contains code blocks (Note I have given each block a name after the "%%":
%Note: for this method your NOT allowed to put comments at the end of lines
%% cellA
disp ( 'running cell A' );
disp ( 'finished cell A' );
%% cellB
disp ( 'running cell B' );
disp ( 'finished cell B' );
%% cellC
disp ( 'running cell C' );
for ii=1:100
aa(ii) = randi(1);
end
% cells can have comments
disp ( 'past 1st coment' );
% cells comments can be indented
disp ( 'past indented comment' );
%{
block
comment
%}
disp ( 'past block comment' );
% cells can have comments
% multiple lines of comments
% not lined up
%
%and empty
disp ( 'past multiple lines of comments' );
disp ( 'finished cell C' );
%% cellD
disp ( 'running cell D' );
disp ( 'finished cell D' );
%% cellE
disp ( 'running cell E' );
disp ( 'finished cell E' );
I have created a class which does the job requested (I called it cellRunner.m )
classdef cellRunner < handle
properties ( SetAccess = private )
fileName
fileInfo
cellInfo
cellNames = {};
end
methods
function obj = cellRunner ( file ) % constructor
if nargin == 0
obj.fileName = 'cellScript.m'; % default file for testing
else
obj.fileName = file; % store user file
end
obj.parseFile(); % read the file into memory
end
function obj = parseFile ( obj )
if ~isempty ( obj.fileInfo ) % on parsing check to see if its been parsed before
if isequal ( obj.fileInfo, dir ( obj.fileName ) ) % Check date stamp (has cell file been modified
% disp ( 'file not changed - reading skipped' ); % if not skip
% reading
return
end
end
obj.fileInfo = dir ( obj.fileName ); % store file info
fid = fopen ( obj.fileName ); % open file for reading
if fid ~= -1
index = 0; % this is the index of each cell
inCell = false; % has it found a cell to start reading
lines = cell(0);
while ( true )
line = fgetl ( fid ); % read the line in the file
if line == -1; break; end % check for the end of the file
sLine = strtrim ( line ); % trim any white space
if length ( sLine ) > 2 && strcmp ( sLine(1:2), '%%' ) % check to see if its the start of a cell
if index > 0 % Store the last cell data
obj.cellInfo{index} = lines; % in class to run when required
end
index = index + 1; % increment the index
obj.cellNames{index} = strtrim ( sLine(3:end) ); % save the name of the cell
lines = cell(0); % re-initialise the lines var
inCell = true; % the start of the cells have been found
elseif inCell % if reading a cell array
lines{end+1} = line; % add each line to the lines var
end
end
if index > 0 % make sure and save the last cell when finished reading
obj.cellInfo{index} = lines;
end
fclose ( fid );
else
error ( 'cellRunner:fileError', 'unable to read file' );
end
end
function obj = runCell ( obj, arg )
% obj.runCell ( 'cellName' );
% obj.runCell ( index );
obj.parseFile(); % check that the file hasn't been changed
if ischar ( arg ) % if user provided a char then search for it
index = strcmp ( arg, obj.cellNames ); % find the index
if ~any ( index ) % check it was found
error ( 'cellRunner:notFound', '%s not found', arg );
end
else
index = arg; % if index is an integer (not checked - assumed if not char)
if index < 1 || index > length ( obj.cellInfo ) % check integer is valid
error ( 'cellRunner:notFound', 'Index %d not found', arg );
end
end
commands = obj.cellInfo{index}{1}; % start to build the command to execute.
inBlock = false;
for ii=2:length(obj.cellInfo{index}) % loop around - ignoring any commented lines.
nextLine = strtrim ( obj.cellInfo{index}{ii} );
if inBlock
if length ( nextLine ) == 2 && strcmp ( nextLine, '%}' );
inBlock = false;
end
continue
end
if length ( nextLine ) == 2 && strcmp ( nextLine, '%{' );
inBlock = true;
continue
end
if length ( nextLine ) >= 1 && strcmp ( nextLine(1), '%' )
continue;
end
commands = sprintf ( '%s;%s', commands, obj.cellInfo{index}{ii} ); % build a parge string to eval
end
evalin('base',commands); % eval the expression in the base workspace.
end
end
end
The code is then used as follows:
obj.cellRunner();
% Individual cells can be run in two ways:
% By providing the name of the cell (the string after the %%)
obj.runCell ( 'cellC' );
% By providing the index
obj.runCell ( 3 );
Note Recall the file must be saved for this to work.
Sample run:
whos
obj = cellRunner ( 'cellScript.m' );
obj.runCell ( 'cellC' );
running cell C
past 1st coment
past indented comment
past block comment
past multiple lines of comments
finished cell C
whos
Name Size Bytes Class Attributes
aa 1x100 800 double
ans 1x1 112 cellRunner
ii 1x1 8 double
obj 1x1 112 cellRunner
Note 1 - Why handle class? I inherit from the handle class because I only want one copy of my file data that has been read - see answer 1 in this question for an excellent overview of when to use value/handle classes.
This is my ugly code that does what you asked for.
Despite my best attempts, I was unable to find the way to make it work "properly" (i.e. without simulating key presses). It is my hope that this will facilitate somebody else's attempts to find the way to do it right. Anyway, here goes:
runCodeSectionInFile.m
:
function runCodeSectionInFile(scriptFullPath,sectionNum)
%// TIP: You can use "which('scriptName')" to obtain the full path of a
%// script on your MATLAB path.
%%% // Temporary Workaround
import java.awt.Robot; %// part of temporary workaround
import java.awt.event.*; %// part of temporary workaround
RoboKey = Robot; %// part of temporary workaround
RoboKey.setAutoDelay(10);%// part of temporary workaround
%% // Test if the needed components are available (optional)
if ~matlab.desktop.editor.isEditorAvailable || ...
~com.mathworks.mde.editor.codepad.Codepad.isCodepadEnabled
error('MATLAB editor is N\A');
end
%% // Open and\or switch to the script file
%// Test if script is opened:
if ~matlab.desktop.editor.isOpen(scriptFullPath)
scriptDoc = matlab.desktop.editor.openDocument(scriptFullPath);
else %// in case the script is open, get a handle to it, and save it:
scriptDoc = matlab.desktop.editor.findOpenDocument(scriptFullPath);
%// Save the script before running (optional):
scriptDoc.save;
end
scriptDoc.goToLine(0); %// Position the cursor at the beginning of the file
%// NOTE1: - uses zero based indexing!
jEd = com.mathworks.mlservices.MLEditorServices.getEditorApplication ...
.openEditorForExistingFile(java.io.File(scriptFullPath));
jEd.getTextComponent.grabFocus; drawnow; %// part of temp fix
%// NOTE2: most of the above can be replaced with:
%// EDITOROBJ = matlab.desktop.editor.openAndGoToLine(FILENAME,LINENUM);
%% // Get the Codepad and the LineManager handles:
jCm = com.mathworks.mde.editor.codepad.CodepadActionManager ...
.getCodepadActionManager(jEd);
jCp = jEd.getProperty('Codepad');
jLm = jCp.getLineManager(jEd.getTextComponent,jCm);
%% // Advance to the desired section
jAc = com.mathworks.mde.editor.codepad.CodepadAction.CODEPAD_NEXT_CELL;
%// 'next-cell' Action
for ind1=1:sectionNum-1 %// click "advance" several times
%// <somehowExecute(jAc) OR jCp.nextCell() >
RoboKey.keyPress(KeyEvent.VK_CONTROL); %// part of temporary workaround
RoboKey.keyPress(KeyEvent.VK_DOWN); %// part of temporary workaround
end
RoboKey.keyRelease(KeyEvent.VK_DOWN); %// part of temporary workaround
RoboKey.keyRelease(KeyEvent.VK_CONTROL); %// part of temporary workaround
%% Execute section - equivalent to clicking "Run Section" once
jAc = com.mathworks.mde.editor.codepad.CodepadAction.CODEPAD_EVALUATE_CELL;
%// 'eval-cell' Action
%// <somehowExecute(jAc); OR jCp.evalCurrentCell(true) >
RoboKey.keyPress(KeyEvent.VK_CONTROL); %// part of temporary workaround
RoboKey.keyPress(KeyEvent.VK_ENTER); %// part of temporary workaround
RoboKey.keyRelease(KeyEvent.VK_CONTROL); %// part of temporary workaround
%% // Close the file (optional)
jEd.close;
%% // Return focus to the command line:
com.mathworks.mde.cmdwin.CmdWin.getInstance.grabFocus;
testScript.m
:
%% // This is code block one
disp('This is section one');
%% // This is code block two
disp('This is section two');
%% // This is code block three
disp('This is section three');
To run the demo, simply put both files in the same folder, make sure it's on your path (or cd), and do runCodeSectionInFile(which('testScript.m'),1)
. This results in the following:
>> runCodeSectionInFile(which('testScript.m'),1)
This is section one
>> runCodeSectionInFile(which('testScript.m'),2)
This is section two
>> runCodeSectionInFile(which('testScript.m'),3)
This is section three
You may also need to adjust the RoboKey.setAutoDelay(10);
to a larger number if you get NPE.
Sources:
A long-gone MATLAB blog post.
Some old question I posted and its answers.
P.S. I wrote this on 2014b. Other (older) versions may need adjustments...
I have managed to uncover another piece of this puzzle by finding handles to the Codepad
object and its TaggedLineManager
. The current problem is the fact that most Codepad
methods must be run on the EDT (something like a "UI thread", i.e. originate from interactions with the interface).