LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

momentary action button

Can someone please advise if there is an add-on or someone has written code to implement a momentary action switch. I wish to run a loop of code while a command button is pressed and stop execution once the switch is released. While this is easy in LabVIEW, I am modifying some older LabWindows/CVI code and cannot see how to achieve this. Any help, greatly appreciated.
0 Kudos
Message 1 of 4
(3,617 Views)
on your control, you will need to handle EVENT_LEFT_CLICK to detect the user pressing the button and EVENT_LEFT_CLICK_UP to detect the user releasing the mouse button. then there are many ways to achieve your goal:

- the easiest way is to create an asynchronous timer. the timer callback is the body of the loop. start the timer on EVENT_LEFT_CLICK, stop the timer on EVENT_LEFT_CLICK_UP. this method may have some problems since it has a top speed (depending on your processor and the time quantum of threads on your OS) which is slower than just looping, especially if your loop body is very short.

- the LabView way is to create a state machine, and replace the call to RunUserInterface() by a loop which first calls ProcessSystemEvents() then execute your state machine. you switch state on EVENT_LEFT_CLICK and reset your state on EVENT_LEFT_CLICK_UP. a neat way to perform your stuff, also easily expandable with more states. a small disadvantage is that ProcessSystemEvents() may take some time to complete, depending on what you are doing on the user interface (like dragging or resizing a window continuously).

typedef enum
{
    idle,
    loop,
    exit
} STATE;

STATE state;

int main()

    // initialize CVI runtime engine, load panels, ...
   
    // the following loop replaces the call to RunUserInterface()
    while ( state != exit )
    {
        ProcessSystemEvents();
       
        switch ( state )
        {
        case idle:
            break;
           
        case loop:
            // perform the loop body here
            break;
        }
    }
   
    // close panels...
    return 0;
}

int CVICALLBACK button_event (int panel, int control, int event, void *callbackData, int eventData1, int eventData2)
{
    switch (event)
    {
    case EVENT_LEFT_CLICK:
        state = loop;
        break;
       
    case EVENT_LEFT_CLICK_UP:
        state = idle;
        break;
    }
    return 0;
}

- the dirty way is to create a thread on EVENT_LEFT_CLICK. the thread loops until a global variable is set to a specific value then exits. set the variable to the exit value on EVENT_LEFT_CLICK_UP. this method is dirty because of the need to use a global variable. also a small delay is observed between the click and the start of the thread, which is the time it takes to create, initialize and start the new thread (only a few ms).

#include <windows.h>

static DWORD WINAPI loop_body( LPVOID parameter );
static HANDLE loop_thread;
static int loop_control = 0;

int CVICALLBACK button_event (int panel, int control, int event, void *callbackData, int eventData1, int eventData2)
{
    switch (event)
    {
    case EVENT_LEFT_CLICK:
        loop_control = 1;
        loop_thread = CreateThread( NULL, 0, &loop_body, &loop_control, 0, NULL ); // sorry NI but i don't buy the thread pool idea: i want my thread to execute NOW
        break;
       
    case EVENT_LEFT_CLICK_UP:
        loop_control = 0;
        WaitForSingleObject( loop_thread, INFINITE );
        CloseHandle( loop_thread );
        break;
    }
    return 0;
}

static DWORD WINAPI loop_body( LPVOID parameter )
{
    int *control = (int *)parameter;
    // if you need some variables local to your loop, declare them here

    // perform your initialization here.
    while ( *control != 0 )
    {
        // do whatever you need in the body of the loop...
    }
    // if you need to release some resources, it's time to do it here 

    return 0;
}

(- a nicer way: same method as above but encapsulate everything in a proper structure with an initialization function and a dispose function, and a proper synchronisation object to start and end the loop. why all that stuff ? because you may want to have the same behavior on multiple controls, and the code above will force you to copy/paste for each control you need)

there may still be a lot of other ways to do that... be creative !
0 Kudos
Message 2 of 4
(3,606 Views)
Thanks, that is very useful.
0 Kudos
Message 3 of 4
(3,587 Views)

Hi,

I was wondering if there is an easier way.

I have a bug in my program where, If the user drags the mouse away from the momentary button,  the button remains locked on.

I am using 

EVENT_LEFT_CLICK

      holdButtonDown= TRUE

       setCtrlVal.....

EVENT_LEFT_CLICK_UP

     holdButtonDown = FALSE.

 

I know why this doesnt work but I am looking for an easy way to fix it.

 

I am using 8 of these momentaries so dont want to use a timer for each.

Could you please help me through this.

Kind Regards

Col Kerr

 

0 Kudos
Message 4 of 4
(3,003 Views)