Matlab - Is there a way to capture messages sent to the workspace? - matlab

I'm working on a GUI (without GUIDE) in Matlab. The GUI will ultimately be compiled to an application and released to my coworkers.
My GUI calls some custom functions I wrote ages ago. These custom functions display status/progress messages to the workspace window.
As I understand it, I could have my executable write those messages to a log file, but then that leaves the user without any status updates on the GUI while the program is running.
I'm working on some pretty intensive 3D data manipulation, which has the potential to run for 5-10 minutes between function calls, so while I could provide status updates between function calls, it still leaves the end user with no idea what's going on and/or the appearance that the program locked up.
What I would like to do is to have something that works akin to the 'try-catch' method, where there's some way I can execute a function and capture messages intended for the workspace and redirect them to a uicontrol text box.
:EDIT:
I'm adding this for posterity, in the event anyone wants to use it. This is a functional demo that shows how to implement Peter's answer below.
First, create and save a function called "EndlessLoop":
function EndlessLoop(handles,loopCallback)
if nargin<1
handles = [];
loopCallback = #loop_Callback;
else
disp('Callback already set!');
end
tic;
abort = false;
while true
statusText = sprintf('Current Elapsed Time:\n%.2f',toc);
abort = loopCallback(handles,statusText);
if abort
statusText = sprintf('Abort request processed.\nEnding now.');
[~] = loopCallback(handles,statusText);
break;
end
pause(0.1);
end
return;
function abort = loop_Callback(~,myText)
clc;
abort = false;
disp(myText)
return;
Then, create a GUI that calls on EndlessLoop:
function TestGUI
close all;
myTest = figure('Visible','on','Units','normalized','Position',[0.1 0.1 0.8 0.8],'Name','Test GUI','NumberTitle','off');
set(myTest,'menubar','none');
handles = guihandles(myTest);
handles.goButton = uicontrol('Style','pushbutton','Units','normalized','Position',[0 0.5 0.5 0.5],'String','Go');
handles.abortButton = uicontrol('Style','pushbutton','Units','normalized','Position',[0 0 0.5 0.5],'String','Abort','Enable','off');
handles.statusText = uicontrol('Style','text','Units','normalized','Position',[0.5 0 0.5 1],'String','Press Go when ready.');
set(handles.goButton,'Callback',#goButton_Callback,'interruptible','on');
set(handles.abortButton,'Callback',#abortButton_Callback,'interruptible','on');
handles.abortButton.UserData = false;
guidata(myTest,handles);
return;
function goButton_Callback(hObject,~)
handles = guidata(gcbo);
hObject.Enable = 'off';
handles.abortButton.Enable = 'on';
EndlessLoop(handles,#StatusUpdate)
handles.abortButton.String = 'Abort';
handles.abortButton.Enable = 'off';
hObject.Enable = 'on';
pause(0.5);
handles.statusText.String = 'Press Go to start again.';
handles.abortButton.UserData = false;
guidata(gcbo,handles);
return;
function abortButton_Callback(hObject,~)
handles = guidata(gcbo);
if handles.abortButton.UserData
handles.abortButton.UserData = false;
hObject.String = 'Abort';
else
handles.abortButton.UserData = true;
hObject.String = sprintf('Abort pending...');
end
guidata(gcbo,handles);
return;
function abort = StatusUpdate(handles,statusText)
clc;
abort = handles.abortButton.UserData;
disp(handles.abortButton.UserData)
handles.statusText.String = statusText;
return;
A couple things I found when playing with this trying to get it to work:
I have been just adding variables to the handles structure for whatever I needed. In this case it would have been handles.abortRequest = false;. However, it appears that when I pass handles to the EndlessLoop function it becomes stale - handles never updates again. To get around this, I had to store what I wanted in the UserData section of the abortButton. I think this is because the handle to abortButton is still valid, because it hasn't changed, and I get fresh UserData because I'm able to poll with the valid handle. I can't access handles.abortRequest because it's not an object - I can't poll it; it simply exists, and it exists in the "snapshot" that was when I sent handles to EndlessLoop. At least, this is my understanding.
I needed to set the 'Interruptible' property of the goButton callback to 'on' in order for the abortButton to function while the process "hung" on EndlessLoop. With Interruptible set to off no other callbacks may be processed until that particular callback completes, which will never happen with endless loop.
So, in conclusion, this is a complete functional example that answers my question. Thanks to Peter for the answer - this also includes his ProTip of being able to pass an abort option back to the process that's taking a long time to complete. I've never used callbacks like this before so hopefully others will find this useful in the future.

This answer elaborates on my comment to the question.
Assume you have function that outputs stuff to the command window:
function out = longcomputation(param1, param2)
%
while(for_a_long_time)
process_one_step();
percent_complete = something;
fprintf('Step result: %f Complete: %f', stuff, percent_complete);
end
You can make the output function configurable by passing a function handle. Here I'm making it optional by testing nargin:
function out = longcomputation(param1, param2, outputfcn)
function display_progress_to_console(msg, percentage)
sprintf('%s, Complete %f', msg, percentage);
end
if(nargin < 3) % Supply a default output function if none passed in
outputfcn = #display_progress_to_console;
end
while(for_a_long_time)
process_one_step();
percent_complete = something;
msg = sprintf('Step result: %f', stuff);
outputfcn(msg, percent_complete);
end
Now, in your GUI, you can define and pass in a different output callback. This can live right in your GUI code so that the function has access to the GUI objects you need.
ProTip: In addition, have this callback function return true or false to signal the user's desire to abort. The GUI can then present a cancel button to drive that return value, and the long-running code can periodically send the status message AND check if abort has been requested.

You could potentially use the built-in diary functionality to accomplish something similar to what you're trying to do.
diary on % Uses "diary" for filename
diary('logfile.log') % Custom filename
This will write all command line output to the specified file. Then you can periodically poll this file and update your uicontrol with the contents (or last few lines if you want).
logfile = 'logfile.log';
diary(logfile);
u = uicontrol('style', 'text');
% Check every 5 seconds
t = timer('Period', 5, ...
'ExecutionMode', 'FixedRate', ...
'TimerFcn', #(s,e)populate(s));
start(t);
function populate(src)
fid = fopen(logfile, 'rb');
contents = fread(fid, '*char').';
set(src, 'String', contents);
fclose(fid);
end

Related

Can't get variable out of Callback function. Already defined as global. Matlab

I have a serialport bytesavailable callback function.
function readFrame(src,~)
global data
global payloadBytes
message = read(src,payloadBytes,"uint8");
src.UserData = message;
data = message;
return
I read the buffer into the variable message and then save it as global variable data.
i get the variable in my workspace. Then something happens that I can't explain. I try to save this global variable in a local variable in my Mainscript. Notice: global data is defined in mainscript.
global data;
global payloadBytes;
msg=[];
frameCount = 1;
numFrames = 10;
messageByte = zeros(numFrames,payloadBytes)
while(app)
%wait till callback triggered and serial data read
while isempty(msg)
%save global variable data in local variable msg
msg = data;
disp("wait for data")
end
%save msg in an expanding 2D Variable messagebyte
messageByte(frameCount,:) = msg;
%empty msg variable
msg =[]
frameCount = frameCount +1
%stop when 10 frames are caught
if frameCount == 11
app = 0;
end
end
Problem is processedData is always empty. So when i want to make a 2D Matrice to save different data like this:
processedData(frameCount,:) = data;
I get an exception: indices doesn't match. Thats no wonder.
Can somebody tell me what I am doing wrong.
Thank you.
MATLAB is single-threaded. Stuff doesn’t run in parallel unless you explicitly use the Parallel Processing Toolbox. So for your callback to write to data, your main loop must give the callback a chance to execute. Add a pause to your loop, this will allow callbacks to execute, as well as timer objects.
while isempty(msg)
pause(0.1) % find a suitable pause length here
%save global variable data in local variable msg
msg = data;
disp("wait for data")
end
The pause doesn’t need to be long, but make it as long as you can tolerate, since that will make your wait less processor intensive.

Stop fminsearch when objective function reach certain value

How to stop fminsearch when objective function exceeded certain value (minima or maxima)
options = optimset('MaxFunEvals',9999);
[x,fval,exitflag,output] = fminsearch(#(var)objectiveFunction(variables), changingParameters,options);
How to stop the function if I reach certain objective function value (for example 1000) [within the 9999 iterations]
I've tried 'TolFun' , I am not sure if this is correct
options = optimset('MaxFunEvals',999,'TolFun',1000);
[x,fval,exitflag,output] = fminsearch(#(var)objectiveFunction(variables), changingParameters,options);
You can manually stop the search procedure by placing an appropriate function in the options.OutputFcn input struct. This function is called in every iteration of the search, and permits to signal back that the search is to be terminated. For example, you could define
function stop = custom_stop_fun(~, optimValues, ~)
if optimValues.fval >= 1000
stop = true;
else
stop = false;
end
end
and then set it via
options.OutputFcn = #custom_stop_fun;
Check out the full OutputFcn documentation

Receive Mail if matlab is in debugging mode

I'd like to receive an email when Matlab is in debugging mode, so I tried the following:
timerTic=4; % how often the timer checks
timerHandle = timer();
timerHandle.startDelay = timerTic;
timerHandle.Period = timerTic;
timerHandle.ExecutionMode = 'fixedRate';
timerHandle.TasksToExecute = inf;
timerHandle.TimerFcn = #CheckDebugMode;
j=0;
while t==0
j=j+1;
end
where the funcion is:
function CheckDebugMode( ~ )
% Check if Matlab is in Debug mode
if feature('IsDebugMode')
sendSSLmail('mymail#mycountry','Matlab is in debug mode','Matlab is in debug mode')
end
t doesn't exist so that an error occurs and Matlab enters in debug mode (dbstop if error is active).
feature('IsDebugMode') is equal to 1, but I receive no mail.
It's the first time I work with objects in Matlab, so I'm pretty sure the code is in someway wrong.
Maybe someone can help me?
Thanks in advance
Do you start your timer?
start(timerHandle)
edit I haven't tested this but I suspect you have problems with your function handle and the function itself. The timer passes arguments to your function, so you need to accept then in your code:
function CheckDebugMode( varargin )
or stop then from being passed:
timerHandle.TimerFcn = #(~,~)CheckDebugMode

Matlab gui returns output arguments too soon

Although I'm a fairly moderate matlabber, I can't get my gui to work. It's intended to be used for me to have a one-click way of organizing my downloads, but it returns the output arguments way too soon.
The code is as follows:
function [answer]=makegui(files,season,episode)
close all
%% Initialize variables
% Input (if empty)
if nargin==0
files{1}='Avengers';
files{2}='NCIS';
files{3}='Would I Lie to You';
season{1}=1;
season{2}=2;
season{3}=3;
episode{1}=11;
episode{2}=22;
episode{3}=24;
else
if length(files)>length(season) || length(files)>length(episode)
sprintf('Not enough filenames provided')
delete(fig)
end
end
width=450;
height=100+25*length(files);
% Function variables
checks=ones(1,length(files));
% Output variables
answer=[];
fig = figure('Name','Fili3','NumberTitle','off','Visible','off','Position',[360,500,width,height],'CloseRequestFcn',#temp); % Create figure
%% Declare buttons
all = uicontrol('Style','checkbox',...
'String','Select all','Position',[50,height-35,70,25],...
'Callback',{#all_Callback},'Value',1);
for i=1:length(files)
item{i}=uicontrol('Style','checkbox',...
'String',files{i},'Position',[75,height-60-25*i,150,25],...
'Callback',#item_Callback,'Value',1);
uicontrol('Style','text','String',sprintf('%d',season{i}),...
'Position',[320,height-60-25*i,25,25]);
uicontrol('Style','text','String',sprintf('%d',episode{i}),...
'Position',[370,height-60-25*i,25,25]);
end
uicontrol('Style','text','String','TV Show Name',...
'Position',[90,height-60,60,15]);
uicontrol('Style','text','String','Season',...
'Position',[300,height-60,60,15]);
uicontrol('Style','text','String','Episode',...
'Position',[350,height-60,60,15]);
options = uicontrol('Style','popupmenu',...
'String',{'Rename and Move','Rename','Move'},...
'Position',[300,height-35,120,25],...
'Value',1);
uicontrol('Style','pushbutton','String','Run',...
'Position',[345,12,30,30],'Callback',#closefunction);
% Make UI visible
fig.Visible='on';
movegui(fig,'center')
%% Callback functions
function all_Callback(source,eventdata)
if source.Value
checks=ones(1,length(files));
else
checks=zeros(1,length(files));
end
source.Value
for j=1:length(files)
item{j}.Value=source.Value;
end
end
function item_Callback(source,eventdata)
S=regexp(files,source.String);
i=find(~cellfun(#isempty,S));
checks(i)=source.Value;
if source.Value==0
all.Value=0;
elseif source.Value==1 && sum(checks)==length(files)
all.Value=1;
end
end
function closefunction(source,eventdata)
answer.checks=checks(:);
answer.mode=options.Value;
delete(fig)
end
function temp(source,eventdata)
delete(fig)
end
end
I added the nargin part to not constantly have to provide input arguments while testing, and later I intend to let the run button and the close function point to the same class. I want it to return answer, but if you run this on your own computer you'll see that it returns it as soon as answer is initialized, and then refuses to do anything else.
It's probably just a small snippet of code I'm forgetting but if someone could point me in the right direction that'd be great!
Thanks,
Tom
Got it! Just needed to add waitfor(fig).

How can I close files that are left open after an error?

I am using
fid = fopen('fgfg.txt');
to open a file.
Sometimes an error occurs before I manage to close the file. I can't do anything with that file until I close Matlab.
How can I close a file if an error occurs?
First of all, you can use the command
fclose all
Secondly, you can use try-catch blocks and close your file handles
try
f = fopen('myfile.txt','r')
% do something
fclose(f);
catch me
fclose(f);
rethrow(me);
end
There is a third approach, which is much better. Matlab is now an object-oriented language with garbage collector. You can define a wrapper object that will take care of its lifecycle automatically.
Since it is possible in Matlab to call object methods both in this way:
myObj.method()
and in that way:
method(myObj)
You can define a class that mimics all of the relevant file command, and encapsulates the lifecycle.
classdef safefopen < handle
properties(Access=private)
fid;
end
methods(Access=public)
function this = safefopen(fileName,varargin)
this.fid = fopen(fileName,varargin{:});
end
function fwrite(this,varargin)
fwrite(this.fid,varargin{:});
end
function fprintf(this,varargin)
fprintf(this.fid,varargin{:});
end
function delete(this)
fclose(this.fid);
end
end
end
The delete operator is called automatically by Matlab. (There are more functions that you will need to wrap, (fread, fseek, etc..)).
So now you have safe handles that automatically close the file whether you lost scope of it or an error happened.
Use it like this:
f = safefopen('myFile.txt','wt')
fprintf(f,'Hello world!');
And no need to close.
Edit:
I just thought about wrapping fclose() to do nothing. It might be useful for backward compatibility - for old functions that use file ids.
Edit(2): Following #AndrewJanke good comment, I would like to improve the delete method by throwing errors on fclose()
function delete(this)
[msg,errorId] = fclose(this.fid);
if errorId~=0
throw(MException('safefopen:ErrorInIO',msg));
end
end
You can try a very neat "function" added by ML called onCleanup. Loren Shure had a complete writeup on it when it was added. It's a class that you instantiate with your cleanup code, then it executes when it goes out of scope - i.e. when it errors, or the function ends. Makes the code very clean. This is a generic version of the class that Andrey had above. (BTW, for complex tasks like hitting external data sources, custom classes are definitely the way to go.)
from the help:
function fileOpenSafely(fileName)
fid = fopen(fileName, 'w');
c = onCleanup(#()fclose(fid));
functionThatMayError(fid);
end % c executes fclose(fid) here
Basically, you give it a function handle (in this case #()fclose(fid))that it runs when it goes out of scope.
Your cleanup code is executed either when an error is thrown OR when it exits normally, because you exit fileOpenSafely and c goes out of scope.
No try/catch or conditional code necessary.
Andrey's solution above is indeed the best approach to this problem. I just wanted to add that throwing an exception in method delete() might be problematic, if you deal with arrays of safefopen objects. During destruction of such an array, MATLAB will call delete() on each array element and, if any delete() throws, then you might end up with leftover open file handles. If you really need to know whether something went wrong during destruction then issuing a warning would be a better option IMHO.
For those that feel lazy to write all the forwarding methods to every MATLAB builtin that uses file handles, you may consider the simple alternative of overloading method subsref for class safefopen:
methods(Access=public)
function varargout = subsref(this, s)
switch s(1).type
case '.'
if numel(s) > 1,
feval(s(1).subs, this.fid, s(2).subs{:});
else
feval(s(1).subs, this.fid);
end
% We ignore outputs, but see below for an ugly solution to this
varargout = {};
otherwise
varargout{1} = builtin('subsref', this, s);
end
end
end
This alternative uses the somewhat ugly feval, but has the advantage of working even if the MATLAB guys (or yourself) decide to add new functions that involve file handles, or if the number/order of the input arguments to a given function change. If you decide to go for the subsref alternative then you should use class safefopen like this:
myFile = safefopen('myfile.txt', 'w');
myFile.fprintf('Hello World!');
EDIT: A disadvantage of the subsref solution is that it disregards all output arguments. If you need the output arguments then you will have to introduce some more ugliness:
methods(Access=public)
function varargout = subsref(this, s)
if nargout > 0,
lhs = 'varargout{%d} ';
lhs = repmat(lhs, 1, nargout);
lhs = ['[' sprintf(lhs, 1:nargout) ']='];
else
lhs = '';
end
switch s(1).type
case '.'
if numel(s) > 1,
eval(...
sprintf(...
'%sfeval(''%s'', this.fid, s(2).subs{:});', ...
lhs, s(1).subs) ...
);
else
eval(...
sprintf('%sfeval(''%s'', this.fid);', ...
lhs, s(1).subs) ...
);
end
otherwise
varargout{1} = builtin('subsref', this, s);
end
end
end
And then you could do things like:
myFile = safefopen('myfile.txt', 'w');
count = myFile.fprintf('Hello World!');
[filename,permission,machineformat,encoding] = myFile.fopen();
fids=fopen('all');
fclose(fids);
%assuming that you want to close all open filehandles