Trådet plotting i Visual Studio C ++ med Windows Forms

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg skriver en ansøgning ved hjælp af Visual Studio C ++ 2010 til at udføre Data Acquisition og plotte disse oplysninger i realtid. Jeg bruger Windows Forms til at oprette GUI. Jeg tager data fra både seriel port og et DAQ-kort (som jeg har biblioteker til og har brugt) og vil plotte dem i realtid. Jeg har gjort dette før i Python, men jeg skal bruge et andet bibliotek, der er gjort i C ++, så jeg ikke kan bruge Python denne gang.


Min idé var at have seriel port og daq-kort i separate tråde at erhverve data og derefter sende opdaterede oplysninger til hovedprogrammet for at opdatere plottet med de nye data. Jeg har endelig fået tråde til at fungere korrekt, men hvad jeg ikke synes at finde ud af, er hvordan man opdaterer plottet inde fra tråden, som det jeg har forårsaget et nedbrud.


Her er hvad jeg har hidtil:


#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();
         }
};
 }


Koden kompilerer og kører, indtil jeg starter tråden, og den går ned. Jeg ved, jeg kan ikke opdatere GUI fra en anden proces, så jeg ved ikke rigtig, hvordan jeg skal gøre dette. Jeg har forsøgt (og jeg undskylder for ikke at have prøvekode) for at oprette en midlertidig indsamling af DataPoint-objekter og derefter opdatere plottet ved hjælp af en TimerEvent, men jeg løb over problemer med at ikke kunne bruge denne-> notation inde i en statisk metode til klassen.


Eventuelle hints/tips/råd til denne situation?

Bedste reference


Medmindre jeg er forkert, prøver du at ændre brugergrænsefladen fra en tråd, der ikke er brugergrænsefladen, hvilket er en fejl.


Hvis dette er tilfældet, skal du bruge StartInvoke-metoden i din formular til at udføre kode inden for brugergrænsefladen.


Jeg er ikke bekendt med C ++/CLI + WinForms-koden, så jeg kan ikke give dig en kodekorrektion, men i C # ville det have været noget som:


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);
         }
     }


Bemærk StartInvoke-opkaldet, der tager her en lambda-funktion (af typen Action, der betyder ingen parametre og ingen returværdi). Denne lambda-funktion vil blive køet i brugergrænsefladen og udføres senere i UI-tråden.


Yderligere oplysninger om BeginInvoke finder du på: http://msdn.microsoft.com/en-us/library/0b1bf3y3.aspx[3]