Doug's MATLAB Video Tutorials

Video: Saving the state of a GUI 13

Posted by Doug Hull,

A question that comes across my inbox from time to time is how to save the state of a GUI between session. There are few methods for doing this, one is using the HGSAVE command to save the .FIG file for the GUI. This can be done in a way that modifies the original .FIG file, or you can save a different one. Another way is to explicitly query each of the UICONTROLS for their value and then save those values in a structure. You can later reload and set the values. Personally, I am very willing to do more typing to get explicit control over activities like this. The saved state .MAT file is very readable, and would allow you to save state that might not be reflected in the UICONTROLS. Also, this helps to keep the saved data separate from the files that create GUI components. The modular architecture is appealing. Finally, for people that are building their entire GUI programmatically (that is, using UICONTROL to position widgets instead of GUIDE) this method is really useful because you do not ever really load a .FIG file.
MATLAB Central Files Icon Also, be sure to take some time to explore the new look of the File Exchange. Lots of new features.

13 CommentsOldest to Newest

It is useful but a little to simple for the task that I have. My application is huge, I do not know exactly the number of handles but it is close to 300. What do I do in this situation? On top of that I do not save only the values that they hold … I need to save also the position because the GUI has tabs meaning that at some point a large number of graphic elements are hidden.

I needed to save the GUI status so that the user can reset the GUI at any time with just one click and avoid closing and opening the entire application. Later on I will use the same data to save the status for a later session of the application (like demonstrated in your movie).

The solution that I found was to save all the graphic handles in a structure (similar to your solution) but in a more automatic manner. Here is the code for that:

% — start code here
function saveGUIStatus(obj, handles);
% Method for saving GUI elements properties
% obj – the object that sends the data
% handles – the GUI handles structure

handlesFields = fieldnames(handles); % extract all the field names in handles
tmpdata = cell(size(handlesFields)); % make a tmp data cell
obj.GUIprop = cell2struct(tmpdata, handlesFields, 1); % make a structure with the same field names as handles

for(i = 1:length(handlesFields)) % index all the fields
test = ishandle(handles.(handlesFields{i})); % test if the name of field is a handle reference
if(test)
obj.GUIprop.(handlesFields{i}) = get(findobj(‘Tag’, handlesFields{i})); % save the properties
else
obj.GUIprop = rmfield(obj.GUIprop, handlesFields{i}); % remove the element because is not a GUI handle
end
end
% — end code here

The application is using object oriented programing but one can disregard the obj. structure like reference. The code above basically saves only the widgets from the handles structure. By using the get function with only the tag name of the element I extract all the properties of the widget.

Now I will load that data saved previously in the GUIprop structure. The code here:

% —- start code here
function resetGUI(obj, handles);
% Method for reseting GUI elements properties
% obj – the object that sends the data
% handles – the GUI handles structure

h = warndlg(‘Reseting the application! Please whait …’,’!! Reset !!’, ‘modal’);
handlesFields = fieldnames(obj.GUIprop); % extract all the field names in handles

for(i = 2:length(handlesFields) – 1) % index all the fields (do not take figure1 properties)
test = ishandle(handles.(handlesFields{i})); % test if the name of field is a handle reference
if(test)
setableProps = set(findobj(‘Tag’, handlesFields{i})); % extract the user accessible properties
setableNames = fieldnames(setableProps); % extract only the names of properties
allProps = fieldnames(obj.GUIprop.(handlesFields{i})); % all the properties of the element
[tf, loc] = ismember(allProps, setableNames); % map the properties to update
valuesOriginal = struct2cell(obj.GUIprop.(handlesFields{i})); % extract the values
values = valuesOriginal(tf)'; % filter the accessible values
set(findobj(‘Tag’, handlesFields{i}), setableNames, values); % save the properties
end
end
% —- end code here

When running in reverse, setting instead of getting, Matlab is not allowing the user to set all the values of the widget. This means that I have to filter again this time using the set function with only the tag of the widget.

The code is working but some times it is not giving me the expected result. Some times elements that are suppose to be hidden will not hide and the GUI looks out of place after reseting. To debug the code is difficult again because of the large amount of elements.

What can I do in this situation? How can I find the rezone for improper reset of the GUI? At this stage it seams that if I execute the function resetGUI two times the things get to be done properly. This is not a solution first because I can’t ask the user to click twice the button just because I have a bug and second the process is slow (I need to speed up the code but I will do that after I make it work).

Any ideas about all this? Is there a more clever way of doing this? Thank you in advance!

One more thing … For the entire GUI reset maybe hgsave and hgload are a good idea … thank you for that! But what do I do if I need to reset only a portion of the GUI, 100 elements instead of 300!?

Caitlin,

It seems like you have a pretty good idea of what is going on here already. I think that you can speed your code by avoiding the use of FINDOBJ, you already have the handle to it in the handles structure.

I think this is the pseudo code implementation that I would recommend:

for i = 1 : numel(handles)
storedHandle.val = get(handles.name, ‘value’)
other relevent states, like string, there should only be a few you need to capture.
end

Other states that your GUI might have seems to be the current tab. You apparently have a tab implementation of some sort, so that data should be stored somewhere also (I expect you are using GETAPPDATA and SETAPPDATA for this) So store that value also.

As part of your “restore function” I would expect that you could call the “Go to this Tab” function and that would reset the state also.

By removing the findObj, you will likely find this code goes way faster, and I think that will help make the other problems easier to deal with.

-Doug

Hi Doug,

First of all thank you for your reply and I apologize for the late answer.

In the beginning I have created this classes with the idea that eventually I will used them in different projects and this is why I have used FINDOBJ. Initially I was afraid to send the handle structure because at runtime will hold a lot of data and I considered that the application will use a lot of memory (I am still not sure about that one).

So I could execute this line:

handlesFields = fieldnames(handles); % extract all the field names in handles

before I call the function listed above. In this way the function code is independent of the handles structure presence.

I did not use GETAPPDATA and SETAPPDATA because I found it useless (when I started programming GUIs). From the documentation I understood that the UserData is the field that holds this additional data for each widget that I have. But in the first place I usually need the string or the current value of the widget so why should I save something in the UserData? I feel like I am missing something here!

I will take a closer look at the things that you suggested. The use of handle structure instead of FINDOBJ is definitely something that I will consider because I know it take time to find the objects.

And one more thing! I am doing something like:

handles.varA = varAClass; % create an instance of a class

Where varA is an instance of the class varAClass. In this way varA is accessible from any callback function in the GUI.

Could I do something like:

varATmp = varAClass; % create a temporary instance
setappdata(figurehandle,’varA’,varATmp); % store the data in the appdata.

If this is a possibility then what will happen with the class methods? How do I have access to the class instance? Do I need to use getappdata or I just use the variable name (in this case varA.methodA())? Do I still have to use a call like handle.varA?

I know that this post is taking another root that the original but I do not know where to continue the discussion. If you have any suggestions please say so.

Thank you again for the help and the time spent on my post!

Regards and Happy New Year (it is almost time for that :))

Catalin

Hi Doug,

I am able to save and restore the states of all gui objects except the private handle.

for eg: handles.private.bmatrix.
I can save the state, but unable to restore.

here goes my code;

function saveState(handles)

state.resliced_images_edit = get(handles.resliced_images_edit, ‘String’);
state.maskimage_radiobutton = get(handles.maskimage_radiobutton,’Value’);

state.bmatrix = handles.private.bmatrix;

save images.mat state;
—————————————————-
function restoreState(handles)

load ‘images.mat’ ‘state’
set(handles.resliced_images_edit, ‘String’,state.resliced_images_edit);
set(handles.maskimage_radiobutton,’Value’,state.maskimage_radiobutton);
handles.private.bmatrix = state.bmatrix;
———————————————————

Please help!

Thanks in advance!

Lekshmi

@Lekshmi,

It all depends what state.bmatrix and handles.private.bmatrix are! Please elaborate.

Doug

Hi Doug –

I’m having a lot of difficulty trying to code a way to not only save and restore the state of a GUI in the same session (as you illustrate here), but to restore the state of a GUI after starting a NEW Matlab session. The approach you describe here seems to fail when in a new Matlab session because the handles data from the previous session can’t be restored. Is there a way to apply a GUI restoration method that will work within a new/different Matlab session? Thanks for any help you can provide.

@Jason,

Nothing we are doing here needs the same handles structure. We are only saving the values, not the specific handles. What are you storing in the MATLAB file?

I have a callback function called saveMAT that looks like:

function saveMAT_Callback(hObject, eventdata, handles)
SessionName = evalin(‘base’,’SessionName’);
filename = [SessionName ‘.txt’];
fidOUT = fopen(filename, ‘a’);
h = waitbar(0.5,’Saving workspace data…’);
guidata(hObject, handles);
getstate(handles,1);
evalin(‘base’, [‘save(”’, SessionName ”’)’]);
set(handles.EndSessionButton,’Enable’,’on’);
newline = ‘———————————————-‘;
newline2 = char([‘Master File ‘, SessionName, ‘.mat created/updated.’]);
fprintf(fidOUT,’%s\n’,newline,newline2);
fclose(fidOUT);
delete(h);
clc;

The function getstate saves all the handles I want to restore into a structure variable ‘state’ and saves the variable as a .mat file, as you describe in your videos. It does so by doing this repeatedly:

state.SessionName = get(handles.SessionName);
state.createsession = get(handles.createsession);
state.openoldsession = get(handles.openoldsession);
state.LoadData = get(handles.LoadData);

so that the structure state.* contains all the handle data for GUI element *.

The problem arises when I start a new MATLAB session and I want to restore the GUI using the handle data in ‘state’. When the GUI is initialized, the handles are just floating point numbers, not pointers to elements in my GUI. So, when I try a command like set(handles.*,state.*) I get an error that says that handles.* is not a valid handle structure. Given that I store all the handle information from my previous session in ‘state’, how then do I restore this data to the GUI when it opens after the beginning of a new MATLAB session?

Hello !

Thank you very much for your code, it is very useful ! Maybe I ask something ? How can I do so that when the user inputs are loaded back in the fields of the GUI, they automatically update the workspace without having to press ENTER in every field ?

Thank you very much again ! :=)

@Matt,

Sorry, but the inputs are not ‘noticed’ by MATLAB until Enter is pressed, or the focus is changed from that control.

Doug

Add A Comment

What is 1 + 10?

Preview: hide

These postings are the author's and don't necessarily represent the opinions of MathWorks.