I am building a GUI in MATLAB (2016a) which I will be compiling and deploying. I want to try to do some global error handling, and it occurs to me that any command given to the GUI (button click, etc) first goes through the main initialization code before going to the specific Callback function. My thought was to put a try-catch block around the calls to gui_mainfcn. What's making me hesitate is that the code is bookended by some big old warnings:
% Begin initialization code - DO NOT EDIT
... initialization code here ...
% End initialization code - DO NOT EDIT
Could I break something by putting a try-catch block inside this initialization section? Is there a better way to attempt global error handling for a single GUI?
There is no reason that you can't insert global error handling in the main function of your GUIDE GUI. The warnings are really there to prevent people from inadvertently disrupting GUI functionality. In your case, a try/catch isn't going to actually modify the functionality so you're fine. You just want to be sure to not remove the calls to gui_mainfcn which is an internal function which contains all of the GUI logic.
Aside from that, you will also want to ensure that all requested output arguments are populated so that in the case of an error (for a function call where an output argument is expected) no error (within your catch block) is thrown because of that. That should be easy enough though
Also, I would only wrap the calls to gui_mainfcn
try
if nargout
[varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
gui_mainfcn(gui_State, varargin{:});
end
catch ME
% Go ahead and fill in the requested outputs with [] so we don't get an error
[varargout{1:nargout}] = deal([]);
% Do special error handling here
fprintf('Caught error: %s\n', ME.message);
end
Related
I was making a GUI using GUIDE in Matlab. My computer froze and Matlab crashed. When I went to reopen GUIDE, I get the following error:
Undefined function or variable 'badfcn_CreateFcn'.
Error in gui_mainfcn (line 95)
feval(varargin{:});
Error in quick_gui (line 42)
gui_mainfcn(gui_State, varargin{:});
Error in
matlab.graphics.internal.figfile.FigFile/read>#(hObject,eventdata)quick_gui('badfcn_CreateFcn',hObject,eventdata,guidata(hObject))
However, this function badfcn_CreateFcn does not exist anywhere in my gui.m file. There is also no object in my gui with this tag or description.
Is there a way to fix this?
The issue was that the tag of a ui object had been changed, but the Callback and Create functions were using the old tag name. This won't be apparent in the object browser. Since I have everything in panels, I did what #marco wassmer recommended and created the function with a breakpoint. This is how I found that the object was residing in Panel X. I went through all objects in Panel X and sure enough, one of them was using the badfcn tag for the Callback and Create functions even though the tag name was different.
There have been some similar questions posed here but none seem to answer my following question. I have a function with a menu inside the function. This function is embedded within a main function. If the user hits "Quit" on the menu gui, I want the function to stop on that line and not continue AND I want the main function to stop as well.
But the function also has variable outputs, so I always get the error: "Output arguments {var} not assigned during call to {function}"
I have tried using "return", "break" or others, but have not seen a simple solution to this problem. Any ideas are appreciated.
function main
%do stuff
function [a,b,c,d]=sub_main
imenu=menu(' ','yes','no','quit');
if imenu==3
%RETURN, BREAK OR SOMETHING!
end
a=1; b=2; c=3; d=4;
end
end
My matlab program is a multiple window programmatic GUI. I have implemented a reporting system so when an error is encountered, it calls a function I wrote, generateReport.m, which sends an e-mail with some log and state info and then continues execution.
To accomplish this I have put a try-catch block in EVERY single function. That means even creating a wrapper for my main function. Does anybody know of a way to avoid this? I.e. being able to but a global try-catch. The reason I need multiple try-catch blocks right now is because try-catch will catch errors in functions within the block, but not sub-functions of those
Example psuedo-code:
try:
segmentImage
catch:
generateReport
end
^-- This way an error in segment-image calls generateReport, however an error in a subfunction of segment-image won't. Optimally I would only need one try-catch (or some other statement/structure I'm not aware of) in each file.
Most of the files code one GUI window each. Some are just utility.
I wrote a similar question before: matlab can't catch error in subfunction
That question asked how to use the try-catch or some function-wrapper in a callback, to implement the reporting system I have now. Before I just wanted to know why I couldn't catch errors in subfunctions. I put try-catches in every subfunction to solve that.
This question is different as I'm asking is if there's another way to do this instead of putting a try-catch in every function and subfunction which is really inconvenient and doesn't look that great. Maybe a technique I don't know about to do this or a more efficient way of structuring my code to accomplish this is needed?
Example of multiple try-catches:
First the main function I run, which just wraps CSTMainWindow
function CeleST
try
% Global try-catch on CeleST
CSTMainWindow()
catch exception
generateReport(exception)
end
Within CSTMainWindow: I have to put try-catch blocks on it's subfunctions. In this example given CSTProcessVideos and CSTCheckResults are programmatic GUI files
function processVideo(hObject,eventdata) %#ok<INUSD>
try
set(mainFigure,'Visible','off');
CSTProcessVideos
set(mainFigure,'Visible','on');
flagConsistentButton = false;
checkSequences
populateFilters
catch exception
generateReport(exception)
end
end
function checkResults(hObject,eventdata) %#ok<INUSD>
try
set(mainFigure,'Visible','off');
CSTCheckResults
set(mainFigure,'Visible','on');
flagConsistentButton = false;
checkSequences
populateFilters
catch exception
generateReport(exception)
end
end
I would like to know if there's something I could do to avoid putting a try-catch on everything (I also put the try-catch blocks on subfunctions that don't contain code written in other files)
I hope my question was clear. Thanks in advance for the help
Preliminaries. Since your application is event-driven (i.e. performs actions by callbacks) you have at least two potential sources of errors: 1) the function that builds your GUI, and then exits, 2) every callback assigned to the active UI elements.
The GUI builder. Errors in building the GUI should exit the application. Which means that your function should look like
function main(arg1, arg2, ...)
try
% test your arguments
% create the main window
% add ui elements
% register the callbacks
% create backup for application state
catch ME
generateReport(ME);
end;
end
without putting a try/catch in every helper function that is called. Since the GUI creation is faulty, the catch block should not attempt to restore the application to a (safe) default state, because there is no such default state to begin with.
The callbacks. Errors in callbacks are most likely due to wrong user input, so there is a way to rollback the application to the last known safe state:
function ui1_cbk(h, varargin)
try
% test your arguments
% perform required action
% update the backup state with the actual state
catch ME
generateReport(ME);
% restore state from last good backup
% let the user know something went wrong
end;
end
function ui2_cbk(h, varargin)
try
% ...
catch ME
% ...
end;
end;
Please note that, for UI elements that have a single callback registered to them, there is one easy way to handle all actions in a single function, thus having a single try/catch block:
function ui_general_cbk(h, varargin)
try
switch get(h, 'Tag')
case 'Tag_ui1'
% test your arguments
% perform required action
case 'Tag_ui2'
% test your arguments
% perform required action
% ...
otherwise
% ignore request
end;
% update the backup state with the actual state
catch ME
generateReport(ME);
% restore state from last good backup
% let the user know something went wrong
end;
end
Of course, for this to work, your GUI builder should assign unique (and maybe suggestive) tags to all your active UI elements.
How to generate error reports. The try/catch block could be only at the top level of the main function and the callback; to see what exactly caused the exception, one can always inspect the .stack struct array of the ME object, and—for fancy exception handling—eventually the .cause field (which would be another MException object if not empty). Like this one avoids polluting all the functions with exception handling on every level.
I'm trying to implement a bug reporting system in my code so I put a try/catch around the function that is run to start the program. It's a programmatic GUI so most of the subfunctions are callbacks for buttons or other GUI elements. However whenever an error is thrown in these subfunctions, it is not caught. Some of the subfunctions are defined in other files as they are other programmatic GUI files.
My question is, is there anyway to catch errors that are more than one function level deep?
Example below:
I run CeleST to start the program
function CeleST
try
% Global try-catch
CSTMainWindow()
catch exception
generateReport(exception) % bugReporter
end
CSTMainWindow is a programattic GUI file and here is one of the pushbuttons:
uicontrol('parent',mainPanel,'style','pushbutton','string','1. Process videos...','position',[500 yFilters+hFilters+10 170 60],'callback',#processVideo);
However errors in processVideo are not caught
processVideo:
function processVideo(hObject,eventdata) %#ok<INUSD>
set(mainFigure,'Visible','off');
CSTProcessVideos % Programmatic GUI File for another window
set(mainFigure,'Visible','on');
flagConsistentButton = false;
checkSequences
populateFilters
end
Even putting undefined variables in the subfunctions throws errors but they're not caught by my try/catch. Any suggestions or am I doing something wrong? Do I really have to put try-catch blocks around everything?
GTSMainWindow is not calling processVideo. Instead the function is used as a callback and called later.
Basically every callback function must care for it's own errors, put the try catch into the processVideo function and it will catch the error.
I have a compiled matlab program that automatically tunes machine parameters. At the end of a tuning cycle I need to reistate some original settings. Sometime unexpected errors occur and sometimes the user can see that tuning algorithm is not working properly so should be terminated (with control-C).
If a predictable error occurs, I can reinstate parameters with a try catch block. However, if an unexpected error occurs or the user invokes control-C, the program exits without passing though the catch clause and the machine is left in an undefined state.
I know I can register a cleanup function that will run as my working functions finish, either normally, via an predictable or unpredictable error, or control-C event. Ideally, the the clean up function would do nothing and pass control to the top level to clean up if a predictable error occurs. If a control-C event or an unpredictable error occurs, clean up function should warn the user that the program failed so they clean up manually.
To do this, I need to make the fact of a predicted or not-predicted termination (i.e. control-C) known to the clean up function at run time. I know that the cleanup function takes a copy of parameter values when it is registered, and that these cannot be changed at run time, so passing in a parameter cannot work. What I believe should work is nesting the cleanup function within my working function so local variables in the enclosing function are available to the clean up function. This however does not work.
My question is: can anyone see a way to make just a single boolean available to a clean function so it can select between normal and abnormal clean up?
Here is some contrived example code that I believe should work.
function do_tuning
% Set this false to cause a warning messages for control-C or MATLAB coding errors.
normalTermination = false;
oc = onCleanup(#() my_clean_up());
tuningError = tuning_function()
if tuningError
% Set this true to suppress the warning message if a predictable error occurs.
normalTermination = true;
error('tuningError')
end
% Set this true to suppress the warning message if the function runs to completion.
normalTermination = true;
function my_clean_up
if ~normalTermination
disp('Warning: clean up failed. Please clean up manually');
end
end
end
Running real code following this pattern causes the error:
Undefined function or variable "normalTermination".
The onCleanup object calls the function from outside the workspace of the do_tuning function so using a nested function will not help...
Infact the documetation states:
Your cleanup routine should never rely on variables that are defined outside of that routine
[edit - based on comments] A better solution would be to change the problem and have all cleaning up done within the my_clean_up function, which may require it to determine if what needs doing (or to have generic behaviour which can always be applied)
If we disregard this warning and solve the question...
Passing varables between workspaces is easiest with global variables so for the example code above.
Firstly define the variable as global before initially setting it to false (otherwise the variable may be overwritten)
function do_tuning
%# snip
global normalTermination
normalTermination = false;
%# snip
Secondly define the variable as global in the my_clean_up callback function before using it to retrieve the value
function my_clean_up
global normalTermination
if ~normalTermination
%# snip
Warning as ever with global variables this is susceptible to the value of the global variable being edited elsewhere at the wrong time.
Here is a modification of my example that behaves as I want, using the global suggested by #RTL.
function do_tuning
global normalTermination;
% Set this false to cause a warning messages for control-C or MATLAB coding errors.
normalTermination = false;
oc = onCleanup(#() my_clean_up());
tuningError = tuning_function()
if tuningError
% Set this true to suppress the warning message if a predictable error occurs.
normalTermination = true;
error('tuningError')
end
% Set this true to suppress the warning message if the function runs to completion.
normalTermination = true;
function my_clean_up
if ~normalTermination
disp('Warning: clean up failed. Please clean up manually');
else
disp('Normal termination, you can relax.');
end
end
end
A more elegant solution can be achieved by modifying the onCleanup code. You could easily add a property or method to cancel the normal behaviour. Example:
classdef onCleanupCancel < handle
properties
active = true;
end
properties(SetAccess = 'private', GetAccess = 'public', Transient)
task = #nop;
end
methods
function h = onCleanupCancel(functionHandle)
h.task = functionHandle;
end
function delete(h)
if h.active
h.task();
end
end
end
end
function nop
end
At the last line of your function set the active of the onCleanupCancel object to false...