0

I'm trying to create a simple gui in matlab and have some problems. The main idea of this simplified program is that I have a slider, it determines the frequency of a sine wave. Two plots show the resulting signal. A high res in 44100 Hz, and a low res in 100 Hz. Another slider determines the length of the signal being plotted (also this measured in Hz). The original program has quite a few more sliders to add more frequencies, but since I didn't think it was important I tried to scale it back for this question. The resulting code is still quite large, sorry for that.

My problem is it doesn't always update, and I get error messages. I try to store everything in handles, since I think this is how you're supposed to do it. handles.lowresx contains the low res time scale, handles.highresx contains the high res time scale. A temporary lowresy and highresy is then created in the function calcandplot(handles). Every time the time interval slider is moved, the function recalcx(hObject, newhz, handles) is called from the slider movement callback and a new lowresx and lowresy is calculated. It is then stored in guidata(hObject,handles) (I would hope) and these are used to calculate new lowresy and highresy for plotting. It doesn't seem to be stored though.

I'm not sure how to save the data now when it is a nested function inside the callback function. Am I supposed to call guidata(hObject, handles) all the way up in the call stack, meaning I have to pass down hObject as an argument to every function? Or only in the most inner function? I've tried both and none really work. And is it enough to just calculate lowresy and highresy and plot them, if I don't need them later, or should I save them in handles too, to make everything work right?

Do I have to call guidata(handles) after calling set(handles.intervaltext, 'String', num2str(val)) or does it update itself?

And I have a question about handles. The way I understand it a copy is created and passed down every time a function is called. Does this pose some kind of restrictions on how big data structures you can save there for it to be efficient? If a copy is created for every gui component when some kind of event is called on it (mouseover, key pressed etc) I can certainly see how things can get sluggish. Any tips for how to handle this?

Error message:

Reference to non-existent field 'lowresx'.

Error in gui>calcandplot (line 85)
lowresy = wave(handles.lowresx, handles.freq1);

Error in gui>intervalslider_Callback (line 106)
calcandplot(handles);

Code:

function varargout = gui(varargin)
% GUI MATLAB code for gui.fig
%      GUI, by itself, creates a new GUI or raises the existing
%      singleton*.
%
%      H = GUI returns the handle to a new GUI or the handle to
%      the existing singleton*.
%
%      GUI('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in GUI.M with the given input arguments.
%
%      GUI('Property','Value',...) creates a new GUI or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before gui_OpeningFcn gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to gui_OpeningFcn via varargin.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Edit the above text to modify the response to help gui

% Last Modified by GUIDE v2.5 12-Oct-2016 14:18:38

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @gui_OpeningFcn, ...
                   'gui_OutputFcn',  @gui_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT


% --- Executes just before gui is made visible.
function gui_OpeningFcn(hObject, eventdata, handles, varargin)
handles.freq1 = 0;
handles.lowsr = 1000;
handles.sr = 44100;
handles.lenhz = 220;
recalcx(hObject, handles.lenhz, handles);
'recalculated'

% Choose default command line output for gui
handles.output = hObject;

% Update handles structure
guidata(hObject, handles);

% UIWAIT makes gui wait for user response (see UIRESUME)
% uiwait(handles.figure1);


% --- Outputs from this function are returned to the command line.
function varargout = gui_OutputFcn(hObject, eventdata, handles) 
varargout{1} = handles.output;

% --- Executes on slider movement.
function slider1_Callback(hObject, eventdata, handles)
val = get(hObject,'Value');
handles.freq1 = val;
guidata(hObject, handles);
set(handles.text1, 'String', num2str(val));
calcandplot(handles);

% --- Executes during object creation, after setting all properties.
function slider1_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end

function calcandplot(handles)
lowresy = wave(handles.lowresx, handles.freq1);
highresy = wave(handles.highresx, handles.freq1);
axes(handles.axes1);
plot(handles.lowresx,lowresy, 'o');
axes(handles.axes2);
plot(handles.highresx,highresy, 'o');

function y = wave(x, freq1)
% x in sec
y = sin(x*freq1);

% --- Executes on slider movement.
function intervalslider_Callback(hObject, eventdata, handles)
val = get(hObject,'Value');
recalcx(hObject, val, handles);

strcat('val is now ', num2str(val))
strcat('handles.lenhz is now', num2str(handles.lenhz))
guidata(hObject, handles);
set(handles.intervaltext, 'String', num2str(val));
handles
calcandplot(handles);

% --- Executes during object creation, after setting all properties.
function intervalslider_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end

function recalcx(hObject, newhz, handles)
handles.lenhz = newhz;
handles.lowresx = (0:1/handles.lowsr:(2*pi)/handles.lenhz)';
handles.highresx = (0:1/handles.sr:(2*pi)/handles.lenhz)';
strcat('inside handles is', num2str(handles.lenhz))
guidata(hObject, handles);
strcat('inside handles again is', num2str(handles.lenhz))
Suever
  • 64,497
  • 14
  • 82
  • 101
user1661303
  • 539
  • 1
  • 7
  • 20

1 Answers1

1

The issue is that within recalcx, you are modifying handles by adding the field lowresx. You (correctly) save this to the guidata using guidata(hObject, handles).

The issue is that in the calling function (gui_OpeningFcn), you then save a different handles struct that doesn't have those fields to the guidata since the modifications to the handles structure within recalcx aren't propagated back to the calling function.

It is this handles struct without those fields that is then passed around your GUI and causes the error you're seeing.

One option for how to fix this would be to have recalcx return the modified handles struct

function handles = recalcx(hObject, newhz, handles)
    handles.lenhz = newhz;
    handles.lowresx = (0:1/handles.lowsr:(2*pi)/handles.lenhz)';
    handles.highresx = (0:1/handles.sr:(2*pi)/handles.lenhz)';
    strcat('inside handles is', num2str(handles.lenhz))
    guidata(hObject, handles);
    strcat('inside handles again is', num2str(handles.lenhz))
end

And then the calling function can have an updated version

function gui_OpeningFcn(hObject, eventdata, handles, varargin)
    handles.freq1 = 0;
    handles.lowsr = 1000;
    handles.sr = 44100;
    handles.lenhz = 220;
    handles = recalcx(hObject, handles.lenhz, handles);

    % Choose default command line output for gui
    handles.output = hObject;

    % Update handles structure
    guidata(hObject, handles);
Graham
  • 7,431
  • 18
  • 59
  • 84
Suever
  • 64,497
  • 14
  • 82
  • 101
  • Thanks. Thought it would be something like that. I've tried reading up on this from multiple sources, but can't really get my head straight around it. Why isn't it enough to just update it in recalcx function with guidata(hObject,handles) and disable it in gui_OpeningFcn? Wouldn't it be saved then, assuming I don't make any more changes to it in gui_OpeningFcn? I read somewhere you should never overwrite handles, only change fields in it, so I guess that's why I was afraid to pass it as a value in the recalcx function. – user1661303 Oct 12 '16 at 15:32
  • 1
    @user1661303 any changes made to the `handles` struct in `recalcx` function do not exist in the `handles` variable of the calling function. Since you save the `guidata` in the calling function *after* calling `recalcx` it overwrites the "good" `handles` struct. – Suever Oct 12 '16 at 15:56