An Address Book Reader

Address Book Reader Example Outcome

This example shows how to implement a GUI that displays names and phone numbers, which it reads from a MAT-file.

address book GUI

Techniques Used in This Example

This example demonstrates the following GUI programming techniques:

Managing Shared Data

One of the key techniques illustrated in this example is how to keep track of information and make it available to the various subfunctions. This information includes

The descriptions of the subfunctions that follow illustrate how to save and retrieve information from the handles structure. See handles Structure for background information on this structure.

View Completed Layout and Its GUI M-File

If you are reading this in the MATLAB® Help browser, you can click the following links to display the GUIDE Layout Editor and the MATLAB Editor with a completed version of this example. This enables you to see the values of all component properties and to understand how the components are assembled to create the GUI. You can also see a complete listing of the code that is discussed in the following sections.

Running the GUI

The GUI is nonblocking and nonmodal since it is designed to be displayed while you perform other MATLAB tasks.

GUI Option Settings

This GUI uses the following GUI option settings:

Calling the GUI

You can call the GUI M-file with no arguments, in which case the GUI uses the default address book MAT-file, or you can specify an alternate MAT-file from which the GUI reads information. In this example, the user calls the GUI with a pair of arguments, address_book('book', 'my_list.mat'). The first argument, 'book', is a key word that the M-file looks for in the opening function. If the M-file finds the key word, it knows to use the second argument as the MAT-file for the address book. Calling the GUI with this syntax is analogous to calling it with a valid property-value pair, such as ('color', 'red'). However, since 'book' is not a valid figure property, in this example the opening function in the M-file includes code to recognize the pair ('book', 'my_list.mat').

Note that it is not necessary to use the key word 'book'. You could program the M-file to accept just the MAT-file as an argument, using the syntax address_book('my_list.mat'). The advantage of calling the GUI with the pair ('book', 'my_list.mat') is that you can program the GUI to accept other user arguments, as well as valid figure properties, using the property-value pair syntax. The GUI can then identify which property the user wants to specify from the property name.

The following code shows how to program the opening function to look for the key word 'book', and if it finds the key word, to use the MAT-file specified by the second argument as the list of contacts.

function address_book_OpeningFcn(hObject, eventdata,...
    handles, varargin)
% Choose default command line output for address_book
handles.output = hObject;
% Update handles structure
guidata(hObject, handles);
% User added code follows
if nargin < 4
    % Load the default address book
    Check_And_Load([],handles);
    % If the first element in varargin is 'book' and 
    & the second element is a MATLAB file, then load that file
elseif (length(varargin) == 2 && ...
       strcmpi(varargin{1},'book') && ...
       (2 == exist(varargin{2},'file')))
    Check_And_Load(varargin{2},handles);
else
    errordlg('File Not Found','File Load Error')
    set(handles.Contact_Name,'String','')
    set(handles.Contact_Phone,'String','')
end

Loading an Address Book Into the Reader

There are two ways in which an address book (i.e., a MAT-file) is loaded into the GUI:

Validating the MAT-file

To be a valid address book, the MAT-file must contain a structure called Addresses that has two fields called Name and Phone. The Check_And_Load subfunction validates and loads the data with the following steps:

Check_And_Load Code Listing

This is the Check_And_Load function.

function pass = Check_And_Load(file,handles)
% Initialize the variable "pass" to determine if this is
% a valid file.
pass = 0;
% If called without any file then set file to the default
% filename.
% Otherwise, if the file exists then load it.
if isempty(file)
	file = 'addrbook.mat';
	handles.LastFile = file;
	guidata(handles.Address_Book,handles)
end
if exist(file) == 2
	data = load(file);
end
% Validate the MAT-file
% The file is valid if the variable is called "Addresses"
% and it has fields called "Name" and "Phone"
flds = fieldnames(data);
if (length(flds) == 1) && (strcmp(flds{1},'Addresses'))
	fields = fieldnames(data.Addresses);
	if (length(fields) == 2) && ...
         (strcmp(fields{1},'Name')) && ...
         (strcmp(fields{2},'Phone'))
       pass = 1;
	end
end
% If the file is valid, display it
if pass
	% Add Addresses to the handles structure
	handles.Addresses = data.Addresses;	
	guidata(handles.Address_Book,handles)	
	% Display the first entry
	set(handles.Contact_Name,'String',data.Addresses(1).Name)
	set(handles.Contact_Phone,'String',data.Addresses(1).Phone)
	% Set the index pointer to 1 and save handles
	handles.Index = 1;
	guidata(handles.Address_Book,handles)
else
	errordlg('Not a valid Address Book','Address Book Error')
end

The Open Menu Callback

The address book GUI contains a File menu that has an Open submenu for loading address book MAT-files. When selected, Open displays a dialog (uigetfile) that enables the user to browse for files. The dialog displays only MAT-files, but users can change the filter to display all files.

The dialog returns both the filename and the path to the file, which is then passed to fullfile to ensure the path is properly constructed for any platform. Check_And_Load validates and load the new address book.

Open_Callback Code Listing

function Open_Callback(hObject, eventdata, handles)
[filename, pathname] = uigetfile( ...
	{'*.mat', 'All MAT-Files (*.mat)'; ...
		'*.*','All Files (*.*)'}, ...
	'Select Address Book');
% If "Cancel" is selected then return
if isequal([filename,pathname],[0,0])
	return
% Otherwise construct the fullfilename and Check and load 
% the file
else
	File = fullfile(pathname,filename);
	% if the MAT-file is not valid, do not save the name
	if Check_And_Load(File,handles)
		handles.LastFIle = File;
		guidata(hObject, handles)
	end
end

See the Creating Menus section for information on creating the menu.

The Contact Name Callback

The Contact Name text box displays the name of the address book entry. If you type in a new name and press enter, the callback performs these steps:

Storing and Retrieving Data

This callback makes use of the handles structure to access the contents of the address book and to maintain an index pointer (handles.Index) that enables the callback to determine what name was displayed before it was changed by the user. The index pointer indicates what name is currently displayed. The address book and index pointer fields are added by the Check_And_Load function when the GUI is run.

If the user adds a new entry, the callback adds the new name to the address book and updates the index pointer to reflect the new value displayed. The updated address book and index pointer are again saved (guidata) in the handles structure.

Contact Name Callback

function Contact_Name_Callback(hObject, eventdata, handles)
% Get the strings in the Contact Name and Phone text box
Current_Name = get(handles.Contact_Name,'string');
Current_Phone = get(handles.Contact_Phone,'string');
% If empty then return
if isempty(Current_Name)
	return
end
% Get the current list of addresses from the handles structure
Addresses = handles.Addresses;
% Go through the list of contacts
% Determine if the current name matches an existing name
for i = 1:length(Addresses)
	if strcmp(Addresses(i).Name,Current_Name)
		set(handles.Contact_Name,'string',Addresses(i).Name)
		set(handles.Contact_Phone,'string',Addresses(i).Phone)
		handles.Index = i;
		guidata(hObject, handles)
		return
	end
end
% If it's a new name, ask to create a new entry
Answer=questdlg('Do you want to create a new entry?', ...
	'Create New Entry', ...
	'Yes','Cancel','Yes');			
switch Answer
case 'Yes'
	Addresses(end+1).Name = Current_Name; % Grow array by 1
	Addresses(end).Phone = Current_Phone; 
	index = length(Addresses);
	handles.Addresses = Addresses;
	handles.Index = index;
	guidata(hObject, handles)
	return			
case 'Cancel'
	% Revert back to the original number
	
set(handles.Contact_Name,'String',Addresses(handles.Index).Name
)
	
set(handles.Contact_Phone,'String',Addresses(handles.Index).Pho
ne)
	return
end

The Contact Phone Number Callback

The Contact Phone # text box displays the phone number of the entry listed in the Contact Name text box. If you type in a new number click one of the push buttons, the callback opens a question dialog that asks you if you want to change the existing number or cancel your change.

Like the Contact Name text box, this callback uses the index pointer (handles.Index) to update the new number in the address book and to revert to the previously displayed number if the user selects Cancel from the question dialog. Both the current address book and the index pointer are saved in the handles structure so that this data is available to other callbacks.

If you create a new entry, you must save the MAT-file with the File > Save menu.

Code Listing

function Contact_Phone_Callback(hObject, eventdata, handles)
Current_Phone = get(handles.Contact_Phone,'string');
% If either one is empty then return
if isempty(Current_Phone)
	return
end
% Get the current list of addresses from the handles structure
Addresses = handles.Addresses;
Answer=questdlg('Do you want to change the phone number?', ...
	'Change Phone Number', ...
	'Yes','Cancel','Yes');			
switch Answer
case 'Yes'
	% If no name match was found create a new contact
	Addresses(handles.Index).Phone = Current_Phone; 
	handles.Addresses = Addresses;
	guidata(hObject, handles)
	return		
case 'Cancel'
	% Revert back to the original number
	set(handles.Contact_Phone,...
       'String',Addresses(handles.Index).Phone)
	return
end

Paging Through the Address Book — Prev/Next

The Prev and Next buttons page back and forth through the entries in the address book. Both push buttons use the same callback, Prev_Next_Callback. You must set the Callback property of both push buttons to call this subfunction, as the following illustration of the Prev push button Callback property setting shows.

property inspector showing settings for the prev U I control

Determining Which Button Is Clicked

The callback defines an additional argument, str, that indicates which button, Prev or Next, was clicked. For the Prev button Callback property (illustrated above), the Callback string includes 'Prev' as the last argument. The Next button Callback string includes 'Next' as the last argument. The value of str is used in case statements to implement each button's functionality (see the code listing below).

Paging Forward or Backward

Prev_Next_Callback gets the current index pointer and the addresses from the handles structure and, depending on which button the user presses, the index pointer is decremented or incremented and the corresponding address and phone number are displayed. The final step stores the new value for the index pointer in the handles structure and saves the updated structure using guidata.

Code Listing

function Prev_Next_Callback(hObject, eventdata,handles,str)
% Get the index pointer and the addresses
index = handles.Index;
Addresses = handles.Addresses;
% Depending on whether Prev or Next was clicked,
% change the display
switch str
case 'Prev'
	% Decrease the index by one
	i = index - 1;	
	% If the index is less than one then set it equal to the index
   %  of the last element in the Addresses array
	if i < 1
		i = length(Addresses);
	end
case 'Next'
	% Increase the index by one
	i = index + 1;	
	% If the index is greater than the size of the array then 
	% point to the first item in the Addresses array
	if i > length(Addresses)
		i = 1;
	end	
end
% Get the appropriate data for the index in selected
Current_Name = Addresses(i).Name;
Current_Phone = Addresses(i).Phone;
set(handles.Contact_Name,'string',Current_Name)
set(handles.Contact_Phone,'string',Current_Phone)
% Update the index pointer to reflect the new index
handles.Index = i;
guidata(hObject, handles)

Saving Changes to the Address Book from the Menu

When you make changes to an address book, you need to save the current MAT-file, or save it as a new MAT-file. The File submenus Save and Save As enable you to do this. These menus, created with the Menu Editor, use the same callback, Save_Callback.

The callback uses the menu Tag property to identify whether Save or Save As is the callback object (i.e., the object whose handle is passed in as the first argument to the callback function). You specify the menu's Tag property with the Menu Editor.

Saving the Addresses Structure

The handles structure contains the Addresses structure, which you must save (handles.Addresses) as well as the name of the currently loaded MAT-file (handles.LastFile). When the user makes changes to the name or number, the Contact_Name_Callback or the Contact_Phone_Callback updates handles.Addresses.

Saving the MAT-File

If the user selects Save, the save command is called to save the current MAT-file with the new names and phone numbers.

If the user selects Save As, a dialog is displayed (uiputfile) that enables the user to select the name of an existing MAT-file or specify a new file. The dialog returns the selected filename and path. The final steps include

Save_Callback Code Listing

function Save_Callback(hObject, eventdata, handles)
% Get the Tag of the menu selected
Tag = get(hObject, 'Tag');
% Get the address array
Addresses = handles.Addresses;
% Based on the item selected, take the appropriate action
switch Tag
case 'Save'
	% Save to the default addrbook file
	File = handles.LastFile;
	save(File,'Addresses')
case 'Save_As'
	% Allow the user to select the file name to save to
	[filename, pathname] = uiputfile( ...
		{'*.mat';'*.*'}, ...
		'Save as');	
	% If 'Cancel' was selected then return
	if isequal([filename,pathname],[0,0])
		return
	else
		% Construct the full path and save
		File = fullfile(pathname,filename);
		save(File,'Addresses')
		handles.LastFile = File;
		guidata(hObject, handles)
	end
end

The Create New Menu

The Create New menu simply clears the Contact Name and Contact Phone # text fields to facilitate adding a new name and number. After making the new entries, the user must then save the address book with the Save or Save As menus. This callback sets the text String properties to empty strings:

function New_Callback(hObject, eventdata, handles)
set(handles.Contact_Name,'String','')
set(handles.Contact_Phone,'String','')

The Address Book Resize Function

The address book defines its own resize function. To use this resize function, you must set the Application Options dialog Resize behavior to User-specified, which in turn sets the figure's ResizeFcn property to:

address_book('ResizeFcn',gcbo,[],guidata(gcbo))

Whenever the user resizes the figure, MATLAB software calls the ResizeFcn subfunction in the address book M-file (address_book.m)

Behavior of the Resize Function

The resize function allows users to make the figure wider, to accommodate long names and numbers, but does not allow the figure to be made narrower than its original width. Also, users cannot change the height. These restrictions do not limit the usefulness of the GUI and simplify the resize function, which must maintain the proper proportions between the figure size and the components in the GUI.

When the user resizes the figure and releases the mouse, the resize function executes. At that point, the resized figure's dimensions are saved. The following sections describe how the resize function handles the various possibilities.

Changing the Width

If the new width is greater than the original width, set the figure to the new width.

The size of the Contact Name text box changes in proportion to the new figure width. This is accomplished by:

If the new width is less than the original width, use the original width.

Changing the Height

If the user attempts to change the height, use the original height. However, because the resize function is triggered when the user releases the mouse button after changing the size, the resize function cannot always determine the original position of the GUI on screen. Therefore, the resize function applies a compensation to the vertical position (second element in the figure Position vector) by adding the vertical position when the mouse is released to the height when mouse is released and subtracting the original height.

When the figure is resized from the bottom, it stays in the same position. When resized from the top, the figure moves to the location where the mouse button is released.

Ensuring the Resized Figure Is On Screen

The resize function calls movegui to ensure that the resized figure is on screen regardless of where the user releases the mouse.

When the GUI is first run, it is displayed at the size and location specified by the figure Position property. You can set this property with the Property Inspector when you create the GUI.

Code Listing

function ResizeFcn(hObject, eventdata, handles)
% Get the figure size and position
Figure_Size = get(hObject, 'Position');
% Set the figure's original size in character units
Original_Size = [ 0 0 94 19.230769230769234];
% If the resized figure is smaller than the 
% original figure size then compensate.
if (Figure_Size(3)<Original_Size(3)) | ...
   (Figure_Size(4) ~= Original_Size(4))
   if Figure_Size(3) < Original_Size(3)
       % If the width is too small then reset to origianl width.
       set(hObject, 'Position',...
           [Figure_Size(1), Figure_Size(2), ...
            Original_Size(3), Original_Size(4)])
       Figure_Size = get(hObject, 'Position');
   end	
   if Figure_Size(4) ~= Original_Size(4)
       % Do not allow the height to change. 
       set(hObject, 'Position',...
           [Figure_Size(1),...
            Figure_Size(2)+Figure_Size(4)-Original_Size(4),...
            Figure_Size(3), Original_Size(4)])
   end 
end
% Adjust the size of the Contact Name text box.
% Set the units of the Contact Name field to 'Normalized'.
set(handles.Contact_Name,'units','normalized')
% Get its Position.
C_N_pos = get(handles.Contact_Name,'Position');
% Reset it so that it's width remains normalized. 
% relative to figure.
set(handles.Contact_Name,'Position',...
	[C_N_pos(1) C_N_pos(2)  0.789 C_N_pos(4)])
% Return the units to 'Characters'.
set(handles.Contact_Name,'units','characters')
% Reposition GUI on screen.
movegui(hObject, 'onscreen')
  


 © 1984-2008- The MathWorks, Inc.    -   Site Help   -   Patents   -   Trademarks   -   Privacy Policy   -   Preventing Piracy   -   RSS