Запустить определенный раздел ячейки из командной строки в Matlab?
Я вручную перебираю различные ячейки в Matlab в скрипте (назовем его foo.m):
%%
%Code for cell 1
%%
%Code for cell 2
Из командной строки Matlab я хотел бы иметь возможность выборочно запускать код в ячейке 2. Документация содержит только инструкции о том, как сделать это в интерактивном режиме (например, поместите курсор в соответствующую ячейку, а затем бла-бла). Я хотел бы что-то для командной строки, чтобы я мог сделать что-то вроде foo.runCell(1) для запуска кода в ячейке 1 выше.
Если нет способа сделать это, я просто разделю ячейки на отдельные скрипты / функции. Это менее удобно, так как я нахожусь в режиме кодирования "очень быстро растираю прототип", поэтому пока хочу все в одном файле.
3 ответа
Dev-iL предоставил хороший ответ, используя java и т. Д. Я приведу здесь альтернативу, которая не использует java или редактор, но читает файл и пропускает операторы по запросу.
Чтобы это работало, у него есть предварительное требование, чтобы файл был сохранен (что, я ценю, не является обязательным условием для интерактивного запуска ячеек /codeBlocks).
В любом случае, я подумал, что это "странный" вопрос, и решил добавить ответ.
Вот мой скрипт (cellScript.m), который содержит блоки кода (заметьте, я дал каждому блоку имя после "%%":
%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' );
Я создал класс, который выполняет запрошенную работу (я назвал его 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
Код затем используется следующим образом:
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 );
Примечание Напомним, что файл должен быть сохранен, чтобы это работало.
Образец прогона:
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
Примечание 1 - Зачем обращаться с классом? Я наследую от дескриптора класса, потому что мне нужна только одна копия данных моего файла, которые были прочитаны - см. Ответ 1 в этом вопросе для превосходного обзора того, когда использовать классы value/handle.
Это мой уродливый код, который делает то, что вы просили.
Несмотря на все мои попытки, я не смог найти способ заставить его работать "должным образом" (т.е. без симуляции нажатий клавиш). Я надеюсь, что это облегчит чужие попытки найти способ сделать это правильно. Во всяком случае, здесь идет:
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');
Чтобы запустить демонстрацию, просто поместите оба файла в одну папку, убедитесь, что они находятся на вашем пути (или компакт-диске), и выполните runCodeSectionInFile(which('testScript.m'),1)
, Это приводит к следующему:
>> 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
Вам также может понадобиться настроить RoboKey.setAutoDelay(10);
на большее число, если вы получаете NPE.
Источники:
PS Я написал это на 2014b. Другие (более старые) версии могут нуждаться в корректировке...
Edit1:
Мне удалось раскрыть еще одну часть этой головоломки, найдя ручки для Codepad
объект и его TaggedLineManager
, Текущая проблема заключается в том, что большинство Codepad
методы должны быть запущены в EDT (что-то вроде "потока пользовательского интерфейса", т. е. происходят из взаимодействия с интерфейсом).
Чтобы запустить определенный раздел из файла с помощью командной строки, вы можете использовать
echodemo(filename, index of section)