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

5 views (last 30 days)
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
Jan
Jan on 11 Mar 2021
Edited: Jan on 11 Mar 2021
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.
Daniel Melendrez
Daniel Melendrez on 12 Mar 2021
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)

Jan
Jan on 11 Mar 2021
Edited: Jan on 11 Mar 2021
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
Jan
Jan on 11 Mar 2021
@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.
Daniel Melendrez
Daniel Melendrez on 12 Mar 2021
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 Search Path in Help Center and File Exchange

Products


Release

R2019a

Community Treasure Hunt

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

Start Hunting!