Search code examples
matlabmatlab-guidematlab-gui

Matlab GUI - Trouble updating handles using an update function


I am not sure if I'm going about this the right way, but I want to have a function that when called, essentially resets 4 plots. I store the plots as handles.distplot1, handles.distplot2 and so on in hopes of having a drop down to select which plot is shown on the axis. I will need reset these plots after a few different events, so naturally I want to toss them in a function and avoid code redundancy. I hope a function like

function setupDistPlots(hObject, eventdata, handles)
    % filler data for surfc
    x = [1 2];
    z = zeros(2);
    % setup blank plots for funtion to work with
    a = figure(1); set(a, 'Visible', 'off')
    handles.distplot1 = surfc(x, x, z);
    b = figure(2); set(b, 'Visible', 'off');
    handles.distplot2 = bar(NaN);
    c = figure(3); set(c, 'Visible', 'off')
    handles.distplot3 = surfc(x, x, z);
    d = figure(4); set(d, 'Visible', 'off')
    handles.distplot4 = bar(NaN);
    guidata(hObject, handles);

Which I believe should work as intended, but I have no idea how to call it. In the opening function I try setupDistPlots(hObject, eventdata, handles) but receive the following error when I try to access handles.distplot1 later on:

Reference to non-existent field 'distplot1'.

Error in tabbedGUI>distanceToggle_Callback (line 212)
                distribution(hObject,
                handles.distplot1, ...

Error in gui_mainfcn (line 95)
        feval(varargin{:});

Error in tabbedGUI (line 45)
    gui_mainfcn(gui_State, varargin{:});

Error in
@(hObject,eventdata)tabbedGUI('distanceToggle_Callback',hObject,eventdata,guidata(hObject))


Error while evaluating UIControl Callback

edit: Also please point out everything I could improve. Everything I do in Matlab feels hackish like there must be a better way.

edit2: The problem with the opening function was calling setupDistPlots before the guidata(hObject, handles) call of the opening function. However now when when I call `setupDistPlots' again on a button press I get the following error:

Error using matlab.graphics.primitive.Data/set
Invalid or deleted object.

Error in andrewDistribution (line 45)
    set(hplot1, 'xData', x1, 'yData', y1, 'zData', zeros(length(x1)))

Error in tabbedGUI>distanceToggle_Callback (line 200)
                distribution(hObject, handles.distplot1, ...

Error in gui_mainfcn (line 95)
        feval(varargin{:});

Error in tabbedGUI (line 45)
    gui_mainfcn(gui_State, varargin{:});

Error in @(hObject,eventdata)tabbedGUI('distanceToggle_Callback',hObject,eventdata,guidata(hObject))


Error while evaluating UIControl Callback

Solution

  • I'm assuming your initial attempt looked like this:

    % --- Executes just before testgui is made visible.
    function testgui_OpeningFcn(hObject, eventdata, handles, varargin)
    % Choose default command line output for testgui
    handles.output = hObject;
    
    setupDistPlots(hObject, eventdata, handles)
    
    % Update handles structure
    guidata(hObject, handles);
    

    As talked to in the comments, your changes to the handles structure saved to the GUI by setupDistPlots using guidata are being overwritten by the GUIDE's subsequent guidata call. The short answer is to put setupDistPlots after guidata to make everything function as written.


    Now the longer answer. I'm going to guess that you're familiar mostly working with MATLAB scripts rather than MATLAB functions. Where scripts share a base workspace, functions each have their own separate workspace in memory. As written there is no way for your OpeningFcn to know that you have changed the handles structure, so it happily overwrites your changes using the value of handles that was passed to setupDistPlots. To get around this, you need to include some way for the OpeningFcn to know that you have made changes.

    One approach is to specify an output for setupDistPlots:

    function handles = setupDistPlots(hObject, eventdata, handles)
        % filler data for surfc
        x = [1 2];
        z = zeros(2);
        % setup blank plots for funtion to work with
        a = figure(1); set(a, 'Visible', 'off')
        handles.distplot1 = surfc(x, x, z);
        b = figure(2); set(b, 'Visible', 'off');
        handles.distplot2 = bar(NaN);
        c = figure(3); set(c, 'Visible', 'off')
        handles.distplot3 = surfc(x, x, z);
        d = figure(4); set(d, 'Visible', 'off')
        handles.distplot4 = bar(NaN);
    

    Placing setupDistPlots before the guidata call in your GUIDE code:

    % --- Executes just before testgui is made visible.
    function testgui_OpeningFcn(hObject, eventdata, handles, varargin)
    % Choose default command line output for testgui
    handles.output = hObject;
    
    handles = setupDistPlots(hObject, eventdata, handles);
    
    % Update handles structure
    guidata(hObject, handles);