Run particular cell section from command line in Matlab?

前端 未结 3 1355
悲&欢浪女
悲&欢浪女 2021-01-13 03:32

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

相关标签:
3条回答
  • 2021-01-13 04:06

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

    echodemo(filename, index of section)
    

    MATLAB documentation

    0 讨论(0)
  • 2021-01-13 04:20

    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.

    0 讨论(0)
  • 2021-01-13 04:20

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

    0 讨论(0)
提交回复
热议问题