Hello I'm trying to create an application that can read in data, process it and plot it on a graph in real time until I press the ESC that stops the app from reading and processing more data. I created this app in Visual C++/CLR:
My Real Time Data Processing and Plotting App
When I click the analyze data button (see picture). The data is being read in and processed (based on the results in the console window) but my graph in my GUI does not update in real time. It only updates until I stopped the app from reading and processing in any more data. I know the issue has something to do with threading. I believe I'm running the data read and processing on the same thread as I'm updating the GUI. Below is the code where I do my data grabbing and processing and GUI updates:
private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) {
vector<int> test;
test.push_back(1);
PD.setChannels(test);
//Grab and process data until the escape key is pressed
while (!GetAsyncKeyState(VK_ESCAPE)) {
PD.refreshBufferSize();
PD.grabDataFromBuffer();
vector<double> fireRates = PD.getFireRates();
//Debug code to print to console
for (int i = 0; i < fireRates.size(); i++) {
cout << fireRates[i] << endl;
}
Sleep(10); //wait 10 ms before getting the next buffer of data
//update GUI below
vector<int> channels = PD.getChannels();
for (int i = 0; i < channels.size(); i++) {
chart1->Series["Fire Rate"]->Points->AddXY(channels[i], fireRates[i]);
}
}
}
I've tried looking for solutions and found stuff saying to use BeginInvoke, delegate and Invoke methods but most of them are in C# and are very hard to understand. The reason I'm using C++ is because I'm using some DLL files to call certain functions that help me grab the data that I want to process and analyze and C# in my experience plays hard when trying to use DLL files. Additionally the documentation and examples I found on how to use the methods in C++ I mentioned is not very clear or doesn't translate well to C++.
My question is how can I update my GUI chart in real time in C++/CLR? Is it possible to do so using C++/CLR? If so can you give me an example? Your help is greatly appreciated! Thanks!
You're right - the GUI thread is confined to executing in the while
loop and since it never leaves the method, it never performs window message handling, including WM_PAINT messages, and thus the screen is never redrawn.
You need to put the acquire on a separate thread, then synchronize calls back to the GUI for update. Here's an example:
private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) {
System::Threading::ThreadPool::QueueUserWorkItem(gcnew WaitCallback(acquireData));
}
void acquireData(Object^ state)
{
vector<int> test;
test.push_back(1);
PD.setChannels(test);
//Grab and process data until the escape key is pressed
while (!GetAsyncKeyState(VK_ESCAPE)) {
PD.refreshBufferSize();
PD.grabDataFromBuffer();
vector<double> fireRates = PD.getFireRates();
//Debug code to print to console
for (int i = 0; i < fireRates.size(); i++) {
cout << fireRates[i] << endl;
}
Sleep(10); //wait 10 ms before getting the next buffer of data
//update GUI below
vector<int> channels = PD.getChannels();
for (int i = 0; i < channels.size(); i++) {
chart1->BeginInvoke(gcnew Action<int, int>(updateChart), gcnew array<Object^>(channels[i], fireRates[i]));
}
}
}
void updateChart(int channel, int fireRate)
{
chart1->Series["Fire Rate"]->Points->AddXY(channel, fireRate);
}
One problem with this is that it calls BeginInvoke
in a tight loop. This could make performance suffer (be sure to measure to see if it in fact does). The reason this is a problem is that you are using vector<int>
and you can't pass this type into any managed method. You'd need to copy the data out of the vector and into a .Net type in order to pass it into BeginInvoke
.