While-loop in GUI ends when interacting with other GUI controls

Dear community
I am working on a very complex GUI that controls a group of instruments in a scientific facility. The GUI is in charge of communicating with three instruments via RS-232, query data and display it in a live plot. Let's call them Instruments A, B, and C. To control the devices, the GUI contains a whole bunch of buttons, menus, checkboxes, etc. for the three instruments.
In order to keep the plotting 'alive', the while loop queries a Global flag acquire which is assigned to the handles structure and updated at the end of every Callback using: guidata(hObject, handles)
global acquire; % Flag to keep the acquisition alive
acquire = 0; % Initial status of the acquisition flag
The live plotting function prototype looks like the following 'skeleton'. (Of course is way more complex than this, we are talking about ~600+ lines of actual code only in this function)
function [loggedData, timeGroup, handles] = liveDataCapture(handles, hObject)
% Before the loop starts there are a bunch of initializations...
while isequal(handles.flagAcq, 1)
% At every loop verify the status of the flag
handles.flagAcq = getFlagAcquisition;
...
...
%
%% Complex data acquisition routines, queries values from the 3 instruments, creates data arrays, timestamps, animatedlines, etc.
%
% Set legends according to the checkbox --> read description below
if isequal(get(handles.checkHideLegend, 'Value'), 1)
legend('Show')
else
legend('Hide')
end
end
%
% If the user presses a 'stopButton', then the flag is set to 0 with: handles.flagAcq = setFlagAcquisiton(0);
%
end
The only way to keep the while-loop running when interacting with one of the controls from, say Instrument A or the live plot itself, is by updating the global flag using a function that sets its value to 1. For instance, if I interact with the checkbox for showiing/hiding the graph legends inside the previous while-loop, I must update the global flag inside the checkbox Callback:
function checkHideLegend_Callback(hObject, eventdata, handles)
% Force the Global flag to keep its value of 1
setFlagAcquisiton(1);
handles.flagAcq = getFlagAcquisition;
value = get(hObject, 'Value');
if isequal(value, 1)
set(hObject, 'String', 'Hide Legend')
else
set(hObject, 'String', 'Show Legend')
end
guidata(hObject, handles) % Update the handles structure
return
Similarly, I have included the setFlagAcquisition(1) function in some controls from Instrument A.
Now, if I don't do this on the other controls (buttons, checkboxes, etc.) from Instruments B and C, the While-loop ends despite the value of handles.flagAcq is always 1. I have checked it in debugging mode already.
The main issue here is that I would have to do this for around 50+ controls and I have the feeling that this is NOT the right way to tell the GUI to keep the acquisition loop running if the user intracts with the instruments controls.
Questions:
  1. Is there a way to avoid using Global flags to keep the loop running until the user want to stop the live acquisition?
  2. Why the While-loop ends? Is it related to updating the handles structure when using: guidata(hObject, handles)
  3. Am I missing a property from the handles structure that will avoid the need of this global variable? (e.g. the Interruptible property, etc.)
Thank you in advance for your time
EDIT: Just to clarify, I have sucessfully used this Global flag approach on a previous GUI however, I understand that Global variables are generally not recommended.

7 Comments

Did you try using an infinite while loop and exit the loop using break statement as needed.
I do not exactly undestand, what your problem is. Global variables have severe disadvantages. So using a flag inside the handles struct, which belongs to a specific figure, is a secure and efficient way.
Dear Jan
Thanks for your comment. My apologies for not clearly expressing my problem.
The problem is: the only way I have to keep the while loop running and let the user interact with the 50+ controls (ensuring that the live capture is kept 'alive') is by overwriting the value of the global flag at the beggining of each one of the GUI controls Callbacks; granted the program is currently running a continuous live capture. To do so, I check the value of the flag before taking action inside the Callback from any of the controls.
For instance, there is a callback from a button that queries the status of 'Instrument_A' (I am using name aliases to make my point clear):
% --- Executes on button press in queryInstrument_A_Status.
function queryInstrument_A_Status_Callback(hObject, eventdata, handles)
% Verify first if we are under a live acquisition
if isequal(handles.flagAcq, 1)
% Tell the live acquisition loop to keep going with the live capture
setFlagAcquisiton(1);
handles.flagAcq = getFlagAcquisition;
end
instrA_Stat = getSystemStatus(handles.serialPortInstrA);
% Then do something with the returned value instrA_Stat
...
I want to understand the following:
What is telling the while loop to stop? Why is that happening? I am NOT using any breaks or changing the conditions of the handles.flagAcq in any of those controls. The while loop simply stops if I interact with the controls during live acquisition if I do NOT use the approach I have described here.
I understand that using Global variables is not recommended, however it is not clear to me what process or event is behind telling the handles structure to stop being that the flagAcq member is not being changed
Thank you guys
Best wishes
For general advice and examples for how to create a GUI (and avoid using GUIDE), have look at this thread.
Thanks for your comment, however, it does not correctly address what my problem is.
I am NOT having issues with exiting a while loop. I understand pretty well how they work and when to use a break to end them.
My problem is precisely that my loop is ending when I have not told it to end. Its condition is the handles.flagAcq variable which remains untouched when I interact with the GUI controls that I have mentioned in my description and my latest response to Jan
Cheers
Best wishes
I am NOT using any breaks or changing the conditions of the handles.flagAcq in any of those controls
This is the problem of global variables: It is hard to find out, from where they have been changed.
What about setting a breakpoint in setFlagAcquisiton to see, when the flag is dsiabled? Obviously there is a command, which resets the flag. Without seeing the complete code, it is impossible to guess, where this happens.
Good point!
I actually spent a lot of time debugging and monitoring the state of this variable (and many others) and guess what? It does not change! Despite the flag is 1 (meaning that the while loop MUST keep running), the acquisition stops :(

Sign in to comment.

Answers (1)

I have an idea:
function checkHideLegend_Callback(hObject, eventdata, handles)
% When reaching this, the struct [handles] contains the value from the
% time point, when this callback has been created. This value is not
% updated dynamically.
% This means, that e.g. handles.flagAcq has the initial value.
% If you now call:
guidata(hObject, handles) % Update the handles structure
% directly, the current state of handles is replaced by the initial
% state.
For this reason I consider is as bad design to provide the handles struct as 3rd input argument. It is more clear, to obtain the current value dynamically:
function checkHideLegend_Callback(hObject, eventdata, handles_ignore)
handles = guidata(hObject);
...
guidata(jObject, handles);
end
Using this should replace the helper function setFlagAcquisiton(), but it is suffcient to store this flag directly in handles.flagAcq .

3 Comments

Actually, GUIDE sets the callbacks like this:
@(hObject,eventdata)checkHideLegend_Callback(hObject,eventdata,guidata(hObject))
So the handles struct is loaded dynamically.
However, I agree with you that explicitly loading it dynamically (and only if required) results in a clearer design.
@Rik: This is nice. I tried GUIDE in R2009a and gave up very soon. When the handles struct is updated dynamically already, me guess does not hit the point. Then anywhere in the code the flag must be set actively. The Debugger can reveal the location, when all callbacks are observed using break points.
Dear Jan
Apologies for the late reply and thank you for your kind help.
I have been extremely busy implementing more functions in my GUI.
Unfortunately your recommended solution did not work. I added a test button for pressing during live acquisition, replaced my code for updating the handles.flagAcq from the Global variable with your snippet, however, the loop effectively stops after pressing the button.
If I remember corrently, some time ago I tried a smiliar code whitout sucess. The idea was to keep tack of the whole handles structure inside of the while loop, however it wasn't the right solution.
I believe that the only option I have is to continue using my code as it is. I will have to add my code to 'refresh' the live acquisition flag on each one of the buttons/menus/controls that are enabled for interaction during live capture. As I mentioned before, approximately 57 controls... yeah, big complex project
I will keep trying with different approaches. It is not clear to me (yet) why this is happening.
Finally, I wanted to clarify that I do not want to move from GUIDE. Honestly, I like it and I think is a shame that it will be removed in future versions, according to what I've recently read. I have tried with App Designer and I am truly sorry, but I strongly dislike it! I do not feel the same 'simplicity' or flexibility from GUIDE. I think it would have been a better idea just to improve GUIDE's capabilities . I find AppDesigner extremely restrictive and rather complicated (but I may just be a n00b and I don't completely understand it)
I want to thank you all for the feedback and support on this.
Cheers
Stay safe guys
Daniel

Sign in to comment.

Categories

Find more on MATLAB in Help Center and File Exchange

Products

Release

R2019a

Asked:

on 8 Mar 2021

Commented:

on 12 Mar 2021

Community Treasure Hunt

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

Start Hunting!