Как обновить декартову диаграмму LiveCharts в WinForm при изменении переменной во внешнем классе?

Недавно я купил гарнитуру ЭЭГ (NeuroSky MindWave Mobile). Это просто устройство, которое носят на голове и собирают данные о мозговых волнах. Устройство передает эти данные в режиме реального времени через Bluetooth, которые затем могут быть прочитаны/проанализированы программой.

NeuroSky предоставляет простой в использовании набор API-интерфейсов, которые я использовал для написания базового класса для чтения потоковых данных гарнитуры. Сокращенный вариант его выглядит следующим образом:

using System;
using NeuroSky.ThinkGear;


namespace MindWave_Reader
{
    class ReadEEG
    {
        public double AlphaValue { get; set; }
        private Connector connector;

        public ReadEEG()
        {
            // Initialize a new Connector and add event handlers
            connector = new Connector();
            connector.DeviceConnected += new EventHandler(OnDeviceConnected);

            // Scan for headset on COM7 port
            connector.ConnectScan("COM7");
        }


        // Called when a device is connected
        public void OnDeviceConnected(object sender, EventArgs e) {
            Connector.DeviceEventArgs de = (Connector.DeviceEventArgs)e;

            Console.WriteLine("Device found on: " + de.Device.PortName);
            de.Device.DataReceived += new EventHandler(OnDataReceived);
        }


        // Called when data is received from a device
        public void OnDataReceived(object sender, EventArgs e) {
            Device.DataEventArgs de = (Device.DataEventArgs)e;
            DataRow[] tempDataRowArray = de.DataRowArray;

            TGParser tgParser = new TGParser();
            tgParser.Read(de.DataRowArray);

            /* Loops through the newly parsed data of the connected headset */
            for (int i = 0; i < tgParser.ParsedData.Length; i++) {
                if (tgParser.ParsedData[i].ContainsKey("EegPowerAlpha")) {
                    AlphaValue = tgParser.ParsedData[i]["EegPowerAlpha"];
                    Console.WriteLine("Alpha: " + AlphaValue);
                }
            }
        }
    }
}

Приведенный выше код сначала пытается подключиться к гарнитуре ЭЭГ. После подключения каждый раз при получении данных от гарнитуры вызывается OnDataReceived(). Этот метод выводит соответствующие значения потоковых данных гарнитуры (альфа-волны) на консоль.

Теперь я хочу отобразить эти значения альфа-волн в режиме реального времени на декартовой диаграмме, и я обнаружил LiveCharts, который кажется удобной графической библиотекой. Этот пример WinForms соответствует тому, чего я пытаюсь достичь.

Значения альфа-волны должны быть нанесены по оси Y в зависимости от времени по оси X. Однако вместо того, чтобы обновлять диаграмму каждые 500 мс, как в примере, я бы хотел, чтобы она обновлялась только при получении данных от гарнитуры (другими словами, когда переменная AlphaValue обновляется OnDataReceived() в классе ReadEEG).

Я хотел бы знать, как я могу заставить свою WinForm взаимодействовать с классом ReadEEG, чтобы таким образом обновлять его декартову диаграмму. Я немного новичок, поэтому любая помощь будет принята с благодарностью.

Я действительно надеюсь, что я ясно выразился и попытался сделать объяснения как можно более простыми. Если у вас есть какие-либо вопросы, не стесняйтесь спрашивать. Заранее спасибо за вашу помощь!

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
1 310
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вот один из способов сделать это. Я добавил новое событие в класс ReadEEG. Это новое событие возникает всякий раз, когда появляется новый AlphaValue. В основной форме он подписывается на это событие и добавляет новые значения в коллекцию ChartValues, что и показано на LiveChart.

using System;
using System.Data;
using NeuroSky.ThinkGear;

namespace MindWave_Reader
{
  class ReadEEG
  {
    public double AlphaValue { get; set; }
    private Connector connector;

    public ReadEEG()
    {
      // Initialize a new Connector and add event handlers
      connector = new Connector();
      connector.DeviceConnected += new EventHandler(OnDeviceConnected);

      // Scan for headset on COM7 port
      connector.ConnectScan("COM7");
    }

    // Called when a device is connected
    public void OnDeviceConnected(object sender, EventArgs e)
    {
      Connector.DeviceEventArgs de = (Connector.DeviceEventArgs)e;

      Console.WriteLine("Device found on: " + de.Device.PortName);
      de.Device.DataReceived += new EventHandler(OnDataReceived);
    }

    // Called when data is received from a device
    public void OnDataReceived(object sender, EventArgs e)
    {
      Device.DataEventArgs de = (Device.DataEventArgs)e;
      DataRow[] tempDataRowArray = de.DataRowArray;

      TGParser tgParser = new TGParser();
      tgParser.Read(de.DataRowArray);

      /* Loops through the newly parsed data of the connected headset */
      for (int i = 0; i < tgParser.ParsedData.Length; i++)
      {
        if (tgParser.ParsedData[i].ContainsKey("EegPowerAlpha"))
        {
          AlphaValue = tgParser.ParsedData[i]["EegPowerAlpha"];
          Console.WriteLine("Alpha: " + AlphaValue);
          // Raise the AlphaReceived event with the new reading.
          OnAlphaReceived(new AlphaReceivedEventArgs() { Alpha = AlphaValue });
        }
      }
    }

    /// <summary>
    /// The arguments for the <see cref = "AlphaReceived"/> event.
    /// </summary>
    public class AlphaReceivedEventArgs : EventArgs
    {
      /// <summary>
      /// The alpha value that was just received.
      /// </summary>
      public double Alpha { get; set; }
    }

    /// <summary>
    /// Raises the <see cref = "AlphaReceived"/> event if there is a subscriber.
    /// </summary>
    /// <param name = "e">Contains the new alpha value.</param>
    protected virtual void OnAlphaReceived(AlphaReceivedEventArgs e)
    {
      AlphaReceived?.Invoke(this, e);
    }

    /// <summary>
    /// Event that gets raised whenever a new AlphaValue is received from the
    /// device.
    /// </summary>
    public event EventHandler AlphaReceived;
  }
}

На Form1 я добавил LiveCharts.WinForms.CartesianChart с дизайнером. Код выглядит следующим образом:

using LiveCharts;
using LiveCharts.Configurations;
using LiveCharts.Wpf;
using System;
using System.Windows.Forms;
using static MindWave_Reader.ReadEEG;

namespace MindWave_Reader
{
  public partial class Form1 : Form
  {
    /// <summary>
    /// Simple class to hold an alpha value and the time it was received. Used
    /// for charting.
    /// </summary>
    public class EEGPowerAlphaValue
    {
      public DateTime Time { get; }
      public double AlphaValue { get; }

      public EEGPowerAlphaValue(DateTime time, double alpha)
      {
        Time = time;
        AlphaValue = alpha;
      }
    }

    private ReadEEG _readEEG;

    /// <summary>
    /// Contains the alpha values we're showing on the chart.
    /// </summary>
    public ChartValues<EEGPowerAlphaValue> ChartValues { get; set; }

    public Form1()
    {
      InitializeComponent();

      // Create the mapper.
      var mapper = Mappers.Xy<EEGPowerAlphaValue>()
          .X(model => model.Time.Ticks)   // use Time.Ticks as X
          .Y(model => model.AlphaValue);  // use the AlphaValue property as Y

      // Lets save the mapper globally.
      Charting.For<EEGPowerAlphaValue>(mapper);

      // The ChartValues property will store our values array.
      ChartValues = new ChartValues<EEGPowerAlphaValue>();
      cartesianChart1.Series = new SeriesCollection
      {
        new LineSeries
        {
          Values = ChartValues,
          PointGeometrySize = 18,
          StrokeThickness = 4
        }
      };

      cartesianChart1.AxisX.Add(new Axis
      {
        DisableAnimations = true,
        LabelFormatter = value => new DateTime((long)value).ToString("mm:ss"),
        Separator = new Separator
        {
          Step = TimeSpan.FromSeconds(1).Ticks
        }
      });

      SetAxisLimits(DateTime.Now);
    }

    private void StartButton_Click(object sender, EventArgs e)
    {
      _readEEG = new ReadEEG();
      _readEEG.AlphaReceived += _readEEG_AlphaReceived;
    }

    /// <summary>
    /// Called when a new alpha value is received from the device. Updates the
    /// chart with the new value.
    /// </summary>
    /// <param name = "sender">The <see cref = "ReadEEG"/> object that raised this
    /// event.</param>
    /// <param name = "e">The <see cref = "AlphaReceivedEventArgs"/> that contains
    /// the new alpha value.</param>
    private void _readEEG_AlphaReceived(object sender, EventArgs e)
    {
      AlphaReceivedEventArgs alphaReceived = (AlphaReceivedEventArgs)e;

      // Add the new alpha reading to our ChartValues.
      ChartValues.Add(
        new EEGPowerAlphaValue(
          DateTime.Now,
          alphaReceived.Alpha));

      // Update the chart limits.
      SetAxisLimits(DateTime.Now);

      // Lets only use the last 30 values. You may want to adjust this.
      if (ChartValues.Count > 30)
      {
        ChartValues.RemoveAt(0);
      }
    }

    private void SetAxisLimits(DateTime now)
    {
      if (cartesianChart1.InvokeRequired)
      {
        cartesianChart1.Invoke(new Action(() => SetAxisLimits(now)));
      }
      else
      {
        // Lets force the axis to be 100ms ahead. You may want to adjust this.
        cartesianChart1.AxisX[0].MaxValue =
          now.Ticks + TimeSpan.FromSeconds(1).Ticks;

        // We only care about the last 8 seconds. You may want to adjust this.
        cartesianChart1.AxisX[0].MinValue =
          now.Ticks - TimeSpan.FromSeconds(8).Ticks;
      }
    }
  }
}

Большое спасибо! Он делает именно то, что я искал. Я проголосовал, но пользователи с репутацией менее 15, по-видимому, не влияют на оценку. Просто из интереса, это «самый чистый» способ чтения изменения переменной в классе ReadEEG?

user11327116 09.04.2019 06:13

Вы должны иметь возможность пометить этот ответ как принятый, даже если вы не можете проголосовать. Это использование немного отличается от того, что я обычно делаю, если вы хотите получать уведомления об изменении свойства. Для этого вам нужно прочитать об INotifyPropertyChanged. Вы можете изменить то, как я сделал это выше, и поместить список альфа-значений в качестве свойства внутри класса ReadEEG, а затем привязать диаграмму к этому списку.

gunnerone 09.04.2019 16:25

Просто пометил ответ как принятый. Я здесь новенькая, так что еще ко всему привыкаю. Буду ли я вас слишком сильно беспокоить, если попрошу код с INotifyPropertyChanged? Хотел бы я посмотреть, как ты это сделаешь. Спасибо за ваши содержательные ответы!

user11327116 09.04.2019 18:36

Другие вопросы по теме