I have a problem with a while loop inside a switch-case statement, used together with callback functions and "drawnow". In my code, while the cases of the switch-case are determined by pushbuttons in uicontrol, the case statements involves further callback functions to track mouse movements using 'windowbuttondown/up/motionfcn's. Because I draw multiple plots inside the while loop in the case statement, however, I use 'drawnow', which gives me the following error when I run the programme:
Error on line 160 ==> drawnow
??? Interrupt while evaluating uicontrol Callback
The piece of code inside the case statement gives no error when I run independently but somehow creates problem when is integrated with the rest of the code, which I attach below. Any help would be so much appreciated. Many thanks!
function programme(selection)
if nargin == 0
selection=0
end
switch selection
case 0 %start GUI and uicontrols to set up the cases i.e programme(1), programme(2) etc
uicontrol('style','pushbutton',...
'string','First', ...
'position',[50 700 50 20], ...
'callback','programme(1);');
uicontrol('style','pushbutton',...
'string','Second', ...
'position',[150 700 50 20], ...
'callback','programme(2);');
case 1
%mouse track:
set(gcf,'windowbuttondownfcn','mousedown=1;');
set(gcf,'windowbuttonupfcn','mouseup=1;');
set(gcf,'windowbuttonmotionfcn','mousemotion=1;');
%to terminate the while loop, set up stopit=1 on one of uicontrol buttons:
uicontrol('style','pushbutton',...
'string','First', ...
'position',[50 700 50 20], ...
'callback','stopit=1;');
stopit=0;
while (stopit==0)
if mousedown==1
statements
if mouseup ==1
statements (plots)
mouseup=0;
mousedown=0;
mousedown=0;
end
end
drawnow
end
case 2
statements
otherwise
statements
end
Look in the help: drawnow
It interrupts callbacks. And you call your function in a callback. Maybe you can replace it with a pause(0.01).
Though I would strongly advise you to get rid of the loop and use callbacks instead.
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 have a Matlab UI where I want the user to input several areas using imrect as soon as a radiobutton is selected.
It is unknown how many areas will be selected so the selection needs to be in an infinite loop.
As soon as another radiobutton is selected, the imrect input should stop, which I cannot get to work.
Here is a minimal working example:
function mwe
ax = axes('Position', [0 0 1 1]);
bg = uibuttongroup('Position',[0 0 .15 1], 'SelectionChangedFcn',{#bselection, ax});
r1 = uicontrol(bg, 'Style','radiobutton', 'String','Option 1', 'Position',[10 250 100 30]);
r2 = uicontrol(bg, 'Style','radiobutton', 'String','Option 2', 'Position',[10 225 100 30], 'Value',1);
function bselection(source, event, ax)
switch event.NewValue.String
case 'Option 1'
while true
h = imrect(ax);
% do stuff
delete(h);
end
case 'Option 2'
% do not show imrect and do other stuff
end
I appreciate any help.
You can set the Interruptible property on the button. You can also set BusyAction to cancel. The help says:
The interruption occurs at the next point where MATLAB processes the
queue, such as when there is a drawnow, uifigure, getframe, waitfor,
or pause command.
So if you include a 'pause', it may not stop until the next rectangle has been selected. This is because once you've called imrect, it may not know that it has to stop.
However this method may not work if imrect blocks the matlab UI from triggering a callback.
An altogether better way is not to use an endless loop. You need to tell it when to end by checking --
running = true;
while running
h=imrect(ax)
% do stuff
delete(h)
if (SOMETHING)
running = false
end
end
What is SOMETHING? We need to check if the button has been deselected.
You could use
if r1.Value!=1
running = false
end
Which would check if r1 is not selected, and if so, running becomes false, and the loop stops cycling round.
The title says it all. I was wondering if the following code is prone to a race condition.
classdef Foo < handle
properties
value = true
end
methods
function toggle(o, ~, ~)
o.value = ~o.value;
end
end
end
function main
foo = Foo;
uicontrol('style', 'pushbutton', 'callback',#foo.toggle);
drawnow
%// Endless loop which gets broken up by a button press
while foo.value
pause(1)
end
%// What happens if I press the button twice, really fast? There
%// is no external function called between here and the previous
%// while loop.
if foo.value
error('Race')
else
fprintf('\nWe made it\n')
end
Am I save from the race condition since everything happens in the event dispatch thread? Is the same still true if I play with Interruptible and BusyAction property of the button? I found the matlab help quite confusing.
In case it matters I'm using R2012a.
Background: I have several portion in an GUI to handle different tasks. In 1 portion (portion1) I have text input and send button. So once I will click send it would send one data to my serial port. Another portion (portion2) would receive signal from serial port which have been received from other devices. Both the portions are using two buttons; one to start the work of that particular portion and one to stop the work. I have used global variables and while loop (with boolean) to exit the infinite loop, as I need to send the data or receive data continuously.
My question: Problem is when I am using global variables and the above mentioned way of using infinite loop, if I click portion1, portion1 will start iterate. Now if I click portion2 then portion1 will be stopped. But I need to use them both at the same time and both of them will continuously send and receive data until I click other button (s) to exit the infinite loop.
My sample code for clarification:
function send_Callback(hObject, eventdata, handles)
global firstFlag;
firstFlag = true;
while firstFlag
%Required codes
end
function haltSend_Callback(hObject, eventdata, handles)
global firstFlag;
firstFlag = false;
function receive_Callback(hObject, eventdata, handles)
global secondFlag;
secondFlag = true;
while true
%Required codes
end
function stopReceive_Callback(hObject, eventdata, handles)
global secondFlag;
secondFlag=false;
I have tried to find a solution referring to internet, but most of the solutions are using global variable. It would be better if I could work without global variables. But even if my requirements are fulfilled (as per my question) it would work.
Global or not is not the issue here.
Callbacks in Matlab all run on the same thread.
So when callback1 is running and you trigger callback2, callback2 will interrupt callback1.
callback1 will then only proceed once callback2 is finished.
You can only slightly modify this procedure using the BusyAction property:
http://www.mathworks.de/de/help/matlab/ref/uicontrol_props.html#bqxoija
But this won't help in your case.
In a "proper" programming language, you'd have a sending and a receiving thread running in parallel. You can't do this matlab however - unless you're e.g. willing to write java-code.
If you want to stick with matlab, the closest to thread would be timers.
These would replace your while loops.
E.g. as in the following minimalistic example:
function cbtest()
try close('cbtest');end
f = figure('name', 'cbtest');
% create the timers:
period = 0.2; % period in seconds, in which the timer shall execute
sendTimer = timer('TimerFcn', #sendFcn, 'ExecutionMode', 'fixedDelay', 'Period', period, 'TasksToExecute', Inf);
recvTimer = timer('TimerFcn', #recvFcn, 'ExecutionMode', 'fixedDelay', 'Period', period, 'TasksToExecute', Inf);
uicontrol(f, 'position', [10 10 100 25], 'Callback', #(a,b) start(sendTimer), 'string', 'start1');
uicontrol(f, 'position', [120 10 100 25], 'Callback', #(a,b) stop(sendTimer), 'string', 'stop1');
uicontrol(f, 'position', [10 50 100 25], 'Callback', #(a,b) start(recvTimer), 'string', 'start2');
uicontrol(f, 'position', [120 50 100 25], 'Callback', #(a,b) stop(recvTimer), 'string', 'stop2');
end
function sendFcn(hTimer, timerEvt)
% your send-loop-code
disp('sending');
end
function recvFcn(hTimer, timerEvt)
% your receive-loop-code
disp('receiving');
end
sendFcn and recvFcn here then should contain the code you have within your according while loops.
You can of course lower the period to your needs, I chose the above for testing purposes.
Your problem is that you're executing the loop inside the Callbacks. So when the second button is clicked, the second callback will start and loop infinitely, until it is ended. The first loop will wait for the second Callback to terminate until it can resume. What you need is a main program where you execute your loop. Nesting the Callbacks will make sure they can access and change the local variables without making them global. If you're fine with building a GUI programmatically, try this:
function main()
sendFlag = false;
receiveFlag = false;
while true
if sendFlag
% Your Sending code
end
if receiveFlag
% Your Receiving code
end
end
function send_Callback(~,~,~)
sendFlag = true;
end
% other Callbacks
end
Then in the Callbacks for your Buttons (you could use Toggle Buttons by the way), you simply set the sendFlag and receiveFlag, respectively.
Using GUIDE, you want to use toggle buttons.
I don't exactly know how GUIDE handles the OpeningFcn, so you should probably put a "Start" button into your GUI which basically executes the above program with a few changes:
function startbutton_Callback(hObject,~,handles)
while true
handles = guidata(hObject); % Updates handles structure
sendFlag = get(handles.sendtoggle, 'Value');
if sendFlag
% Your Sending code
end
receiveFlag = get(handles.receivetoggle, 'Value');
if receiveFlag
% Your Receiving code
end
end
Also, create send and receive toggle buttons and set their 'Min' to 0 and 'Max' to 1. This will make them switch their 'Value' property (which you read in the above loop) between 0 and 1 when you click them. In the Callback, you can change what they display:
function send_Callback(hObject,~,handles)
on = get(hObject,'Value');
if on
set(hObject, 'String', 'Stop sending');
else
set(hObject, 'String', 'Start sending');
end
guidata(hObject,handles); % Update GUI data
Now your main function, which starts when you click the start button, runs the loop and just checks the toggle buttons' states to determine whether to send and receive.
I'm trying to create a scrolling list within a GUI using nested panels. However, whenever I try to move the scrollbar in the GUI, I receive this error:
??? Error using ==> PyroGUI>slider Too many input arguments.
???
Error while evaluating uicontrol Callback
Here is my code:
screensize=get(0,'ScreenSize');
handles.fig=figure('Position',[100 100 screensize(3)-150 screensize(4)-150]);
handles.hpanel=uipanel(handles.fig,'Position',[0.005 0.01 0.99 0.99],'Title','Panel');
handles.hsp = uipanel('Parent',handles.hpanel,'Title','Subpanel','FontSize',12,...
'Position',[.025 .05 .3 .935]);
handles.hpop = uicontrol('Style', 'slider',...
'Position', [20 30 20 700],...
'Min',1,'Max',700,'Value',700,...
'callback', {#slider,handles.hsp});
handles.pos=get(handles.hpanel,'Position');
function slider()
slidervalue=get(handles.hpop,'Value');
set(handles.hpanel,'Position',[handles.pos(1) handles.pos(2)-slidervalue+1 handles.pos(3) handles.pos(4)]);
end
Any ideas as to what could be causing this?
If you provide a callback function for uicontrol, it always needs two input arguments like this (even if you don't need them)
function slider(hobj, evnt)
slidervalue=get(handles.hpop,'Value');
set(handles.hpanel,'Position',[handles.pos(1) handles.pos(2)-slidervalue+1 handles.pos(3) handles.pos(4)]);
end
Edit: See here http://www.mathworks.de/de/help/matlab/creating_guis/examples-programming-gui-components.html