matlabuser-interfacehandlesobjecthandles

MATLAB GUI: how to update handles when new objects are created in a subfunction?


I have a problem with my MATLAB GUI.

I am not using GUIDE so I am self coding everything for my GUI. I have a Main which generates the a first panel of the GUIDE. This panel contains a button which can create other 2 panels through its related callback function. In the other 2 panels it is possible to do other operations calling other callback functions. One of these operations is to create 2 new static texts and 2 new editable texts. I have problems in updating the handles related to those editable texts. More precisely I am not able to retrieve the value of their string once the text is edited and the callback functions of the last 2 panels are called again.

Attached is the code where all the callback functions of the GUI are:

%% server_selected callback function
function server_selected(hObject, eventdata, handles)
%UNTITLED8 Summary of this function goes here
%   Detailed explanation goes here

% Get server version and run LaunchCDbQuery
servers = get(handles.server_popup, 'String');
serverVersion = servers{get(handles.server_popup, 'Value')};
[Day] = LaunchCDbQuery(serverVersion);
assignin('base', 'Day', Day)

% Update current outing on GUI
set(handles.outing_text, 'String', strcat(Day.date, Day.DocumentKey))

% Tool description
% Create panel for the tool description
handles.description_panel = uipanel('Title', 'Tool description',...
    'units', 'normalized',...
    'position', [0.675, 0.025, 0.3, 0.9]);
% Create items inside the panel for the tool description
% Function heading
handles.funheading_text = uicontrol('Parent', handles.description_panel,...
    'Style', 'text',...
    'units', 'normalized',...
    'position', [0, 0.7, 1, 0.2],...
    'String', 'Please choose a tool and click description to obtain the tool''s heading and description.',...
    'HorizontalAlignment', 'left');
% Function description
handles.description_text = uicontrol('Parent', handles.description_panel,...
    'Style', 'text',...
    'units', 'normalized',...
    'position', [0, 0.05, 1, 0.6],...
    'HorizontalAlignment', 'left');

% Tool selection
% Create panel for the tool selection
handles.tool_panel = uipanel('Title', 'Tool selection',...
    'units', 'normalized',...
    'position', [0.35 0.025 0.3 0.9]);
% Create items inside the panel for the tool selection
% Text
handles.tool_text = uicontrol('Parent', handles.tool_panel,...
    'Style', 'text',...
    'units', 'normalized',...
    'position', [0 0.7 1 0.2],...
    'String', 'Please choose a tool to perform a piece of analysis.',...
    'HorizontalAlignment', 'left');
% Popup
handles.tool_popup = uicontrol('Parent', handles.tool_panel,...
    'Style', 'popup',...
    'units', 'normalized',...
    'position', [0.2 0.25 0.6 0.4],...
    'String', {'plotmaptg'; 'TestReview'});    
% Input variables panel
handles.varin_panel = uipanel('Parent', handles.tool_panel,...
    'Title', 'Type input variables',...
    'units', 'normalized',...
    'position', [0, 0, 1, 0.3]);
% Description push
handles.tool_push_description = uicontrol('Parent', handles.tool_panel,...
    'Style', 'pushbutton',...
    'units', 'normalized',...
    'position', [0.05 0.4 0.4 0.1],...
    'String', 'Description',...
    'callback', {@tool_description, handles});
% Ok push
handles.tool_push_ok = uicontrol('Parent', handles.tool_panel,...
    'Style', 'pushbutton',...
    'units', 'normalized',...
    'position', [0.51 0.4 0.4 0.1],...
    'String', 'Ok',...
    'callback', {@tool_selected, handles, Day});

% Update guidata
guidata(hObject, handles)
end

%% tool_description callback function
function tool_description(hObject, eventdata, handles)
%UNTITLED2 Summary of this function goes here
%   Detailed explanation goes here

% Call handles function
handles = tool_description_handles(handles);

% Update guidata
guidata(hObject, handles)
end

function newHandles = tool_description_handles(handles)
%UNTITLED Summary of this function goes here
%   Detailed explanation goes here

% Get tool name
tools = get(handles.tool_popup, 'String');
tool_selected = tools{get(handles.tool_popup, 'Value')};

% Open tool .m file
fid = fopen(strcat(tool_selected, '.m'), 'r');

% Read .m file to find function description and save it
line = fgets(fid);
heading = line;
while isempty(regexp(line, '%', 'ONCE'))
    line = fgets(fid);
end
description = [];
while ~isempty(regexp(line, '%', 'ONCE'))
    description = strcat(description, line(regexp(line, '%', 'ONCE'):end));
    line = fgets(fid);
end
description(regexp(description, '%')) = [];
fclose(fid);

% Set descritption found to the description handle
set(handles.funheading_text, 'String', heading);
set(handles.description_text, 'String', description);

% Find the input variables needed to run the tool
global inputs varout
[varin, varout] = get_arg_names(strcat(pwd, '\Tools\', tool_selected, '.m'));
inputs = cell(1, length(varin{1,1}));
for i = 1:length(varin{1,1})
    % Input variable text
    handles.varin_text(i) = uicontrol('Parent', handles.varin_panel,...
        'Style', 'text',...
        'units', 'normalized',...
        'position', [0, 1-i*(1/length(varin{1,1})), 0.45, 1/length(varin{1,1})],...
        'String', varin{1,1}{i,1},...
        'HorizontalAlignment', 'left');
    % Input variables editable text
    handles.varin_edit(i) = uicontrol('Parent', handles.varin_panel,...
        'Style', 'edit',...
        'units', 'normalized',...
        'position', [0.55, 1-i*(1/length(varin{1,1})), 1, 1/length(varin{1,1})],...
        'HorizontalAlignment', 'left',...
        'callback', {@varin_callback, handles});
end

% Save handles
newHandles = handles;
end

function varin_callback(hObject, eventdata, handles)
% hObject    handle to edit1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Save in the main workspace the input variables
global inputs
for i = 1:length(inputs)
    if isempty(inputs{1,i})
        inputs{1,i} = get(hObject,'String');
        break
    end
end
end


%% tool_selected callback function
function tool_selected(hObject, eventdata, handles, Day)
%UNTITLED Summary of this function goes here
%   Detailed explanation goes here

% Get tool name
tools = get(handles.tool_popup, 'String');
tool = tools{get(handles.tool_popup, 'Value')};
% fh = str2func(tool);

% Get tool inputs and outputs
global inputs varout

% Run the tool
if ~isempty(varout)
    expression = strcat('out = ', tool, '(');
else
    expression = strcat(tool, '(');
end
for i = 1:length(inputs)
    if ~isempty(inputs{1,i})
        expression = strcat(expression, inputs{1,i}, ',');
    else
        break
    end
end
expression(end) = ')';
eval(expression)

% Update guidata
guidata(hObject, handles)
end

I have tried using guidata(hObject, handles) at the end of each function but it did not work out. As now I am using global variables to avoid the problem but I would really like to just update the handles as they are modified.


Solution

  • When providing an input variable to a callback function, the variable that is passed when you invoke the callback is that variable as it exists when the callback is defined. MATLAB does not know to update these inputs as you modify guidata.

    You can see this with the following example:

    function testcode
    h.mf = figure('Menubar', 'none', 'NumberTitle', 'off', 'ToolBar', 'none');
    
    h.lb = uicontrol('Parent', h.mf, 'Style', 'Listbox', ...
                     'Units', 'Normalized', 'Position', [0.1 0.5 0.4 0.4] ...
                     );
    
    h.b1 = uicontrol('Parent', h.mf, 'Style', 'pushbutton', ...
                     'Units', 'Normalized', 'Position', [0.1 0.1 0.4 0.3], ...
                     'String', 'Pass handles','Callback', {@button1push, h});
    
    h.b2 = uicontrol('Parent', h.mf, 'Style', 'pushbutton', ...
                     'Units', 'Normalized', 'Position', [0.5 0.1 0.4 0.3], ...
                     'String', 'Use GUIdata', 'Callback', @button2push);
    
    guidata(h.mf, h);
    makepanel(h.mf);
    end
    
    function makepanel(mf)
    h = guidata(mf);
    h.panel = uipanel('Parent', h.mf, 'Title', 'A Panel', ...
                      'Units', 'Normalized', 'Position', [0.5 0.5 0.4 0.4] ...
                      );
    guidata(h.mf, h);
    end
    
    function button1push(~, ~, h)
    h.lb.String = fieldnames(h);
    end
    
    function button2push(hObj, ~)
    h = guidata(hObj);
    h.lb.String = fieldnames(h);
    end
    

    Push each button and look at the output in the listbox. You can see that the change to h made in makepanel is not shown when you hit Button 1.

    Rather than passing handles as a callback input (like Button 1 above), call guidata with an output at the beginning of your function to obtain the most current version of handles. If your callback makes changes to the handles structure, call guidata again to store these changes for other callbacks.