使用 Windows 窗体在 Visual Studio C++ 中进行线程绘图
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5696214/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Threaded plotting in Visual Studio C++ with Windows Forms
提问by Nate
I'm writing an application using Visual Studio C++ 2010 to perform Data Acquisition and plot this information in real-time. I'm using Windows Forms to create the GUI. I am taking data from both the serial port and a DAQ card (which I have libraries for and have used) and want to plot them real-time. I've done this before in Python, but I must use another library which is done in C++ so I can't use Python this time around.
我正在使用 Visual Studio C++ 2010 编写一个应用程序来执行数据采集并实时绘制此信息。我正在使用 Windows 窗体来创建 GUI。我正在从串行端口和 DAQ 卡(我有库并使用过)获取数据,并希望实时绘制它们。我以前在 Python 中做过这个,但我必须使用另一个用 C++ 完成的库,所以这次我不能使用 Python。
My idea was to have the serial port and daq card in separate threads acquiring data and then sending updated information to the main program to update the plot with the new data. I have finally gotten threading to work correctly, but what I can't seem to figure out is how to update the plot from inside the thread, as what I have causes a crash.
我的想法是让串行端口和 daq 卡在单独的线程中获取数据,然后将更新的信息发送到主程序以使用新数据更新绘图。我终于让线程正常工作,但我似乎无法弄清楚如何从线程内部更新绘图,因为我所拥有的会导致崩溃。
Here is what I have so far:
这是我到目前为止所拥有的:
#pragma once
#include <math.h>
namespace PlotUpdate {
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
using namespace System::Threading;
/// <summary>
/// Summary for Form1
/// </summary>
public ref class Form1 : public System::Windows::Forms::Form
{
public:
Form1(void)
{
InitializeComponent();
//
//TODO: Add the constructor code here
//
th1 = gcnew Thread(gcnew ThreadStart(this, &Form1::th1Method));
}
delegate void UpdatePlot();
UpdatePlot^ myDelegate;
protected:
/// <summary>
/// Clean up any resources being used.
/// </summary>
~Form1()
{
if (components)
{
delete components;
}
}
private: System::Windows::Forms::Button^ button1;
protected:
private:
/// <summary>
/// Required designer variable.
/// </summary>
System::ComponentModel::Container ^components;
private: System::Windows::Forms::DataVisualization::Charting::Chart^ chart1;
Thread ^th1;
#pragma region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
void InitializeComponent(void)
{
System::Windows::Forms::DataVisualization::Charting::ChartArea^ chartArea1 = (gcnew System::Windows::Forms::DataVisualization::Charting::ChartArea());
System::Windows::Forms::DataVisualization::Charting::Series^ series1 = (gcnew System::Windows::Forms::DataVisualization::Charting::Series());
this->button1 = (gcnew System::Windows::Forms::Button());
this->chart1 = (gcnew System::Windows::Forms::DataVisualization::Charting::Chart());
(cli::safe_cast<System::ComponentModel::ISupportInitialize^ >(this->chart1))->BeginInit();
this->SuspendLayout();
//
// button1
//
this->button1->Location = System::Drawing::Point(291, 369);
this->button1->Name = L"button1";
this->button1->Size = System::Drawing::Size(75, 23);
this->button1->TabIndex = 0;
this->button1->Text = L"button1";
this->button1->UseVisualStyleBackColor = true;
this->button1->Click += gcnew System::EventHandler(this, &Form1::button1_Click);
//
// chart1
//
chartArea1->Name = L"ChartArea1";
this->chart1->ChartAreas->Add(chartArea1);
this->chart1->Location = System::Drawing::Point(32, 30);
this->chart1->Name = L"chart1";
series1->ChartArea = L"ChartArea1";
series1->ChartType = System::Windows::Forms::DataVisualization::Charting::SeriesChartType::Line;
series1->Name = L"Series1";
series1->XValueMember = L"xvals";
series1->YValueMembers = L"yvals";
this->chart1->Series->Add(series1);
this->chart1->Size = System::Drawing::Size(669, 314);
this->chart1->TabIndex = 1;
this->chart1->Text = L"chart1";
//
// Form1
//
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->ClientSize = System::Drawing::Size(778, 415);
this->Controls->Add(this->chart1);
this->Controls->Add(this->button1);
this->Name = L"Form1";
this->Text = L"Form1";
(cli::safe_cast<System::ComponentModel::ISupportInitialize^ >(this->chart1))->EndInit();
this->ResumeLayout(false);
}
#pragma endregion
static double time = 0.0;
private: System::Void th1Method()
{
for(int i=0;i<500;i++)
{
this->chart1->Series["Series1"]->Points->AddXY(time, sin(time));
time += 0.1;
Thread::Sleep(1);
}
}
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
th1->Start();
}
};
}
The code compiles and runs, until I start the thread, and it crashes. I know, I can't update the GUI from another process, so I don't really know how I am supposed to do this. I have tried (And I apologize for not having sample code) to create a temporary Collection of DataPoint Objects and then update the plot using a TimerEvent, but I ran into issues of not being able to use this-> notation inside a static method of the class.
代码编译并运行,直到我启动线程,然后它崩溃了。我知道,我无法从另一个进程更新 GUI,所以我真的不知道我该怎么做。我尝试过(我为没有示例代码而道歉)创建一个临时的 DataPoint 对象集合,然后使用 TimerEvent 更新绘图,但是我遇到了无法在静态方法中使用 this-> 符号的问题班上。
Any hints/tips/advice for this situation?
对于这种情况有什么提示/提示/建议吗?
采纳答案by paercebal
Unless I'm wrong, you're trying to modify the UI from within a thread that is not the UI thread, which is an error.
除非我错了,否则您试图从不是 UI 线程的线程中修改 UI,这是一个错误。
If this is the case, you should use the BeginInvoke method of your Form to execute code from within the UI thread.
如果是这种情况,您应该使用 Form 的 BeginInvoke 方法从 UI 线程中执行代码。
I'm unfamiliar with C++/CLI + WinForms code, so I am unable to provide you with a code correction, but in C#, it would have been something like:
我不熟悉 C++/CLI + WinForms 代码,所以我无法为您提供代码更正,但在 C# 中,它会是这样的:
private void th1Method()
{
for(int i=0;i<500;i++)
{
this.BeginInvoke
((Action)(
() =>
{
this.chart1.Series["Series1"].Points.AddXY(time, sin(time));
time += 0.1;
}
)) ;
Thread.Sleep(1);
}
}
Note the BeginInvoke call, which takes here a lambda function (of type Action, meaning no parameters, and no return value). This lambda function will be queued in the UI thread, and executed later, in the UI thread.
注意BeginInvoke 调用,它在这里接受一个lambda 函数(类型为Action,意味着没有参数,也没有返回值)。这个 lambda 函数将在 UI 线程中排队,稍后在 UI 线程中执行。
For more information about BeginInvoke, see: http://msdn.microsoft.com/en-us/library/0b1bf3y3.aspx
有关 BeginInvoke 的更多信息,请参阅:http: //msdn.microsoft.com/en-us/library/0b1bf3y3.aspx