Integrating multiple callbacks in a GUI window

I have been trying for an extended period of time to share data among the apps of a GUI window. I have several codes to format data and navigate through but each time i attempt to create a common code between callbacks I generate an error. I have looked at the MathWorks page for sharing data among callbacks with no luck as of yet. At the moment I need to create a window with an axis, a push button and a slider so that i can test if my slider code is working.
function varargout = push_slider(varargin)
% PUSH_SLIDER MATLAB code for push_slider.fig
% PUSH_SLIDER, by itself, creates a new PUSH_SLIDER or raises the existing
% singleton*.
%
% H = PUSH_SLIDER returns the handle to a new PUSH_SLIDER or the handle to
% the existing singleton*.
%
% PUSH_SLIDER('CALLBACK',hObject,eventData,handles,...) calls the local
% function named CALLBACK in PUSH_SLIDER.M with the given input arguments.
%
% PUSH_SLIDER('Property','Value',...) creates a new PUSH_SLIDER or raises the
% existing singleton*. Starting from the left, property value pairs are
% applied to the GUI before push_slider_OpeningFcn gets called. An
% unrecognized property name or invalid value makes property application
% stop. All inputs are passed to push_slider_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 push_slider
% Last Modified by GUIDE v2.5 06-Apr-2017 13:10:23
% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name', mfilename, ...
'gui_Singleton', gui_Singleton, ...
'gui_OpeningFcn', @push_slider_OpeningFcn, ...
'gui_OutputFcn', @push_slider_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 push_slider is made visible.
function push_slider_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% varargin command line arguments to push_slider (see VARARGIN)
% Choose default command line output for push_slider
handles.output = hObject;
% Update handles structure
guidata(hObject, handles);
% UIWAIT makes push_slider wait for user response (see UIRESUME)
% uiwait(handles.figure1);
% --- Outputs from this function are returned to the command line.
function varargout = push_slider_OutputFcn(hObject, eventdata, handles)
% varargout cell array for returning output args (see VARARGOUT);
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Get default command line output from handles structure
varargout{1} = handles.output;
% --- Executes on button press in pushbutton1.
function pushbutton1_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
guidata(hObject, handles);
myFolder = uigetdir('C:\Users\c13459232\Documents\MATLAB'); % Generate command window to choose a folder
if ~isdir(myFolder) % if the directory is not a valid path
errorMessage = sprintf('Error: the following folder does not exist: \n%s', myFolder); % print this error message
uiwait(warndlg(errorMessage)); % block the execution of program and wait to resume
return;
end
outFolder = fullfile(myFolder, 'output'); % build full file name from parts in folder 'Output'
mkdir(outFolder); % create folder
filePattern = fullfile(myFolder, '*.asc'); % Call all files with '.asc' from the chosen folder
Files = dir(filePattern); % list folder contents
finishCell = cell(length(Files));
for k = 1 : length(Files) % for all files files in the folder
baseFileName = Files(k).name;
FileName = fullfile(myFolder, baseFileName);
fid = fopen(FileName); % open the file from chosen folder
Cell = textscan( fid, '%d', 'delimiter', ';'); % scanning data from files
fclose(fid); % close file from chosen folder
Data = cell2mat(Cell); % convert the cell data to matrix
N = 1024; % Number of numbers per row
skip = 2;
Finish0 = reshape(Data, N, [])'; % reshape the data into the correct format
Finish1 = Finish0(1:skip:end, 1:skip:end);
finishCell{k} = Finish1;
end
% --- Executes on slider movement.
function slider1_Callback(hObject, eventdata, handles)
% hObject handle to slider1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Hints: get(hObject,'Value') returns position of slider
% get(hObject,'Min') and get(hObject,'Max') to determine range of slider
SliderValue = get( handles.slider, 'value');
x = SliderValue;
z = (SliderValue - 1);
Value = contourf(finishCell{x,1});
PreviousValue = contourf(finishCell{z,1});
% --- Executes during object creation, after setting all properties.
function slider1_CreateFcn(hObject, eventdata, handles)
% hObject handle to slider1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles empty - handles not created until after all CreateFcns called
% Hint: slider controls usually have a light gray background.
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
set(hObject,'BackgroundColor',[.9 .9 .9]);
end
I'm not sure how I can specify that the finishCell value created by the push button is what will be navigated through using the slider. Honestly any tips that anyone can provide would be greatly appreciated as staring at this code and mathworks pages has led me here as a last resort.

3 comentarios

Jan
Jan el 10 de Abr. de 2017
I do not get the point. What is the relevant part of the code? "specify that the finishCell value created by the push button is what will be navigated through using the slider" does not let me understand exactly, what you want to achieve.
Adam
Adam el 10 de Abr. de 2017
Editada: Adam el 10 de Abr. de 2017
You can't have looked at the Mathworks page on sharing data among callbacks for very long if you opted to not even try any of the options it gives to share 'finishCell' between your pushbutton callback and the slider callback!
gives numerous options, none of which you have tried in the code you post. Personally I generally use the guidata option, but they all work, in certain situations.
Aaron Smith
Aaron Smith el 10 de Abr. de 2017
Each cell of the finishCell is a matrix that will be plotted using contourf. Then the slider will generate the next contourf plot when the arrow is pushed.
With regards to the guidata, I have tried using it, as well as the GatherAndUpdate method and I can not get them to work. The reason I didn't have them here in the code is to avoid confusion

Iniciar sesión para comentar.

Respuestas (1)

Jan
Jan el 10 de Abr. de 2017
The guidata method is straight forward:
function OpeningFcn(hFigure, EventData, handles)
handles.Data = []; % Initialize properly
handles.Value = [];
guidata(hObject, handles); % Store ucrrent value in the figure
function CallbackXYZ(hObject, EventData, handlesFromInputs)
handles = guidata(hObject); % Get current value from figure
...
handles.Data = rand; % any modifications
disp(handles.Value); % any access
guidata(hObject, handles); % Store handles struct in the figure again
Now the handles struct is available and uptodate in each callback.

13 comentarios

Aaron Smith
Aaron Smith el 10 de Abr. de 2017
Editada: Aaron Smith el 10 de Abr. de 2017
Thanks Jan.
So I put the handles.Data lines and handles.Value lines in the opening function section of the code and inside the square brackets, I should place the label of my data? (In my case, finishCell). What is hObject and handles in the guidata line? I'm just trying to get this as clear as possible in my head. I think it's just that the same titles are used over and over (handles, hObject) in different callbacks that is confusing. I'm not sure how to ascribe a value to them. Thanks again
guidata(hObject, handles);
is always used and should never change.
It just writes the updated handles struct back into the GUI after you have added something new to it.
You don't have to add things in the OpeningFcn, the same idea works across any GUIDE-defined callbacks. Callbacks you create yourself can still use this, but need a little more work to ensure you get handles in and updated correctly.
"hObject" is used as name of the first input of callbacks frequently. It means the handle of the object, which has triggered the callback. A "handle" is the address of a GUI element. The names "handles" for the user-defined data stored in GUIs is a really bad bad bad choice and causes confusions too often. "guidata" might be more useful, but unfortunately this is the name of the function already. :-( But it is a name only.
In my code snippet the fields "handles.Data" and "handles.Value" have been defined as example only. The got a default value in the OpeningFcn to avoid an error, if you press on the buttons in the wrong order. In your case I assume something like this is wanted:
handles.finishCell = {} % Default in the OpeningFcn
handles.finishCell = finishCell; % The definition in the callback
Aaron Smith
Aaron Smith el 20 de Abr. de 2017
Hey Jan, I tried fitting each of my callbacks into a similar format, using the guidata method. The thing is I'm not sure If I have used it correctly because some of my callbacks are much longer than others. I'm also not sure what is needed in the opening function part of the guide code. Could I send my code to you to see if I am using guidata correctly? I would post it here but it is relatively long with comments.
@Aaron: All I could do is using the debugger:
dbstop if error
Then I would run your code and click on all GUI elements in different orders. Matlab would stop if an error occurres. Then I would fix the surrounding code.
But you can do this by your own. This has the advantage that you are the one who knows exactly, what the code should do. If the code runs successfully, you can assume that it is written correctly.
Aaron Smith
Aaron Smith el 27 de Abr. de 2017
Thanks :D
Aaron Smith
Aaron Smith el 28 de Abr. de 2017
Editada: Aaron Smith el 28 de Abr. de 2017
I've had some trouble understanding the exact purpose of some of the lines used in guidata and the significance of their order. This page has helped immensely. On this page it states that any sub-functions in the callback need to be updated before the entire function. From your experience with guidata, would you consider the loops within this callback to be sub-functions? They don't appear to fit that description to me as they do not update the figure, they simply reformat the data before the data is saved again.
% --- Executes on button press in pushbutton1.
function pushbutton1_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
handles = guidata(hObject);
myFolder = uigetdir('C:\Users\c13459232\Documents\MATLAB'); % Generate command window to choose a folder
if ~isdir(myFolder) % if the directory is not a valid path
errorMessage = sprintf('Error: the following folder does not exist: \n%s', myFolder); % print this error message
uiwait(warndlg(errorMessage)); % block the execution of program and wait to resume
return;
end
outFolder = fullfile(myFolder, 'output'); % build full file name from parts in folder 'Output'
mkdir(outFolder); % create folder
filePattern = fullfile(myFolder, '*.asc'); % Call all files with '.asc' from the chosen folder
Files = dir(filePattern); % list folder contents
finishCell = cell(length(Files));
for k = 1 : length(Files) % for all files files in the folder
baseFileName = Files(k).name;
FileName = fullfile(myFolder, baseFileName);
fid = fopen(FileName); % open the file from chosen folder
Cell = textscan( fid, '%d', 'delimiter', ';'); % scanning data from files
fclose(fid); % close file from chosen folder
Data = cell2mat(Cell); % convert the cell data to matrix
N = 1024; % Number of numbers per row
Finish0 = reshape(Data, N, [])'; % reshape the data into the correct format
finishCell{k} = Finish0;
end
message = sprintf( 'Task Complete' );
uiwait(msgbox(message));
guidata(hObject, handles);
handles.pushbutton = finishCell{k};
This is taken directly from my GUI code so there may be some minor bugs or errors still present
Adam
Adam el 28 de Abr. de 2017
Editada: Adam el 2 de Mayo de 2017
Unless your code inside your callback either branches off, passing handles into some other function (in which case, if that function edits anything other than a graphical object or handle-derived class object it will need to return handles as an output argument and reassign it) or triggers some listener (unlikely in your case) that causes the code to jump somewhere else where handles is being used then you just need a single
guidata( hObject, handles )
at the end of the callback. e.g in your case the 'pushbutton' that you add will just get lost instantly when the callback ends because you added it after the guidata.
guidata simply replaces the 'handles' stored in the GUI with the one you give it from your callback. It only needs to be done when your local version of handles is about to go out of scope, or in the cases I mentioned above which don't seem to be relevant to your code.
So long as guidata is called at the end of a callback all your changes to handles will be stored and available in whichever callback is next triggered.
In my code in the previous comment, there is a variable data. I have another callback that needs access to this variable. In order to share it i need to create a subfunction (nested function) inside my main function above so that i can give Data a handle. The problem is Data is in the middle of a control statement (for loop). Do you have any idea how to get around this. Would it be possible for me to end the loop before the Data = cell2mat(cell) statement, create the subfunction and the start another for loop to finish what is now inside the loop?
N = 1024; % Number of numbers per row
Finish0 = reshape(Data, N, [])'; % reshape the data into the correct format
finishCell{k} = Finish0;
I'm not sure I get what you are asking. Data gets thrown away each time round the loop, but you are storing it in your
finishCell
cell array.
If you want to access this in another callback then just something like
handles.finishCell = finishCell;
after the for loop and before the guidata call would make this available elsewhere.
Yeah, you're right. I just realized I have 2 variables named data in my code. I changed the name of one of them. The actual one that needs to be used by multiple callbacks is in this one:
% --- Executes on slider movement.
function slider1_Callback(hObject, eventdata, handles)
% hObject handle to slider1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Hints: get(hObject,'Value') returns position of slider
% get(hObject,'Min') and get(hObject,'Max') to determine range of slider
handles = guidata(hObject);
SliderValue = get( handles.slider, 'value');
Data = (handles.pushbutton{SliderValue,1});
handles.slidervalue = contourf(Data);
guidata(hObject, handles);
In this callback for the slider, which i will use to navigate through multiple plots of matrices, Data specifies the specific matrix to be plotted. Then it is used again in a later callback:
% --- Executes on button press in pushbutton4.
function pushbutton4_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton4 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
handles = guidata(hObject);
binned = sum(Data, 2);
handles.pushbutton6 = stairs(binned);
guidata(hObject, handles);
This callback is for binning the matrix and plotting it with the stairs function. Since there is no loop or control statement in the slider callback, I should be able to insert a sub-function, right?
You shouldn't need any subfunctions to share data, just attach it to the handles structure.
I would strongly advise more meaningful variable/field names though as yours are extremely confusing.
You have a field on your handles structure called 'pushbutton' which looks like it is a cell array from the way you access it. Then you have another called 'slidervalue' which looks like it is a graphics object handle to a contour plot.
In the case of 'Data', it is up to you whether you store it or not. You could just access it directly in your pushbutton4 (again, give it a more meaningful tag!) callback as
Data = handles.pushbutton{ get( handles.slider1, 'value' ), 1 };
I used slider1 here as that seems to make sense although in your code you use handles.slider in slider1_Callback. I don't know if this is just a typo, but it is odd otherwise to access the value of a totally different slider in your slider callback!
The pushbutton handle is a cell array that is made when i open files and reformat them into a cell array. Each of these cells can then be plotted by the slider callback.
From my reading of information on handles, it says that if there is a variable inside a function that is shared with multiple callbacks, a subfunction should be created with the handles structure so that the variable can be accessed by other callbacks.
% --- Executes on slider movement.
function slider1_Callback(hObject, eventdata, handles)
% hObject handle to slider1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Hints: get(hObject,'Value') returns position of slider
% get(hObject,'Min') and get(hObject,'Max') to determine range of slider
handles = guidata(hObject);
function myData(handles)
SliderValue = get( handles.slider, 'value');
Data = (handles.pushbutton{SliderValue,1});
handles.Data = Data;
end
handles.slidervalue = contourf(Data);
guidata(hObject, handles);
end
Now I can share the data variable with other callbacks using the handles structure

Iniciar sesión para comentar.

Categorías

Más información sobre Environment and Settings en Centro de ayuda y File Exchange.

Productos

Preguntada:

el 10 de Abr. de 2017

Comentada:

el 3 de Mayo de 2017

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by