LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

Lose control of GUI window during program execution.

when my program runs, the user loses control of the GUI during execution, until the specific times when user input is required.

this causes somewhat annoying behavior.  For instance, if they try to move the GUI window, or click a button on the GUI while some step is being executed.  

Another thing that happens is that the GUI is obliterated from the screen if another window (say Email, or Internet) is opened while the CVI program is running.   when they try to go back to the GUI screen, it's been erased and doesnt "redraw" until the the current function is completed.  

this can be disconcerting to the users in manufacturing, as some of my functions (steps) last over 5 minutes and so they are sitting there "blind" until the screen redraws.

my main routine is pretty standard:

int main (int argc, char *argv[])
{

    if (InitCVIRTE (0, argv, 0) == 0)
        return -1;    /* out of memory */
    if ((panelHandle = LoadPanel (0, "GUImain.uir", PANEL)) < 0)
        return -1;

    initializePanel (panelHandle);
    DisplayPanel (panelHandle);
    RunUserInterface ();

   return 0;
}

and all of my functions are started via CVICALLBACK routines.     but like i mentioned, some of my steps are quite long, and can descend into many levels of sub-functions before eventually returning to the "RunUserInterface" command which will refresh the GUI

I guess i could populate my low level funtions with "ProcessDrawEvents()" commands, but that doesn't seem like a good solution.

Thanks


0 Kudos
Message 1 of 9
(4,716 Views)

ProcessSystemEvents() is one way to deal with the problem.  The catch being that you can make your program re-entrant if you are not careful.  Without knowing what you are doing it is hard to recommend another method.  You may be able to maintain another activity using a timer or an async timer as a way to provide that task with "time slices".  Or you can spin tasks off into other threads (which an async timer can also help with).  If the other task can generate an interrupt or event you may be able to chain that task to a callback.  A com port callback would be a good example of this. 

The event driven model that CVI uses allows a user to enter long processing loops, but for the reasons you point out, they really are not the way to go.  To maintain good user performance you want your callbacks to complete quickly and return control to the main event processing routine.  If you can outline your requirements here we can probably provide some more useful suggestions.

Message 2 of 9
(4,705 Views)
my program is used by Mfg. to perform electrical tests on embedded devices.  the tests require a lot of communication with these devices either serially or via modem.   

most of the GUI functions are setup/initialization stuff that executes via callbacks very quickly.     but the core testing is done in 30+ routines that i developed which, once selected, are called sequentially from the GUI. 

one problem might be that i developed the entire program from the bottom up, DAQ card and functionality first, without thought of the GUI...  the GUI was the last thing to be developed and incorporated. 

due to the command/response nature of the various electrical tests (at 9600 baud), each routine may last anywhere from 5 seconds to 5 minutes.  these routines often require several sequences where a serial command is sent, waiting for a response, taking a measurement.   for some of the sensors, the delay can be longer than others.   after each routine, control comes back to the GUI for printing messages/results/etc, and occasionally requesting input from user.   but during the individual routines the GUI is generally locked up/frozen and reacts badly to attempts to manipulate it.

I'm beginning to suspect I may need a serious rewrite of the entire program structure if I want to deal with this.

What do you suggest?




0 Kudos
Message 3 of 9
(4,681 Views)
If you're in a callback and want to respond to mouse events, as mvr says, call ProcessSystemEvents().  If you want to update the screen, call ProcessDrawEvents().
 
You could add a timer to your UI and call those event processors on EVENT_TIMER_TICK.
If you use a timer to force those updates, I don't see a reason you need to rework the program structure.
 
You could also use SetWaitCursor(1) to let the user know the program is busy.
 
Message 4 of 9
(4,672 Views)

Unfortunately, if the code is executing in response to a control callback, a timer event will not be recognised until the original callback completes - it does not interrupt the execution so this approach will not cure the problem.

My approach to this kind of problem would be to have a timer running at, say 0.1 second intervals which triggers a callback to examine which test number we are up to (held in a global) and then execute just one test at a time. This way, you are guaranteed to regain GUI control between each test. If individual tests are uncomfortably long, you will have to find a way of breaking them into sub-tests perhaps, which can be performed independantly. A couple of global test/subtest counters, and some global state variables (to track if testing has finished, in progress, is stopping, or whatever), should allow you to improve the situation significantly without the need to re-structure totally.

JR

Message 5 of 9
(4,652 Views)

You could also set the functions to run as a thread.  I did this in a program when I wanted to play a music slice (anywhere from 5 seconds to 5 minutes).  Initially, my GUI froze while the music played.  When I launched the music in a thread, everything was fine.  Search for help on CMTScheduleThread for more info.

 

Message 6 of 9
(4,637 Views)

This is difficult to answer, since there are different ways to go, all which have there own merit.  Redesigning the program at this point is not really the answer.  It was unclear to me if all the test were being executed from a single callback.  If they are, breaking this up into decrete test steps that are called by a timer executed routine as was suggested above or another sequencing method will help break program execution into smaller chunks.  This is a good practice, but this still will not solve the overall problem within each test step.  It is possible to improve performance even though you are executing everything from a single callback.  The key is to work on the areas where the program is waiting on an action to take place but is not responding to the user.

I suspect that the serial communication is a likely culprit.  If you are spending long periods in wait loops looking for the response from the UUT over the serial port, you will have the user interface locked out.  This can be handled by adding ProcessSystemEvents() calls in the serial com wait loops (or actually any wait loops within your tests).  The catch is that you must make sure that no other event or callback will execute the same routine that made the last call to ProcessSystemEvents() unless it is made re-entrant.  This really is not that difficult since you can "dim" controls that would call into routines that must be excluded, and use flags and post deferred event calls for events that are not generated by the user.

While moving routines like serial port handling to another thread has definite advantages, it is not required that it be done that way.  You should be able to get pretty decent user interface response even with the single thread design you have.

Message 7 of 9
(4,637 Views)
thanks everyone for your input

yes, it is a single thread.  I didnt give much thought to the GUI design, did it last to wrap up all the individual electrical test functions.

I have about 35 separate steps, each defined in its own routine.   with one callback function, the entire 35 steps are invoked in sequence.  (a few exceptions pass control back to GUI, but generally it is in a routine the entire time)

the reason i did this was so the user wouldn't have to sit in front of the GUI clicking "GO" for each step.  the entire process takes about 12 minutes, which they can do other work, play web sudoku, whatever...

i tried adding a timer after the fact to refresh the GUI, but discovered (as someone else mentioned), the timer events aren't handled while the control is down in a routine.

it seems that multithreading would have been the best way to go.  im just not sure if i should try and incorporate that after 5,000 lines of code.



0 Kudos
Message 8 of 9
(4,616 Views)

I don't think you have to re-write this.  If you just need to refresh the screen put ProcessDrawEvents() inside any loops that take a along time to execute or while waiting on a reply over the serial port.  If the operator is not doing anything with the test itself, ProcessDrawEvents() in any loop, or at intervals in your test, like between test steps, should help to handle things like the operator draging a window across the screen.  Just look for the locations in your code where it spends a long time executing, probably most of the loops spent waiting on some action.

Message Edited by mvr on 05-18-2006 04:40 PM

Message 9 of 9
(4,612 Views)