当外部 class 中的变量发生变化时,如何在 WinForm 中更新 LiveCharts 笛卡尔图表?
How to update LiveCharts Cartesian chart in a WinForm when variable in external class changes?
我最近购买了脑电图耳机 (NeuroSky MindWave Mobile)。它只是一种戴在头上的设备,可以捕捉脑电波数据。该设备通过蓝牙实时传输此数据,然后可以通过软件程序 read/analysed。
NeuroSky 提供了一组易于使用的 API,我用它来编写一个基本的 class 来读取流式耳机数据。它的缩写版本如下:
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);
}
}
}
}
}
以上代码首先尝试连接到 EEG 耳机。连接后,每次从耳机接收到数据时,都会调用 OnDataReceived()
。此方法将相关的流式耳机数据值(alpha 波)打印到控制台。
我现在想在笛卡尔图表上实时显示这些 alpha 波值,我发现了 LiveCharts,它看起来像是一个简洁的图形库。 This WinForms example 与我想要实现的目标一致。
应在 Y 轴上绘制 alpha 波值,在 X 轴上绘制时间。但是,我不想像示例中那样每 500 毫秒更新一次图表,而是希望它仅在从耳机接收到数据时更新(换句话说,当 AlphaValue
变量被 OnDataReceived()
更新时ReadEEG class).
我想知道如何让我的 WinForm 与 ReadEEG 交互 class 以这种方式更新其笛卡尔图表。我有点新手,所以任何帮助将不胜感激。
我真的希望我已经把自己说清楚了,并尽量让解释尽可能简单。如果您有任何问题,请随时提出。预先感谢您的所有帮助!
这是您可以做到的一种方法。我向 ReadEEG
class 添加了一个新事件。只要有新 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;
}
}
}
}
我最近购买了脑电图耳机 (NeuroSky MindWave Mobile)。它只是一种戴在头上的设备,可以捕捉脑电波数据。该设备通过蓝牙实时传输此数据,然后可以通过软件程序 read/analysed。
NeuroSky 提供了一组易于使用的 API,我用它来编写一个基本的 class 来读取流式耳机数据。它的缩写版本如下:
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);
}
}
}
}
}
以上代码首先尝试连接到 EEG 耳机。连接后,每次从耳机接收到数据时,都会调用 OnDataReceived()
。此方法将相关的流式耳机数据值(alpha 波)打印到控制台。
我现在想在笛卡尔图表上实时显示这些 alpha 波值,我发现了 LiveCharts,它看起来像是一个简洁的图形库。 This WinForms example 与我想要实现的目标一致。
应在 Y 轴上绘制 alpha 波值,在 X 轴上绘制时间。但是,我不想像示例中那样每 500 毫秒更新一次图表,而是希望它仅在从耳机接收到数据时更新(换句话说,当 AlphaValue
变量被 OnDataReceived()
更新时ReadEEG class).
我想知道如何让我的 WinForm 与 ReadEEG 交互 class 以这种方式更新其笛卡尔图表。我有点新手,所以任何帮助将不胜感激。
我真的希望我已经把自己说清楚了,并尽量让解释尽可能简单。如果您有任何问题,请随时提出。预先感谢您的所有帮助!
这是您可以做到的一种方法。我向 ReadEEG
class 添加了一个新事件。只要有新 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;
}
}
}
}