Related
I have an array. I am processing elements of this array in a for loop inside a function.
function results = processArray(array)
for ii = 1:length(array)
%some stuff here
results(ii) = %getting the results for this particular element
end
end
There might be a lot of elements and computations might take a lot of time. I want to be able to finish execution of the for loop at any arbitrary time when a user wants to do that so that the results for already processed elements would be available.
I was trying to make a figure with a button which would change a boolean flag. Inside the for loop I was checking the value of that boolean. If the boolean changed then the for loop should break.
function results = processArray(array)
fig = figure;
fig.UserData.continue = 1;
uicontrol('Parent', fig', 'Style', 'pushbutton', 'String', 'stop', 'callback', #interrupt)
for ii = 1:length(array)
if(fig.UserData.continue == 0)
break;
end
%some stuff here
results(ii) = %getting the results for this particular element
end
end
function interrupt(obj, ~)
fig = obj.Parent;
fig.UserData.continue = 0;
end
well, that does not work. The figure shows up only after all the computations are done already. If I draw the figure first using something like waitforbuttonpress and proceed to the for loop pressing the button does not stop the execution. I think the callback function is being executed only after the for loop is finished. Is there any way to solve this?
You will need to drawnow after you create the button, so it will show up. You also need to drawnow within the loop to update the button state. Then you should achieve what you want.
drawnow force figure update, so it will slow down your computation a little.
I am using Matlab to develop an application which performs several mathematical operations, whose parameters can be changed when the mouse is clicked, as in the example below.
while time<endtime
calculate_manythings;
if ~mod(time,checkmouse)
mouseinput_timeout(timemouse, gca);
change_calculation_parameters;
end
time=time+1;
end
At the moment, I am pausing the operations periodically to check for mouse events, but this is slow and unpractical. How can I monitor these continually and run the code at the same time? Could I make the mouse event checks a background process using parfeval, for example?
Many thanks,
Marta
you can use callback functions. here I used 'ButtonDownFcn':
timeinterval = 1; % seconds between mouse clicks
% generate axes with callback function
h = plot(rand(1,2),'LineWidth',6);
set(gca,'ButtonDownFcn',#callback);
% reset Tag and time
h.Tag = '';
tic;
while true
drawnow;
if strcmp(h.Tag,'Pressed') % if pressed
t = toc; % check time passed
if t >= timeinterval
% change parameters
disp('Pressed');
h.Color = rand(1,3);
% reset timer
tic;
end
% reset Tag
h.Tag = '';
end
end
and the callback function is:
function callback(src,~)
src.Children(1).Tag = 'Pressed';
end
I'm building a MATLAB GUI using GUIDE for processing medical images (brain tumor segmentation on MRI scans). As a preprocessing step, the program coregisters different scans, which looks like this:
I now want to display crosshairs on both images as a visual check of the coregistration. The crosshairs should be linked to one another, such that they point to the same pixel in both images. Moreover, it should move when hovering over (or clicking on) one of the images. This is what I want to achieve:
Does a build-in MATLAB function exist that can achieve this? Or, if I have to write it myself, how would one tackle this problem?
I'll use a Toy GUI in order to show how it works :
Let's first try to make it work for the first image only. What you want to achieve is :
When the user clicks on the figure, we want to have access to the location of the mouse click.
Then we want to check if the click was located inside the first image
If it is, we want to update the position of the crossbar, which will be represented by two lines overlayed on the image and crossing at the selected point.
Step 0 : Initial settings
You want to make sure of some details first :
Make sure you add a call to hold on after both your imshow calls, otherwise the crossbars will delete your image instead of overlaying on it.
Step 1 : Getting the position of the mouse click
In your GUI opening function (here it will be SuperDuperGUI_OpeningFcn), you want to add a call to :
set(gcf, 'WindowButtonDownFcn', #getMousePositionOnImage);
This will trigger the function getMousePositionOnImage everytime the user clicks inside your GUI.
Then, you want to add and implement the function getMousePositionOnImage :
function getMousePositionOnImage(src, event)
% Fetch the current handles structure
handles = guidata(src);
% Get the coordinate IN THE AXES UNITS OF AXES1 (Here I chose to define them
% as pixels) of the point where the user clicked
cursorPoint = get(handles.axes1, 'CurrentPoint')
Step 2 : Check if the mouse click is inside the first image
Still in the getMousePositionOnImage function :
% Get the Position of the first image (We're only interested by the width
% and height of the axes1 object)
Img1Pos=get(handles.axes1,'Position')
% Check if inside
if(cursorPoint(1,1)<Img1Pos(3)&&cursorPoint(1,2)<Img1Pos(4)&&cursorPoint(1,1)>=0&&cursorPoint(1,2)>=0)
% Do stuff
end
Step 3 : If the click was inside the first image, update the position of the crossbar
% Check if inside
if(cursorPoint(1,1)<Img1Pos(3)&&cursorPoint(1,2)<Img1Pos(4)&&cursorPoint(1,1)>=0&&cursorPoint(1,2)>=0)
Lines=findobj('Type','line','Parent',handles.axes1);
if isempty(Lines)
% If Lines is empty, we need to create the line objects
line([0 Img1Pos(3)],[cursorPoint(1,2) cursorPoint(1,2)],'Color','g','Parent',handles.axes1);
line([cursorPoint(1,1) cursorPoint(1,1)],[0 Img1Pos(4)],'Color','g','Parent',handles.axes1);
else
% If not, we just update the fields XData and YData of both Lines
Lines(1).XData=[0 Img1Pos(3)]; % Unnecessary but I'll leave it there for clarity
Lines(1).YData=[cursorPoint(1,2) cursorPoint(1,2)];
Lines(2).XData=[cursorPoint(1,1) cursorPoint(1,1)];
Lines(2).YData=[0 Img1Pos(4)]; % Unnecessary but I'll leave it there for clarity
end
end
Result :
Now I'll let you do the last part, which involves linking the two crossbars. Instead of only checking if the click was on first image, you'll check both separately. Then, if it is in one of the image, you'll update both crossbar to the position of the click in the right axes object
Edit
The code below is inspired by the answer of BillBokeey, for which I'm very grateful.
First, do steps 0 and 1 of BillBokeey's solution. Then I put this function in my code:
function getMousePositionOnImage(src, event)
% Fetch the current handles structure
handles = guidata(src);
% Get the coordinate IN THE AXES UNITS OF AXES1 (Here I chose to define them
% as pixels) of the point where the user clicked
cursorPoint1 = get(handles.axes2, 'CurrentPoint');
cursorPoint2 = get(handles.axes3, 'CurrentPoint');
% Get the Position of the first image (We're only interested by the width
% and height of the axes1 object)
Img1Pos = getpixelposition(handles.axes2);
Img2Pos = getpixelposition(handles.axes3);
XLim1 = get(handles.axes2, 'XLim');
YLim1 = get(handles.axes2, 'YLim');
XLim2 = get(handles.axes3, 'XLim');
YLim2 = get(handles.axes3, 'YLim');
% Check if inside
if (cursorPoint1(1)<XLim1(2) && cursorPoint1(2)<YLim1(2) && cursorPoint1(1)>=XLim1(1) && cursorPoint1(1)>=YLim1(1))
Lines1=findobj('Type','line','Parent',handles.axes2);
Lines2=findobj('Type','line','Parent',handles.axes3);
if isempty(Lines1)
% If Lines is empty, we need to create the line objects
line(XLim1,[cursorPoint1(1,2) cursorPoint1(1,2)],'Color','g','Parent',handles.axes2);
line([cursorPoint1(1,1) cursorPoint1(1,1)],YLim1,'Color','g','Parent',handles.axes2);
line(XLim2,[cursorPoint1(1,2) cursorPoint1(1,2)],'Color','g','Parent',handles.axes3);
line([cursorPoint1(1,1) cursorPoint1(1,1)],YLim2,'Color','g','Parent',handles.axes3);
else
% If not, we just update the fields XData and YData of both Lines
Lines1(1).XData=XLim1; % Unnecessary but I'll leave it there for clarity
Lines1(1).YData=[cursorPoint1(1,2) cursorPoint1(1,2)];
Lines1(2).XData=[cursorPoint1(1,1) cursorPoint1(1,1)];
Lines1(2).YData=YLim1; % Unnecessary but I'll leave it there for clarity
Lines2(1).XData=XLim2; % Unnecessary but I'll leave it there for clarity
Lines2(1).YData=[cursorPoint1(1,2) cursorPoint1(1,2)];
Lines2(2).XData=[cursorPoint1(1,1) cursorPoint1(1,1)];
Lines2(2).YData=YLim2; % Unnecessary but I'll leave it there for clarity
end
elseif (cursorPoint2(1)<XLim2(2) && cursorPoint2(2)<YLim2(2) && cursorPoint2(1)>=XLim2(1) && cursorPoint2(1)>=YLim2(1))
Lines1=findobj('Type','line','Parent',handles.axes2);
Lines2=findobj('Type','line','Parent',handles.axes3);
if isempty(Lines2)
% If Lines is empty, we need to create the line objects
line(XLim1,[cursorPoint2(1,2) cursorPoint2(1,2)],'Color','g','Parent',handles.axes2);
line([cursorPoint2(1,1) cursorPoint2(1,1)],YLim1,'Color','g','Parent',handles.axes2);
line(XLim2,[cursorPoint2(1,2) cursorPoint2(1,2)],'Color','g','Parent',handles.axes3);
line([cursorPoint2(1,1) cursorPoint2(1,1)],YLim2,'Color','g','Parent',handles.axes3);
else
% If not, we just update the fields XData and YData of both Lines
Lines1(1).XData=XLim1; % Unnecessary but I'll leave it there for clarity
Lines1(1).YData=[cursorPoint2(1,2) cursorPoint2(1,2)];
Lines1(2).XData=[cursorPoint2(1,1) cursorPoint2(1,1)];
Lines1(2).YData=YLim1; % Unnecessary but I'll leave it there for clarity
Lines2(1).XData=XLim2; % Unnecessary but I'll leave it there for clarity
Lines2(1).YData=[cursorPoint2(1,2) cursorPoint2(1,2)];
Lines2(2).XData=[cursorPoint2(1,1) cursorPoint2(1,1)];
Lines2(2).YData=YLim2; % Unnecessary but I'll leave it there for clarity
end
end
I have an external function say "external_func" (seperate .m file)
Inside this function a while loop is called, and this while loop update a variabl named "update_prog"
Now I will pass this value into the GUIDE using
assignin('base', 'update_prog', update_prog); % passing to workspace
I am making this
"update_prog" as global variable and calling it into GUIDE .m file
function pb1_Callback(hObject, eventdata, handles)
global update_prog
% hObject handle to pb1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% else
% set(handles.pb1,'enable','on');
% end
% update_prog first value prints, but it wont updates as the loop in external_func goes on.
drawnow;
set(handles.slider1,'Value',update_prog)
external_func;
so here in the GUIDE .m file I can get the value
"update_prog" but it wont keep up with the while loop. I used "drawnow" but its of no use.
How can I refine this value "update_prog" as the while loop in the "external_func" goes through multiple iterations. [Note: The updated values are there in the callback function of GUIDE, but unless there is a callback the callback function wont update the "update_prog"], so how can I acheive this real-time update inside a call_back function.
[Note: passing the variables through function input is not possible here in my case, so I am looking for alternatives]
Edit1: please consider this link, which has an exampleWhich may clarify you what I am trying to acheive
What I am doing here is
Passing the variable(which is being updated in the while loop of the externel function) into GUI.
I will use this variable to show the progress on the progress bar(Slider).
What is the problem?
1. The variable inside the GUI callback(Consdier I will press a push button and then it will call the function with while loop) will put the updated values into the set(handles.slider,'Value',variable)
By doing this I cant move the slider.
Why?
Callback updates the variable only when I press the push button, next all the updates to the variable will not be updated, so progress bar/slider wont move.
I wouldn't recommend to pass your variable in 3 steps with an intermediate workspace (external->base workspace->GUI). I would rather recommend to pass your variable directly (external->GUI).
Each Matlab figure offers a space to store variables (Application Data) of any type. I would suggest reading the article Share Data Among Callbacks and read the documentation for the 3 functions:
guidata
setappdata
getappdata
This way will offer you much more control over the scope of your variables and you won't need any global declaration.
Below is an example of a simple gui. The gui declare the variable in it's user space (with setappdata), then uses a timer to periodically read this variable (with getappdata).
The external function do whatever you want it to do (just a random number in the example), then to update the variable you use the same setappdata. The only thing you need for that is the handle of the main GUI figure, so in the example I give it as input of the external function.
The GUI also has two buttons to start and stop the update.
The code for the main example GUI 'theGui.m' is :
function h = theGui
%// basic GUI with 2 buttons and 1 slider
h.fig = figure('Position',[433 434 500 100],'Menubar','none','CloseRequestFcn',#my_closefcn) ;
h.sld = uicontrol('Style','Slider','Position',[20 20 460 20]) ;
h.btnStart = uicontrol('Style','pushbutton','String','Start updating','Callback',#btnStart_callback,'Position',[20 50 200 30]);
h.btnStop = uicontrol('Style','pushbutton','String','Stop updating','Callback',#btnStop_callback,'Position',[280 50 200 30],'Max',1,'Min',0);
%// Define the timer
h.t = timer ;
h.t.Period = 0.1 ; %// 0.1s refresh interval
h.t.TimerFcn = {#timer_callback,h.fig} ;
h.t.ExecutionMode = 'fixedSpacing' ;
%// initialise the variable to update in the GUI appdata
update_prog = 0 ;
setappdata( h.fig , 'update_prog' , update_prog ) ;
%// save handles
guidata( h.fig , h );
function btnStart_callback(hobj,~)
h = guidata( hobj ) ; %// retrieve handles
if strcmp('off',h.t.Running) %// Start timer (only if not already running)
start(h.t)
end
function btnStop_callback(hobj,~)
h = guidata( hobj ) ; %// retrieve handles
stop(h.t) %// Stop timer
function timer_callback(~,~,hfig)
update_prog = getappdata( hfig , 'update_prog' ) ; %// retrieve the 'update_prog' variable value
h = guidata( hfig ) ; %// retrieve handles
set(h.sld , 'Value' , update_prog) ; %// update the slider object with the retrieved value
function my_closefcn(hobj,~)
%// this function is only to clean up when the GUI will be closed.
%// It is recommended to delete the timer manually
h = guidata( hobj ) ; %// retrieve handles
stop(h.t) %// Stop timer (in case it is still running)
delete(h.t) ; %// delete the timer
delete(h.fig) ; %// destroy the figure
And the code for external_func.m
function external_func( guiMainFigureHandle )
%// This function will only generate random numbers and push them into the
%// variable 'update_prog' contained in the GUI appdata.
%// This is why this function NEEDS the handle of the gui to be able to
%// access the Application Data space of the gui.
for k = 1:100
randomValue = rand(1) ; %// generate a random value
hfig = ancestor( guiMainFigureHandle , 'figure' ) ; %// make sure the handle provided is the top level figure
setappdata( hfig , 'update_prog' , randomValue) ; %// update the variable value
pause(0.1) ;
end
Edit:
I place this in edit instead of changing the code above because I don't recommend messing with the root object if you don't need to. But in your case it can be a way round your problem.
If your external function doesn't have access to the GUI, it can always update a part of memory which is available for all the programs running in a given Matlab session, namely the root object. The handle for it is reserved and is the same for any program: 0 (although since v2014b there is another way to invoke it : groot, it is still always the same handle for all Matlab).
So in the example above, in theGui.m, use instead:
setappdata( 0 , 'update_prog' , update_prog ) ;
in the main routine, then in the subfunction function timer_callback(~,~,hfig), use:
update_prog = getappdata( 0 , 'update_prog' ) ; %// retrieve the 'update_prog' variable
And your function external_func() doesn't need any extra argument, the update only needs one line:
setappdata( 0 , 'update_prog' , update_prog) ; %// update the variable value
I "suspect" that your update_prog variable in the base workspace is not a global (you must define it to be global in every workspace that you want to use it).
Since your using globals (there are many better ways to do this - but thats not your question) - why don't you simply define the update_prog variable to be global in your external_func function (replace the assign call).
edit put a drawnow in your external_func function. That way when you click on the button it will update.
edit 3
I think I know what you want to do, try this example and see if it does what you want - updated to show how you find the slider object in your code and update inside your loop:
function mygui
% create a figure
f = figure;
% create a uicontrol slider - note that I give it a tag.
uicontrol ( 'style', 'slider', 'Position', [0 200 200 40], 'tag', 'MYSLIDER', 'backgroundcolor', 'white', 'parent', f );
% create a push button which we can press to update
uicontrol ( 'string', 'Press 2 start', 'callback', #(a,b)myLoop(), 'Position', [0 0 200 50] )
end
% This replicates your "external_func" function.
function myLoop()
% since you cant pass in any var -> you need to find the slider object
% you can do this by using findobj and search for the tag of the item
uic = findobj ( 0, 'tag', 'MYSLIDER' );
% find the figure handle (only needed for this demo)
f = ancestor ( uic, 'figure' );
% initialise the value which will be used to update the slider
count = 0;
% create your loop
while ishandle(f)
% incrememt the count variable -> this will end up on the slider value
count = count + 1e-5;
% reset count if > 1 -> so the slider cant go out of range.
if count >= 1
count = 0;
end
set ( uic, 'Value', count );
% initiate a drawnow -> this allows matlab to process GUI events
drawnow();
end
end
The downside of this is you insert a drawnow in your loop -> which could slow it down somewhat.
If this doesn't fix your problem you need to explain better what you want to do... (in my view)
I'd like to save matrices which are input via GUI pushbuttons in a 3D matrix. for example: I click button 1 a 2-2 matrix is put in the first slice of the 3D matrix. I than click button 3 and a different 2-2 matrix is put in the second slice. SO on and so on. I hope this is clear enough. The problem I have is that I'm not an experienced programmer. I currently initialize a zeros matrix as follows in the opening func:
storageMatrix = ones(2,2,100);
setappdata(0, 'storageMatrix', storageMatrix);
I have put a function updateStorageMatrix in the main script like this, mind you this isn't finished yet.
function updateStorageMatrix
storageMatrix = getappdata(0, 'storageMatrix');
and than when I look at my code of pushbutton 1 for example it looks like this:
% --- Executes on button press in Add_Straightduct.
function Add_Straightduct_Callback(hObject, eventdata, handles)
%prompt command for k number and length
prompt = {'k0:','Length:'};
dlg_title = 'Straight Duct specs';
num_lines = 1;
SDelements = {'0','0'};
Straightduct = inputdlg(prompt,dlg_title,num_lines,SDelements)
%insert values in function
sizeStorageMatrix = size(getappdata(0,'storageMatrix')); %get size of the storage matrix
dimT = sizeStorageMatrix(1,3); %take the number of matrices
if dimT==1
straightDuct = straight_duct(str2num(SDelements{1}), str2num(SDelements{2}), Mach_Frange(1,1))
setappdata(handles.updateStorageMatrix,'storageMatrix', storageMatrix(1:2, 1:2, 1))=straight_duct(str2num(SDelements{1}), str2num(answer{2}), Mach_Frange(1,1))
dimT+1
else
setappdata(0,'storageMatrix', storageMatrix(1:2, 1:2, dimT+1))=straight_duct(str2num(SDelements{1}), str2num(answer{2}), Mach_Frange(1,1))
dimT+1
end
the straight_duct() is a function I used in the script when calculating the mufflers, I have several of those functions, I am not sure if that's the way to work when using GUI. I just hope you get the idea of what I'm trying to achieve and help me on my way. So I can actually make a UI for my script :)
This assumes you've initialized the storageMatrix elsewhere in the GUI like this...
handles.storageMatrix = zeros(2,2,100);
guidata(hObject,handles); % Must call this to save handles so other callbacks can access the new element "storageMatrix"
Then in your first button's callback...
% --- Executes on button press in Add_Straightduct.
function Add_Straightduct_Callback(hObject, eventdata, handles)
.
. % Whatever initializations you need to do
.
localStorageMatrix = handles.storageMatrix; %load the storageMatrix being used by other callbacks/functions
.
. % Whatever you need to do to generate the new 2x2 matrix "slice"
.
localStorageMatrix(:,:,end+1) = newSlice; % appends the new slice to the end of the, indexing using colons assumes newSlice is of the same first and second dimension as other slices in localStorageMatrix
handles.storageMatrix = localStorageMatrix; % overwrite the storageMatrix in handles with the contents of the new localStorageMatrix
guidata(hObject,handles); % save the handles struct again now that you've changed it
Alternatively, you could have just used the handles.storageMatrix throughout, without including a localStorageMatrix but I've included it for emphasis.