【问题标题】:Update GUI using C++/CLR Windows Forms使用 C++/CLR Windows 窗体更新 GUI
【发布时间】:2016-06-17 15:56:55
【问题描述】:

您好,我正在尝试创建一个可以读取数据、处理数据并将其实时绘制在图表上的应用程序,直到我按下阻止应用程序读取和处理更多数据的 ESC 键。我在 Visual C++/CLR 中创建了这个应用程序:
My Real Time Data Processing and Plotting App

当我点击分析数据按钮时(见图)。正在读取和处理数据(基于控制台窗口中的结果),但我的 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 来做到这一点?如果是这样,你能给我一个例子吗?非常感谢您的帮助!谢谢!

【问题讨论】:

    标签: .net user-interface visual-c++ clr real-time-updates


    【解决方案1】:

    你说得对 - GUI 线程仅限于在 while 循环中执行,并且由于它从不离开方法,因此它从不执行窗口消息处理,包括 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&lt;int&gt;,并且您不能将此类型传递给任何托管方法。您需要将数据从向量中复制到 .Net 类型中,以便将其传递给BeginInvoke

    【讨论】:

    • 谢谢!我遇到了这个例子的问题。每当我尝试构建时,我都会收到第 2 行(无效的委托初始化程序)和第 24 行(需要类型说明符)的错误。是否有我必须 #include 才能使这些行工作的文件?
    • 您可能需要在第 2 行中为函数添加类名前缀,但由于我没有您的类,所以我没有包含它。第 24 行可能相同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-12
    • 1970-01-01
    • 2015-03-17
    • 1970-01-01
    • 1970-01-01
    • 2020-02-14
    • 1970-01-01
    相关资源
    最近更新 更多