spm_jobman.m 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. function varargout = spm_jobman(varargin)
  2. % Main interface for SPM Batch System
  3. % This function provides a compatibility layer between SPM and matlabbatch.
  4. %
  5. % FORMAT spm_jobman('initcfg')
  6. % Initialise jobs configuration and set MATLAB path accordingly.
  7. %
  8. % FORMAT spm_jobman('run',job[,input1,...inputN])
  9. % FORMAT output_list = spm_jobman('run',job[,input1,...inputN])
  10. % FORMAT [output_list, hjob] = spm_jobman('run',job[,input1,...inputN])
  11. % Run specified job.
  12. % job - filename of a job (.m or .mat), or
  13. % cell array of filenames, or
  14. % 'jobs'/'matlabbatch' variable, or
  15. % cell array of 'jobs'/'matlabbatch' variables.
  16. % input1,... - optional list of input arguments. These are filled into
  17. % open inputs ('X->' marked in the GUI) before a job is
  18. % run. When using an "{:}" subscript on a cell array,
  19. % MATLAB expands this cell array into a comma separated
  20. % list of arguments. Therefore, one can collect input
  21. % arguments in the right order into a cell array named e.g.
  22. % input_array and call spm_jobman('run',job,input_array{:})
  23. % to run a job using the collected inputs. For files or text
  24. % entry items, these inputs are the values one would specify
  25. % in the GUI. For menus, the item number has to be entered
  26. % (neither the GUI label nor the associated value that is
  27. % saved in a batch).
  28. % output_list - cell array containing the output arguments from each
  29. % module in the job. The format and contents of these
  30. % outputs is defined in the configuration of each module
  31. % (.prog and .vout callbacks).
  32. % hjob - harvested job after it has been filled and run. Note that
  33. % this job has no dependencies any more. If one loads this
  34. % job back to the batch system and changes some of the
  35. % inputs, changed outputs will not be passed on.
  36. %
  37. % FORMAT job_id = spm_jobman
  38. % job_id = spm_jobman('interactive',job[,node])
  39. % job_id = spm_jobman('interactive','',node)
  40. % Run the user interface in interactive mode.
  41. % job - filename of a job (.m or .mat), or
  42. % cell array of filenames, or
  43. % 'jobs'/'matlabbatch' variable, or
  44. % cell array of 'jobs'/'matlabbatch' variables.
  45. % node - indicate which part of the configuration is to be used.
  46. % For example, it could be 'spm.spatial.coreg.estimate'.
  47. % job_id - can be used to manipulate this job in cfg_util. Note that
  48. % changes to the job in cfg_util will not show up in cfg_ui
  49. % unless 'Update View' is called.
  50. %
  51. % FORMAT jobs = spm_jobman('convert',jobs)
  52. % Convert older batch jobs to latest version
  53. % jobs - char or cell array of filenames, or
  54. % 'jobs'/'matlabbbatch' variable
  55. %__________________________________________________________________________
  56. % Copyright (C) 2005-2016 Wellcome Trust Centre for Neuroimaging
  57. % Volkmar Glauche
  58. % $Id: spm_jobman.m 6968 2016-12-09 15:58:00Z guillaume $
  59. %__________________________________________________________________________
  60. %
  61. % Programmers help:
  62. %
  63. % FORMAT [output_list hjob] = spm_jobman('serial')
  64. % [output_list hjob] = spm_jobman('serial',job[,'', input1,...inputN])
  65. % [output_list hjob] = spm_jobman('serial',job ,node[,input1,...inputN])
  66. % [output_list hjob] = spm_jobman('serial','' ,node[,input1,...inputN])
  67. % Run the user interface in serial mode. If job is not empty, then node
  68. % is silently ignored. Inputs can be a list of arguments. These are passed
  69. % on to the open inputs of the specified job/node. Each input should be
  70. % suitable to be assigned to item.val{1}. For cfg_repeat/cfg_choice items,
  71. % input should be a cell list of indices input{1}...input{k} into
  72. % item.value. See cfg_util('filljob',...) for details.
  73. %
  74. % FORMAT spm_jobman('help',node)
  75. % spm_jobman('help',node,width)
  76. % Create a cell array containing help information. This is justified
  77. % to be 'width' characters wide. e.g.
  78. % h = spm_jobman('help','spm.spatial.coreg.estimate');
  79. % for i=1:numel(h), fprintf('%s\n',h{i}); end
  80. %
  81. % FORMAT [tag, job] = spm_jobman('harvest', job_id|job|cfg_item|cfg_struct)
  82. % Take the job with id job_id in cfg_util and extract what is
  83. % needed to save it as a batch job (for experts only). If a (partial) job
  84. % is given instead, the output job is augmented with default settings.
  85. % If the argument is a cfg_item or cfg_struct tree, it will be harvested
  86. % outside cfg_util.
  87. % tag - tag of the root node of the current job/cfg_item tree
  88. % job - harvested data from the current job/cfg_item tree
  89. %__________________________________________________________________________
  90. %
  91. % This code is based on earlier versions by John Ashburner, Philippe
  92. % Ciuciu and Guillaume Flandin.
  93. % It now relies on matlabbatch
  94. % http://sourceforge.net/projects/matlabbatch/
  95. % Copyright (C) 2008 Freiburg Brain Imaging
  96. %__________________________________________________________________________
  97. %-Force jobs configuration initialisation if needed
  98. %--------------------------------------------------------------------------
  99. persistent isInitCfg;
  100. if isempty(isInitCfg) && ~(nargin == 1 && strcmpi(varargin{1},'initcfg'))
  101. % Run spm_jobman('initcfg') beforehand.
  102. fprintf('Initialising batch system... ');
  103. spm_jobman('initcfg');
  104. fprintf('done.\n');
  105. end
  106. isInitCfg = true;
  107. %-Open GUI when called without input arguments
  108. %--------------------------------------------------------------------------
  109. if ~nargin
  110. spm_jobman('interactive');
  111. if nargout > 0, varargout = {findobj(0,'tag','cfg_ui')}; end
  112. return;
  113. end
  114. %-Warn about deprecated syntax
  115. %--------------------------------------------------------------------------
  116. action = lower(varargin{1});
  117. switch action
  118. case 'run_nogui'
  119. warning('spm:spm_jobman:deprecated', ...
  120. 'Callback ''%s'' is deprecated. Use ''run'' instead.',action);
  121. action = 'run';
  122. case {'spm5tospm8','spm5tospm8bulk'}
  123. warning('spm:spm_jobman:deprecated', ...
  124. 'Callback ''%s'' is deprecated. Use ''convert'' instead.',action);
  125. action = 'convert';
  126. end
  127. %-Load and convert batch jobs
  128. %--------------------------------------------------------------------------
  129. if ismember(action, {'interactive','run','serial'})
  130. if nargin > 1
  131. % sort out job/node arguments for interactive, serial, run cmds
  132. if nargin>=2 && ~isempty(varargin{2})
  133. % do not consider node if job is given
  134. if ischar(varargin{2}) || iscellstr(varargin{2})
  135. jobs = load_jobs(varargin{2});
  136. elseif iscell(varargin{2})
  137. if iscell(varargin{2}{1})
  138. % assume varargin{2} is a cell of jobs
  139. jobs = varargin{2};
  140. else
  141. % assume varargin{2} is a single job
  142. jobs{1} = varargin{2};
  143. end
  144. end
  145. mljob = canonicalise_jobs(jobs);
  146. elseif ismember(action, {'interactive','serial'}) && nargin>=3 && isempty(varargin{2})
  147. % Node spec only allowed for 'interactive', 'serial'
  148. mod_cfg_id = cfg_util('tag2mod_cfg_id',varargin{3});
  149. else
  150. error('spm:spm_jobman:invalidSyntax', ...
  151. 'Don''t know how to handle this ''%s'' call.', action);
  152. end
  153. end
  154. end
  155. %-Perform action
  156. %--------------------------------------------------------------------------
  157. switch action
  158. case {'initcfg'}
  159. if ~isdeployed
  160. addpath(fullfile(spm('Dir'),'matlabbatch'));
  161. addpath(fullfile(spm('Dir'),'config'));
  162. end
  163. try
  164. spm_select('init');
  165. catch
  166. S = which('spm_select','-all');
  167. if numel(S) > 1
  168. fprintf('spm_select appears several times in your MATLAB path:\n');
  169. for i=1:numel(S)
  170. if i==1
  171. fprintf(' %s (SHADOWING)\n',S{1});
  172. else
  173. fprintf(' %s\n',S{i});
  174. end
  175. end
  176. end
  177. rethrow(lasterror);
  178. end
  179. cfg_get_defaults('cfg_util.genscript_run', @genscript_run);
  180. cfg_util('initcfg'); % This must be the first call to cfg_util
  181. %if ~spm('cmdline')
  182. % f = cfg_ui('Visible','off'); % Create invisible batch ui
  183. % f0 = findobj(f, 'Tag','MenuFile'); % Add entries to file menu
  184. % f2 = uimenu(f0,'Label','xxx', 'Callback',@xxx, ...
  185. % 'HandleVisibility','off', 'tag','xxx');
  186. %end
  187. case {'interactive'}
  188. if exist('mljob', 'var')
  189. cjob = cfg_util('initjob', mljob);
  190. elseif exist('mod_cfg_id', 'var')
  191. if isempty(mod_cfg_id)
  192. warning('spm:spm_jobman:NodeNotFound', ...
  193. ['Can not find executable node ''%s'' - running '...
  194. 'matlabbatch without default node.'], varargin{3});
  195. cjob = cfg_util('initjob');
  196. else
  197. cjob = cfg_util('initjob');
  198. mod_job_id = cfg_util('addtojob', cjob, mod_cfg_id);
  199. cfg_util('harvest', cjob, mod_job_id);
  200. end
  201. else
  202. cjob = cfg_util('initjob');
  203. end
  204. f = findobj(0,'tag','cfg_ui');
  205. if isempty(f), f = cfg_ui; end
  206. cfg_ui('local_showjob', f, cjob);
  207. if nargout > 0
  208. varargout{1} = cjob;
  209. end
  210. case {'serial'}
  211. if exist('mljob', 'var')
  212. cjob = cfg_util('initjob', mljob);
  213. else
  214. cjob = cfg_util('initjob');
  215. if nargin > 2
  216. [mod_cfg_id, item_mod_id] = cfg_util('tag2cfg_id', lower(varargin{3}));
  217. cfg_util('addtojob', cjob, mod_cfg_id);
  218. end
  219. end
  220. sts = fill_run_job('serial', cjob, varargin{4:end});
  221. if sts
  222. if nargout > 0
  223. varargout{1} = cfg_util('getalloutputs', cjob);
  224. end
  225. if nargout > 1
  226. [un, varargout{2}] = cfg_util('harvestrun', cjob);
  227. end
  228. cfg_util('deljob', cjob);
  229. end
  230. case {'run'}
  231. if ~exist('mljob', 'var')
  232. error('Not enough input arguments.');
  233. end
  234. cjob = cfg_util('initjob', mljob);
  235. sts = fill_run_job('run', cjob, varargin{3:end});
  236. if sts
  237. if nargout > 0
  238. varargout{1} = cfg_util('getalloutputs', cjob);
  239. end
  240. if nargout > 1
  241. [un, varargout{2}] = cfg_util('harvestrun', cjob);
  242. end
  243. cfg_util('deljob', cjob);
  244. end
  245. case {'convert'}
  246. varargout{1} = convert_jobs(varargin{2:end});
  247. case {'harvest'}
  248. if nargin == 1
  249. error('spm:spm_jobman:CantHarvest', ...
  250. ['Can not harvest job without job_id. Please use ' ...
  251. 'spm_jobman(''harvest'', job_id).']);
  252. elseif cfg_util('isjob_id', varargin{2})
  253. [tag, job] = cfg_util('harvest', varargin{2});
  254. elseif iscell(varargin{2})
  255. cjob = cfg_util('initjob', varargin{2});
  256. [tag, job] = cfg_util('harvest', cjob);
  257. cfg_util('deljob', cjob);
  258. elseif isa(varargin{2}, 'cfg_item')
  259. [tag, job] = harvest(varargin{2}, varargin{2}, false, false);
  260. elseif isstruct(varargin{2})
  261. % try to convert into class before harvesting
  262. c = cfg_struct2cfg(varargin{2});
  263. [tag, job] = harvest(c,c,false,false);
  264. else
  265. error('spm:spm_jobman:CantHarvestThis', ...
  266. 'Can not harvest this argument.');
  267. end
  268. varargout{1} = tag;
  269. varargout{2} = job;
  270. case {'help'}
  271. if (nargin < 2) || isempty(varargin{2})
  272. node = 'spm';
  273. else
  274. node = varargin{2};
  275. end
  276. if nargin < 3
  277. width = 60;
  278. else
  279. width = varargin{3};
  280. end
  281. varargout{1} = cfg_util('showdocwidth', width, node);
  282. otherwise
  283. error('spm:spm_jobman:unknownOption','Unknown option "%s".',varargin{1});
  284. end
  285. %==========================================================================
  286. % function newjobs = load_jobs(job)
  287. %==========================================================================
  288. function newjobs = load_jobs(job)
  289. % Load a list of possible job files, return a cell list of jobs.
  290. % If a job file failed to load, an empty cell is returned in the list.
  291. filenames = cellstr(job);
  292. newjobs = {};
  293. for i = 1:numel(filenames)
  294. switch spm_file(filenames{i},'ext')
  295. case 'mat'
  296. try
  297. S = load(filenames{i});
  298. if isfield(S,'matlabbatch')
  299. matlabbatch = S.matlabbatch;
  300. elseif isfield(S,'jobs')
  301. jobs = S.jobs;
  302. end
  303. catch
  304. warning('spm:spm_jobman:loadFailed','Load failed: ''%s''',filenames{i});
  305. end
  306. case 'm'
  307. try
  308. str = fileread(filenames{i});
  309. eval(str);
  310. catch
  311. warning('spm:spm_jobman:loadFailed','Load failed: ''%s''',filenames{i});
  312. end
  313. case 'json'
  314. try
  315. S = spm_jsonread(filenames{i});
  316. if isstruct(S)
  317. for j=1:numel(S)
  318. matlabbatch{j} = S(j);
  319. end
  320. else
  321. matlabbatch = S;
  322. end
  323. catch
  324. warning('spm:spm_jobman:loadFailed','Load failed: ''%s''',filenames{i});
  325. end
  326. otherwise
  327. warning('spm:spm_jobman:unknownExt','Unknown extension: ''%s''',filenames{i});
  328. end
  329. if exist('jobs','var')
  330. newjobs = [newjobs(:); {jobs}];
  331. clear jobs;
  332. elseif exist('matlabbatch','var')
  333. newjobs = [newjobs(:); {matlabbatch}];
  334. clear matlabbatch;
  335. else
  336. warning('spm:spm_jobman:jobNotFound','No batch job found in ''%s''',filenames{i});
  337. newjobs = [newjobs(:); {[]}];
  338. end
  339. end
  340. %==========================================================================
  341. % function varargout = convert_jobs(varargin)
  342. %==========================================================================
  343. function varargout = convert_jobs(varargin)
  344. % Convert a list of jobs to latest version
  345. if nargin && iscell(varargin{1}) && ~iscellstr(varargin{1})
  346. varargout = canonicalise_jobs(varargin);
  347. return;
  348. elseif ~nargin || isempty(varargin{1})
  349. [fname, sts] = spm_select([1 Inf], 'batch', 'Select job file(s)');
  350. if ~sts, return; end
  351. else
  352. fname = varargin{1};
  353. end
  354. fname = cellstr(fname);
  355. joblist = load_jobs(fname);
  356. SPMver = spm('Ver');
  357. outnames = cell(numel(fname),1);
  358. for i=1:numel(fname)
  359. if ~isempty(joblist{i})
  360. outnames{i} = spm_file(fname{i},'prefix',[lower(SPMver) '_']);
  361. fprintf('Initial job: %s\n', fname{i});
  362. fprintf('%s job: %s\n',SPMver, outnames{i});
  363. cjob = cfg_util('initjob', canonicalise_jobs(joblist(i)));
  364. cfg_util('savejob', cjob, outnames{i});
  365. cfg_util('deljob', cjob);
  366. end
  367. end
  368. varargout = {outnames};
  369. %==========================================================================
  370. % function [mljob, comp] = canonicalise_jobs(job)
  371. %==========================================================================
  372. function [mljob, comp] = canonicalise_jobs(job)
  373. % job: a cell list of job data structures.
  374. % Check whether job is a SPM5 or matlabbatch job. In the first case, all
  375. % items in job{:} should have a fieldname of either 'temporal', 'spatial',
  376. % 'stats', 'tools' or 'util'. If this is the case, then job will be
  377. % assigned to mljob{1}.spm, which is the tag of the SPM root configuration
  378. % item.
  379. comp = true(size(job));
  380. mljob = cell(size(job));
  381. for i = 1:numel(job)
  382. for j = 1:numel(job{i})
  383. comp(i) = comp(i) && any(strcmp(fieldnames(job{i}{j}), ...
  384. {'temporal', 'spatial', 'stats', 'tools', 'util'}));
  385. if ~comp(i)
  386. break;
  387. end
  388. end
  389. if comp(i)
  390. tmp = sub_canonicalise_job(job{i});
  391. for j=1:numel(tmp)
  392. mljob{i}{j}.spm = tmp{j};
  393. end
  394. else
  395. mljob{i} = job{i};
  396. end
  397. end
  398. %==========================================================================
  399. % function njobs = sub_canonicalise_job(jobs)
  400. %==========================================================================
  401. function njobs = sub_canonicalise_job(jobs)
  402. decel = struct('spatial',struct('realign',[],'coreg',[],'normalise',[]),...
  403. 'temporal',[],...
  404. 'stats',[],...
  405. 'meeg',[],...
  406. 'util',[],...
  407. 'tools',struct('dartel',[]));
  408. njobs = {};
  409. for i0 = 1:numel(jobs)
  410. tmp0 = fieldnames(jobs{i0});
  411. tmp0 = tmp0{1};
  412. if any(strcmp(tmp0,fieldnames(decel)))
  413. for i1=1:numel(jobs{i0}.(tmp0))
  414. tmp1 = fieldnames(jobs{i0}.(tmp0){i1});
  415. tmp1 = tmp1{1};
  416. if ~isempty(decel.(tmp0))
  417. if any(strcmp(tmp1,fieldnames(decel.(tmp0))))
  418. for i2=1:numel(jobs{i0}.(tmp0){i1}.(tmp1))
  419. njobs{end+1} = struct(tmp0,struct(tmp1,jobs{i0}.(tmp0){i1}.(tmp1){i2}));
  420. end
  421. else
  422. njobs{end+1} = struct(tmp0,jobs{i0}.(tmp0){i1});
  423. end
  424. else
  425. njobs{end+1} = struct(tmp0,jobs{i0}.(tmp0){i1});
  426. end
  427. end
  428. else
  429. njobs{end+1} = jobs{i0};
  430. end
  431. end
  432. %==========================================================================
  433. % function sts = fill_run_job(action, cjob, varargin)
  434. %==========================================================================
  435. function sts = fill_run_job(action, cjob, varargin)
  436. switch lower(action)
  437. case 'serial'
  438. sts = cfg_util('filljobui', cjob, @serial_ui, varargin{:});
  439. case 'run'
  440. sts = cfg_util('filljob', cjob, varargin{:});
  441. end
  442. if sts
  443. cfg_util('run', cjob);
  444. else
  445. cfg_util('deljob', cjob);
  446. error('spm:spm_jobman:jobNotFilled', 'No executable modules, but still unresolved dependencies or incomplete module inputs.');
  447. end
  448. %==========================================================================
  449. % function [val sts] = serial_ui(item)
  450. %==========================================================================
  451. function [val, sts] = serial_ui(item)
  452. % wrapper function to translate cfg_util('filljobui'... input requests into
  453. % spm_input/cfg_select calls.
  454. sts = true;
  455. switch class(item)
  456. case 'cfg_choice'
  457. labels = cell(size(item.values));
  458. values = cell(size(item.values));
  459. for k = 1:numel(item.values)
  460. labels{k} = item.values{k}.name;
  461. values{k} = k;
  462. end
  463. val = spm_input(item.name, 1, 'm', labels, values);
  464. case 'cfg_menu'
  465. val = spm_input(item.name, 1, 'm', item.labels, item.values);
  466. val = val{1};
  467. case 'cfg_repeat'
  468. labels = cell(size(item.values));
  469. values = cell(size(item.values));
  470. for k = 1:numel(item.values)
  471. labels{k} = item.values{k}.name;
  472. values{k} = k;
  473. end
  474. % enter at least item.num(1) values
  475. for k = 1:item.num(1)
  476. val(k) = spm_input(sprintf('%s(%d)', item.name, k), 1, 'm', ...
  477. labels, values);
  478. end
  479. % enter more (up to varargin{3}(2) values
  480. labels = {labels{:} 'Done'};
  481. % values is a cell list of natural numbers, use -1 for Done
  482. values = {values{:} -1};
  483. while numel(val) < item.num(2)
  484. val1 = spm_input(sprintf('%s(%d)', item.name, numel(val)+1), 1, ...
  485. 'm', labels, values);
  486. if val1{1} == -1
  487. break;
  488. else
  489. val(end+1) = val1;
  490. end
  491. end
  492. case 'cfg_entry'
  493. val = spm_input(item.name, 1, item.strtype, '', item.num, ...
  494. item.extras);
  495. case 'cfg_files'
  496. [t,sts] = cfg_getfile(item.num, item.filter, item.name, '', ...
  497. item.dir, item.ufilter);
  498. if sts
  499. val = cellstr(t);
  500. else
  501. val = {};
  502. error('File selector was closed.');
  503. end
  504. end
  505. %==========================================================================
  506. % function [code cont] = genscript_run
  507. %==========================================================================
  508. function [code, cont] = genscript_run
  509. % Return code snippet to initialise SPM defaults and run a job generated by
  510. % cfg_util('genscript',...) through spm_jobman.
  511. modality = spm('CheckModality');
  512. code{1} = sprintf('spm(''defaults'', ''%s'');', modality);
  513. code{2} = 'spm_jobman(''run'', jobs, inputs{:});';
  514. cont = false;
  515. %-Compatibility layer for SPM5
  516. function varargout = interactive(varargin)
  517. function varargout = defaults_edit(varargin)
  518. function varargout = run_serial(varargin)