Потоковое построение в Visual Studio C ++ с Windows Forms - PullRequest
1 голос
/ 18 апреля 2011

Я пишу приложение с использованием Visual Studio C ++ 2010 для выполнения сбора данных и отображения этой информации в режиме реального времени.Я использую Windows Forms для создания графического интерфейса.Я беру данные как с последовательного порта, так и с карты DAQ (для которых у меня есть библиотеки и которые я использовал) и хочу отображать их в режиме реального времени.Я делал это раньше в Python, но я должен использовать другую библиотеку, которая сделана на C ++, поэтому я не могу использовать Python на этот раз.

Моя идея состояла в том, чтобы последовательный порт и карта Daq были разделеныпотоки получают данные, а затем отправляют обновленную информацию в основную программу для обновления графика с новыми данными.Наконец-то я получил многопоточность для правильной работы, но я не могу понять, как обновить график из нити, так как у меня возникает сбой.

Вот что у меня так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();
         }
};
 }

Код компилируется и запускается до тех пор, пока я не запускаю поток и он не падает.Я знаю, что не могу обновить GUI из другого процесса, поэтому я не знаю, как я должен это делать.Я попытался (и я прошу прощения за отсутствие примера кода), чтобы создать временную коллекцию объектов DataPoint, а затем обновить график, используя TimerEvent, но я столкнулся с проблемами невозможности использовать эту-> нотацию внутри статического методакласс.

Любые советы / подсказки / советы для этой ситуации?

1 Ответ

1 голос
/ 18 апреля 2011

Если я не ошибаюсь, вы пытаетесь изменить пользовательский интерфейс из потока, который не является потоком пользовательского интерфейса, что является ошибкой.

В этом случае вам следует использовать BeginInvokeметод вашей формы для выполнения кода из потока пользовательского интерфейса.

Я незнаком с кодом 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);
         }
     }

Обратите внимание на вызов BeginInvoke, который принимает здесь лямбда-функцию (типа Action, то есть без параметров и без возвращаемого значения).Эта лямбда-функция будет поставлена ​​в очередь в потоке пользовательского интерфейса и будет выполнена позже в потоке пользовательского интерфейса.

Для получения дополнительной информации о BeginInvoke см .: http://msdn.microsoft.com/en-us/library/0b1bf3y3.aspx

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...