LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

Problem updating ListBox using Threads

I have 4 threads. I am updating a numeric control as well as a separate listbox inside each thread. What I see is numeric control gets updated correctly, but listboxes only get updated at the end when my threads stop running.
 
Here is how I am creating the threads:
 
 for (i=0; i<MAX_NUM_THREADS; i++) {
  retVal = CmtScheduleThreadPoolFunction(Thread_Pool_Handle, ThreadFunction, &UUT_Info[i], &threadFunctionID[i]);
 }
 
 for (i=0; i<MAX_NUM_THREADS; i++) {
  retVal = CmtWaitForThreadPoolFunctionCompletion(Thread_Pool_Handle, threadFunctionID[i], OPT_TP_PROCESS_EVENTS_WHILE_WAITING);
 }
 for (i=0; i<MAX_NUM_THREADS; i++) {
  retVal = CmtReleaseThreadPoolFunctionID(Thread_Pool_Handle, threadFunctionID[i]);
 }
 
As you see, I am using OPT_TP_PROCESS_EVENTS_WHILE_WAITING.
 
Inside thread function, I do:
 
 for (i=0; i<10; i++) {
 
  //This updates the numeric control correctly
  SetCtrlVal(Main_Panel, Debug_Line_Ctrl[Index], LineNum);
  ProcessDrawEvents();
 
  // This one doesn't work, all 10 updates happen at once at the end
  InsertListItem(Main_Panel, Debug_Msg_Ctrl[Index], -1, pMsg, 0);
  SetCtrlIndex(Main_Panel, Debug_Msg_Ctrl[Index], Debug_Msg_Ctrl_List_Items[Index]);
  Debug_Msg_Ctrl_List_Items[Index]++;
  ProcessDrawEvents();
}
 
 
 
 
 
 
 

Message Edited by vishianand on 08-17-2005 04:04 PM

0 Kudos
Message 1 of 13
(4,935 Views)
When I use TextBox instead of ListBox, I don't run into the above mentioned problem. What does using SetCtrlVal does which doesn't get done when using InsertListeItem? I have tried placing ProcessDrawEvents after the updates but no luck.
0 Kudos
Message 2 of 13
(4,929 Views)

Try adding a call to sleep instead of ProcessDrawEvents. ProcessDrawEvents will only process UIs that were creating in that particular thread. Since you just have a worker thread, there are no messages waiting to be processed. ProcessDrawEvents will make no attempt to handler messages in other threads. What might be happening is that your thread is not giving time for the UI thread to process its messages.

Bilal Durrani
NI
0 Kudos
Message 3 of 13
(4,922 Views)
I forgot to add that I after I update the listeBox I am using a Wait function (my own) which is basically using CVI timers to delay for 1 second with processSystemEvents in a loop. That should give enough time for GUI to update.
 
Here is what  Wait does:
 
void Wait(double NumOfSeconds)
{
 double t0;
 double t1;
 t0 = Timer();
 t1 = Timer();
 while ((t1-t0) < NumOfSeconds) {
  ProcessSystemEvents(); 
  t1 = Timer();
 }
}
 
 
 
Liek I said before, the updates on Numeric controls and textBox happen fine but not the listbox. A friend told me that using SetCTrlVal from inside the thread always updates the GUI while if you do some SetCtrlAttribute things, it wouldn't.
0 Kudos
Message 4 of 13
(4,923 Views)

HI,

any update on the problem?

I'm interested because I'm having the same problem, which is updating a List Box from another thread different from the one in which I run RunUserInterface().

I'm updating a whole List Box Control, first doing a ClearListCtrl() and then performing the necessary InsertListItem()'s to put on the list the actual items.

The problem seems to be that the control doesn't update until you make a call from the thread that controls de CVI. I have tried calling ProcessDrawEvents() and it doesn't work. I have also tried to put a Sleep() after updating the control but it doesn't work either.

What I have done finally is to create a function that puts the control invisible and then visible again, and when it becomes visible again it has the new values correctly updated. The problem with this solution is that the control blinks and it has a very very bad effect on the application.

void RefreshControl(int panel, int control)
{
    SetCtrlAttribute(panel, control, ATTR_VISIBLE, false);
    SetCtrlAttribute(panel, control, ATTR_VISIBLE, true);

}

Is there any function that can force a control to update (or repaint)??

I'm working with Labwindows/CVI 8.1 and Microsoft Visual C++

Regards,

Daniel

0 Kudos
Message 5 of 13
(4,758 Views)
Hello Daniel,

This is a limitation of updating a user interface from a thread other than the thread that owns the panel (i.e. the thread that loads or creates the panel). The limitation is that while you are able to update the UI from a secondary thread, these updates oftentimes require the thread that owns the panel to be processing events (i.e. calling RunUserInterface, ProcessSystemEvent, ProcessDrawEvents, GetUserEvent, or calling CmtWaitForThreadPoolFunctionCompletion with the OPT_TP_PROCESS_EVENTS_WHILE_WAITING option) in order for the updates to show up in the panel. This is because many drawing function schedules events for the drawing to be completed, and those events only arrive at the window when its owning thread is free to process them.

Calling ProcessDrawEvents (or Sleep) in the secondary thread will not help.

For that matter, hiding and showing the control in the secondary thread should not help either. I'm at a loss to explain why that is working for you. Would you be able to post here a small example that reproduces this?

You haven't said what it is that your main thread is doing while the secondary thread is updating the list box. You only mention "making a call", at which point the list box finally updates. What call is this? And what is it doing before it makes this call. Maybe there's a way to modify this?

Luis
0 Kudos
Message 6 of 13
(4,746 Views)

Hello,

I understand there's this limitation to Labwindows, but wouldn't it make sense to have a function (like mine RefreshControl) that would repaint a control on programmer's desire, without having to wait for the scheduled repainting of a panel or control from the main thread?

To simplify my app, it has 2 threads. One thread (CVI_Thread) just starts and runs RunUserInterface() and is responsible for handling the user interaction with all panel and buttons. The other thread (LAN_Thread) starts a socket and handles LAN messages. Then I have a List Box Control with some items. I can 'add' or 'delete' items manually as a user and it works properly (I have buttons for that and they generate events that are treated by CVI_Thread as usual).

But I can also receive a list of these items through a LAN message, so when it arrives, I update my List Box Control, and this is done by the LAN_Thread, and not the CVI_Thread.

The code would be like this:
CVI_Thread::MessageReceived (ItemList)
{
  SaveToDataBase(ItemList);
  ClearListCtrl(panel, listControl);
  for all items in ItemList
     IsertListItem(panel, listControl, item);
 
//RefreshControl(panel, listControl);
}

Without the call to RefreshControl(), the changes on the ItemList don't appear on screen. If I make a call to my 'RefreshControl' function (which just makes the control invisible and visible again, see above), my listControl blinks and when it comes back it has all the new items
Note that if I receive an empty list of items, I would just clear my listControl. This also fails.

I don't know what else to try. Thanks for your help

Regards,

Daniel

0 Kudos
Message 7 of 13
(4,731 Views)
One solution to this could be to do a PostDeferredCallToThread to the thread owning the window so as to make it call ProcessDrawEvents. This way, as long as the thread owning the window is processing messages, the list box should get repainted fairly soon after you update it and call PostDeferredCallToThread.
0 Kudos
Message 8 of 13
(4,711 Views)
Hi Daniel.

Here is one method I have used to handle this type of problem.

When LAN_Thread receives a message with new list items:
  • allocate a block of memory and copy the data to it (at minimum, this will include item quantity plus one value and one label per item)
  • call PostDeferredCall() or PostDeferredCallToThread() as required, passing a pointer to the block
In the deferred callback (excuted in CVI_Thread):
  • cast the received pointer appropriately and recover the data
  • update the list box with the new items
  • free the block
  • call ProcessDrawEvents()
If the maximum number of items in a message is not known in advance, you will need to allocate two blocks of memory: one for the message header, and one for the list items.

Regards,
Colin.

0 Kudos
Message 9 of 13
(4,705 Views)
Daniel,

To clarify my earlier post... if your main thread is inside a RunUserInterface call the whole time that the secondary thread is running, you should not need to do anything special in order to see the list box update. If this is the case, and your listbox is not updating, this is an unexpected behavior that we need to investigate further. But you wrote earlier that the UI only updates when the main thread "makes a call". What call is that? Does this mean that it is no longer inside RunUserInterface? Or is it still nested inside RunUserInterface, but inside a callback function?

Luis
0 Kudos
Message 10 of 13
(4,698 Views)