Run particular cell section from command line in Matlab?
Asked Answered
B

3

5

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 command line of Matlab, I would like to be able to selectively run the code in cell 2. The documentation only has instructions for how to do it interactively (e.g., place the cursor in the appropriate cell, then blah blah). I would like something for the command line so I can do something like foo.runCell(1) to run the code in cell 1 above.

If there is no way to do it, I will just split the cells up into separate scripts/functions. This is less convenient, as I am in a 'grind out prototype very quickly' mode of coding, so want everything in one file for now.

Burberry answered 15/1, 2015 at 18:38 Comment(7)
I'd be surprised if there were a way to run a specific code section from the command line. Doing so relies on the assumption that you know the number of the code section you want to run without necessarily having the file open in the editor in front of you... That would be very error-prone. A cleaner way of doing things would be to define a function for each of your code sections; then you'd know exactly what you're running.Courland
When I am in the same mode than you, I just set very explicit title to my cell (the text you put after the double %% will be highlighted in bold in the editor), then I mix between what I type/try in the console and the editor where I execute full cells. If I get a succession of commands which satisfy me, I just drag that (from the command history) to the editor, make a new cell, give it a good name so I find it easily later, then move on to my next piece of code.Leroi
You probably can do this (no idea how though) but it probably would involve writing all sorts of additional auxiliary code and digging deep into the operation of the matlab gui. You could instead use a for loop combined with conditional statements (if...then or switch), like you might in a shell script. It probably would take a little effort but much less than sorting through esoteric matlab documentation.Olivo
@Jubobs yes there is the option, mentioned in last para of my question, of just splitting them into different functions/scripts. I'm trying to avoid that, but it may end up being the best way.Burberry
@TryHard yes I could just do switch/case instead of using cells. But I actually use the cells the normal way too (go through by hand the way they are "meant" to be used). I want to be able to do it both ways.Burberry
You can place the case clauses between sequential %% markers and hop two cells at a time when running manually.Olivo
I agree with @TryHard - I would not be surprised if this thing is possible using the the java-gui functions. Although, it would likely require you to "simulate" opening the file, advancing the cursor to the correct cell and clicking the execute-cell button. It's a nice exercise in MATLAB gui, but I doubt its usefulness (for the reason Jubobs mentioned). I think it would be easier to find a way to access the root workspace from within a function or just use globals everywhere (both of which sound to me like bad ideas TBH)... I'll give the gui libraries a look in case what you want isn't hard...Etesian
D
3

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.

Dunker answered 16/1, 2015 at 9:48 Comment(8)
Indeed an interesting alternative to my suggestion. BTW, maybe you should explain why you extended handle, as it may be unclear for some readers...Etesian
I added a note about why I inherit from handle class.Dunker
@Dunker Beatful, and works. Question: Shouldn't the initial call be something like obj = cellRunner('filename.m')?Burberry
Yes it should be (I have updated the answer) - the original would simply run the default which I created when testing...Dunker
@Dunker It seems to get hung up on certain commented lines (e.g., if %commet here is on a standalone line it skips the rest of the file). I'm looking over the code to see if I can fix it...Burberry
You cant have lines of code with comments at the end. e.g. yourCommand % your comment is not allowed due to the way it uses evalin.Dunker
The example in the answer cellC has a comment % cells can have comments and that worked ok. You might need to check for leading spaces for example (or write extra code for block comments)Dunker
Let us continue this discussion in chat.Burberry
E
3

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:

  1. A long-gone MATLAB blog post.

  2. Some old question I posted and its answers.

P.S. I wrote this on 2014b. Other (older) versions may need adjustments...

Edit1:

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).

Etesian answered 16/1, 2015 at 3:11 Comment(1)
Works great, wish I could accept two answers. Handles commenting very smoothly.Burberry
D
3

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.

Dunker answered 16/1, 2015 at 9:48 Comment(8)
Indeed an interesting alternative to my suggestion. BTW, maybe you should explain why you extended handle, as it may be unclear for some readers...Etesian
I added a note about why I inherit from handle class.Dunker
@Dunker Beatful, and works. Question: Shouldn't the initial call be something like obj = cellRunner('filename.m')?Burberry
Yes it should be (I have updated the answer) - the original would simply run the default which I created when testing...Dunker
@Dunker It seems to get hung up on certain commented lines (e.g., if %commet here is on a standalone line it skips the rest of the file). I'm looking over the code to see if I can fix it...Burberry
You cant have lines of code with comments at the end. e.g. yourCommand % your comment is not allowed due to the way it uses evalin.Dunker
The example in the answer cellC has a comment % cells can have comments and that worked ok. You might need to check for leading spaces for example (or write extra code for block comments)Dunker
Let us continue this discussion in chat.Burberry
A
3

In order to run a particular section from a file using command line, you can use

echodemo(filename, index of section)

MATLAB documentation

Arlaarlan answered 31/1, 2018 at 6:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.