使用 C++/CLR Windows 表单更新 GUI
Update GUI using C++/CLR Windows Forms
您好,我正在尝试创建一个可以读取数据、处理数据并将其实时绘制在图表上的应用程序,直到我按下 ESC 停止应用程序读取和处理更多数据。我在 Visual C++/CLR 中创建了这个应用程序:
My Real Time Data Processing and Plotting App
当我点击分析数据按钮时(见图)。正在读取和处理数据(基于控制台 window 中的结果),但我的 GUI 中的图形不会实时更新。它只会更新,直到我停止应用程序读取和处理更多数据。我知道这个问题与线程有关。我相信我 运行 在更新 GUI 的同一线程上读取和处理数据。下面是我进行数据抓取和处理以及 GUI 更新的代码:
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]);
}
}
}
我尝试寻找解决方案并找到了使用 BeginInvoke、delegate 和 Invoke 方法的内容,但它们中的大多数都在 C# 中并且很难理解。我使用 C++ 的原因是因为我使用一些 DLL 文件来调用某些函数,这些函数可以帮助我获取我想要处理和分析的数据,而根据我的经验,C# 在尝试使用 DLL 文件时发挥了很大作用。此外,我找到的关于如何使用我提到的 C++ 中的方法的文档和示例不是很清楚,或者不能很好地转换为 C++。
我的问题是如何在 C++/CLR 中实时更新我的 GUI 图表?是否可以使用 C++/CLR 这样做?如果可以,你能举个例子吗?非常感谢您的帮助!谢谢!
你是对的 - GUI 线程被限制在 while
循环中执行并且因为它永远不会离开方法,它永远不会执行 window 消息处理,包括 WM_PAINT消息,因此屏幕永远不会重绘。
您需要将获取放在单独的线程上,然后将调用同步回 GUI 以进行更新。这是一个例子:
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);
}
一个问题是它在一个紧密的循环中调用 BeginInvoke
。这可能会使性能受到影响(一定要进行测量以查看是否确实如此)。这是一个问题的原因是您正在使用 vector<int>
并且您不能将此类型传递给任何托管方法。您需要将数据从向量中复制到 .Net 类型中,以便将其传递到 BeginInvoke
.
您好,我正在尝试创建一个可以读取数据、处理数据并将其实时绘制在图表上的应用程序,直到我按下 ESC 停止应用程序读取和处理更多数据。我在 Visual C++/CLR 中创建了这个应用程序:
My Real Time Data Processing and Plotting App
当我点击分析数据按钮时(见图)。正在读取和处理数据(基于控制台 window 中的结果),但我的 GUI 中的图形不会实时更新。它只会更新,直到我停止应用程序读取和处理更多数据。我知道这个问题与线程有关。我相信我 运行 在更新 GUI 的同一线程上读取和处理数据。下面是我进行数据抓取和处理以及 GUI 更新的代码:
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]);
}
}
}
我尝试寻找解决方案并找到了使用 BeginInvoke、delegate 和 Invoke 方法的内容,但它们中的大多数都在 C# 中并且很难理解。我使用 C++ 的原因是因为我使用一些 DLL 文件来调用某些函数,这些函数可以帮助我获取我想要处理和分析的数据,而根据我的经验,C# 在尝试使用 DLL 文件时发挥了很大作用。此外,我找到的关于如何使用我提到的 C++ 中的方法的文档和示例不是很清楚,或者不能很好地转换为 C++。
我的问题是如何在 C++/CLR 中实时更新我的 GUI 图表?是否可以使用 C++/CLR 这样做?如果可以,你能举个例子吗?非常感谢您的帮助!谢谢!
你是对的 - GUI 线程被限制在 while
循环中执行并且因为它永远不会离开方法,它永远不会执行 window 消息处理,包括 WM_PAINT消息,因此屏幕永远不会重绘。
您需要将获取放在单独的线程上,然后将调用同步回 GUI 以进行更新。这是一个例子:
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);
}
一个问题是它在一个紧密的循环中调用 BeginInvoke
。这可能会使性能受到影响(一定要进行测量以查看是否确实如此)。这是一个问题的原因是您正在使用 vector<int>
并且您不能将此类型传递给任何托管方法。您需要将数据从向量中复制到 .Net 类型中,以便将其传递到 BeginInvoke
.