Обновление графического интерфейса с помощью C++/CLR Windows Forms

Здравствуйте, я пытаюсь создать приложение, которое может считывать данные, обрабатывать их и отображать на графике в режиме реального времени, пока я не нажму клавишу ESC, которая не остановит приложение от чтения и обработки дополнительных данных. Я создал это приложение в Visual C++/CLR:
Мое приложение для обработки данных и построения графиков в реальном времени

Когда я нажимаю кнопку анализа данных (см. рисунок). Данные считываются и обрабатываются (на основе результатов в окне консоли), но мой график в моем графическом интерфейсе не обновляется в режиме реального времени. Он обновляется только до тех пор, пока я не остановлю приложение от чтения и обработки каких-либо данных. Я знаю, что проблема как-то связана с потоками. Я полагаю, что запускаю чтение и обработку данных в том же потоке, что и обновление графического интерфейса. Ниже приведен код, в котором я выполняю сбор и обработку данных и обновления графического интерфейса:

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, делегата и Invoke, но большинство из них написаны на С# и их очень трудно понять. Причина, по которой я использую C++, заключается в том, что я использую некоторые DLL-файлы для вызова определенных функций, которые помогают мне получать данные, которые я хочу обработать и проанализировать, а C#, по моему опыту, плохо работает при попытке использовать DLL-файлы. Кроме того, документация и примеры, которые я нашел о том, как использовать методы в C++, которые я упомянул, не очень ясны или плохо переводятся на C++.

Мой вопрос: как я могу обновить свою диаграмму графического интерфейса в режиме реального времени в C++/CLR? Можно ли это сделать с помощью C++/CLR? Если да, можете ли вы привести пример? Ваша помощь очень ценится! Спасибо!


person halochief996    schedule 17.06.2016    source источник


Ответы (1)


Вы правы - поток графического интерфейса ограничен выполнением в цикле while, и, поскольку он никогда не покидает метод, он никогда не выполняет обработку оконных сообщений, включая сообщения WM_PAINT, и поэтому экран никогда не перерисовывается.

Вам нужно поместить получение в отдельный поток, а затем синхронизировать вызовы с графическим интерфейсом для обновления. Вот пример:

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.

person codekaizen    schedule 30.06.2016
comment
Благодарю вас! У меня возникла проблема с примером. Всякий раз, когда я пытаюсь построить, я получаю ошибки для строки 2 (недопустимый инициализатор делегата) и строки 24 (ожидается спецификатор типа). Есть ли файл, который я должен #include, чтобы эти строки работали? - person halochief996; 01.07.2016
comment
Вам, вероятно, нужно префикс функции с именем класса в строке 2, но, поскольку у меня нет вашего класса, я не включил его. Вероятно, то же самое для строки 24. - person codekaizen; 01.07.2016