本代码调用ZedGraph绘图框架,自己先安装好ZedGraph环境,然后拖一个zedGraphControl控件就行了,直接黏贴下面代码
基本代码显示
using System;
using System.Windows.Forms;
using ZedGraph;
using System.Timers;
namespace ECGPlot
{
public partial class Form1 : Form
{
// 定义导联数量和每个导联的电压偏移量
private const int ChannelCount = 8;
private const int VoltageOffset = 150; // 每个导联的偏移量
// 用于存储每个导联的数据点列表和曲线对象
private PointPairList[] _dataLists;
private LineItem[] _curves;
// 定时器用于模拟ECG信号数据更新
private System.Timers.Timer _timer;
private int _currentIndex = 0;
private int _maxPoints = 500;
private double[][] _yValues;
private double _timeIncrement = 0.1;
private double _currentTime = 0;
private Random _random = new Random();
public Form1()
{
InitializeComponent();
InitializeGraph(); // 初始化图表
StartTimer(); // 启动定时器
}
private void InitializeGraph()
{
// 获取图表区域对象
GraphPane myPane = zedGraphControl1.GraphPane;
// 设置图表标题和轴标题
myPane.Title.Text = "ECG Data";
myPane.XAxis.Title.Text = "Time";
myPane.YAxis.Title.Text = "Voltage";
// 初始化数据点列表和曲线数组
_dataLists = new PointPairList[ChannelCount];
_curves = new LineItem[ChannelCount];
_yValues = new double[ChannelCount][];
for (int i = 0; i < ChannelCount; i++)
{
// 为每个导联创建数据点列表和曲线对象,并添加到图表中
_dataLists[i] = new PointPairList();
_curves[i] = myPane.AddCurve($"ECG Channel {i + 1}", _dataLists[i], GetColor(i), SymbolType.None);
_yValues[i] = new double[_maxPoints];
}
// 设置X轴和Y轴的范围
myPane.XAxis.Scale.Min = 0;
myPane.XAxis.Scale.Max = _maxPoints * _timeIncrement;
myPane.YAxis.Scale.Min = -600;
myPane.YAxis.Scale.Max = 600 + VoltageOffset * (ChannelCount - 1);
// 显示网格线
myPane.XAxis.MajorGrid.IsVisible = true;
myPane.YAxis.MajorGrid.IsVisible = true;
// 应用更改并刷新图表
zedGraphControl1.AxisChange();
}
private System.Drawing.Color GetColor(int index)
{
// 定义一组颜色用于不同导联的曲线
System.Drawing.Color[] colors = {
System.Drawing.Color.Black,
System.Drawing.Color.Red,
System.Drawing.Color.Blue,
System.Drawing.Color.Green,
System.Drawing.Color.Purple,
System.Drawing.Color.Orange,
System.Drawing.Color.Brown,
System.Drawing.Color.Magenta
};
// 根据索引返回颜色
return colors[index % colors.Length];
}
private void StartTimer()
{
// 创建并配置定时器
_timer = new System.Timers.Timer(100); // 100毫秒的更新频率
_timer.Elapsed += OnTimedEvent; // 绑定定时器事件
_timer.AutoReset = true; // 自动重置
_timer.Enabled = true; // 启用定时器
}
private void OnTimedEvent(Object source, ElapsedEventArgs e)
{
// 为每个导联生成模拟ECG信号数据并更新曲线
for (int i = 0; i < ChannelCount; i++)
{
double voltage = _random.Next(-400, 400) + i * VoltageOffset; // 生成带偏移量的电压数据
_yValues[i][_currentIndex] = voltage;
if (_dataLists[i].Count < _maxPoints)
{
// 添加新的数据点
_dataLists[i].Add(_currentTime, voltage);
}
else
{
// 更新现有数据点
_dataLists[i][_currentIndex].Y = voltage;
}
}
// 更新时间和当前索引
_currentTime += _timeIncrement;
_currentIndex = (_currentIndex + 1) % _maxPoints;
// 使图表无效以触发重绘
zedGraphControl1.Invalidate();
}
private void Form1_Load(object sender, EventArgs e)
{
// 窗体加载事件处理方法(目前为空)
}
}
}
注释解释:
- 全局变量定义:定义导联数量、每个导联的电压偏移量,以及存储数据和曲线的变量。
- 构造函数:调用
InitializeGraph
和StartTimer
方法初始化图表和启动定时器。 - InitializeGraph 方法:初始化图表区域,设置标题和轴标题,创建每个导联的曲线对象,并设置轴的范围和网格。
- GetColor 方法:定义一组颜色,根据索引返回颜色用于不同导联的曲线。
- StartTimer 方法:创建并配置定时器,设置定时器事件处理方法。
- OnTimedEvent 方法:在定时器触发时生成模拟ECG信号数据,为每个导联添加或更新数据点,并刷新图表。
- Form1_Load 方法:窗体加载事件处理方法(目前为空)。
添加了y轴方向的导联标签
using System;
using System.Windows.Forms;
using ZedGraph;
using System.Timers;
namespace ECGPlot
{
public partial class Form1 : Form
{
// 定义导联数量和每个导联的电压偏移量
private const int ChannelCount = 8;
private const int VoltageOffset = 500; // 每个导联的偏移量
// 用于存储每个导联的数据点列表和曲线对象
private PointPairList[] _dataLists;
private LineItem[] _curves;
// 定时器用于模拟ECG信号数据更新
private System.Timers.Timer _timer;
private int _currentIndex = 0;
private int _maxPoints = 500;
private double[][] _yValues;
private double _timeIncrement = 0.1;
private double _currentTime = 0;
private Random _random = new Random();
public Form1()
{
InitializeComponent();
InitializeGraph(); // 初始化图表
StartTimer(); // 启动定时器
}
private void InitializeGraph()
{
// 获取图表区域对象
GraphPane myPane = zedGraphControl1.GraphPane;
// 设置图表标题和轴标题
myPane.Title.Text = "ECG Data";
myPane.XAxis.Title.Text = "Time (s)";
myPane.YAxis.Title.Text = "Voltage";
// 初始化数据点列表和曲线数组
_dataLists = new PointPairList[ChannelCount];
_curves = new LineItem[ChannelCount];
_yValues = new double[ChannelCount][];
for (int i = 0; i < ChannelCount; i++)
{
// 为每个导联创建数据点列表和曲线对象,并添加到图表中
_dataLists[i] = new PointPairList();
_curves[i] = myPane.AddCurve("", _dataLists[i], GetColor(i), SymbolType.None);
_yValues[i] = new double[_maxPoints];
}
// 移除图例
myPane.Legend.IsVisible = false;
// 设置X轴和Y轴的范围
myPane.XAxis.Scale.Min = 0;
myPane.XAxis.Scale.Max = _maxPoints * _timeIncrement;
myPane.YAxis.Scale.Min = -800;
myPane.YAxis.Scale.Max = 800 + VoltageOffset * (ChannelCount - 1);
// 显示网格线
myPane.XAxis.MajorGrid.IsVisible = true;
myPane.YAxis.MajorGrid.IsVisible = true;
// 隐藏Y=0的实线
myPane.YAxis.MajorGrid.IsZeroLine = false;
// 自定义Y轴刻度标注
Scale yScale = myPane.YAxis.Scale;
yScale.MajorStep = VoltageOffset;
yScale.MinorStep = VoltageOffset;
yScale.MajorStepAuto = false;
yScale.MinorStepAuto = false;
myPane.YAxis.ScaleFormatEvent += new Axis.ScaleFormatHandler(FormatYScale);
// 应用更改并刷新图表
zedGraphControl1.AxisChange();
}
private string FormatYScale(GraphPane pane, Axis axis, double val, int index)
{
// 自定义Y轴刻度标注
int leadIndex = (int)Math.Round(val / VoltageOffset);
if (leadIndex >= 0 && leadIndex < ChannelCount)
{
return $"Lead {leadIndex + 1}";
}
return "";
}
private System.Drawing.Color GetColor(int index)
{
// 定义一组颜色用于不同导联的曲线
System.Drawing.Color[] colors = {
System.Drawing.Color.Black,
System.Drawing.Color.Red,
System.Drawing.Color.Blue,
System.Drawing.Color.Green,
System.Drawing.Color.Purple,
System.Drawing.Color.Orange,
System.Drawing.Color.Brown,
System.Drawing.Color.Magenta
};
// 根据索引返回颜色
return colors[index % colors.Length];
}
private void StartTimer()
{
// 创建并配置定时器
_timer = new System.Timers.Timer(10); // 100毫秒的更新频率
_timer.Elapsed += OnTimedEvent; // 绑定定时器事件
_timer.AutoReset = true; // 自动重置
_timer.Enabled = true; // 启用定时器
}
private void OnTimedEvent(Object source, ElapsedEventArgs e)
{
// 为每个导联生成模拟ECG信号数据并更新曲线
for (int i = 0; i < ChannelCount; i++)
{
double voltage = _random.Next(-200, 200) + i * VoltageOffset; // 生成带偏移量的电压数据
_yValues[i][_currentIndex] = voltage;
if (_dataLists[i].Count < _maxPoints)
{
// 添加新的数据点
_dataLists[i].Add(_currentTime, voltage);
}
else
{
// 更新现有数据点
_dataLists[i][_currentIndex].Y = voltage;
}
}
// 更新时间和当前索引
_currentTime += _timeIncrement;
_currentIndex = (_currentIndex + 1) % _maxPoints;
// 更新X轴刻度显示
zedGraphControl1.GraphPane.XAxis.Scale.TextLabels = GenerateTimeLabels(_currentTime, _timeIncrement, _maxPoints);
// 使图表无效以触发重绘
zedGraphControl1.Invalidate();
}
private string[] GenerateTimeLabels(double currentTime, double increment, int maxPoints)
{
string[] labels = new string[maxPoints];
double startTime = currentTime - (maxPoints * increment);
for (int i = 0; i < maxPoints; i++)
{
labels[i] = (startTime + i * increment).ToString("0.0");
}
return labels;
}
private void Form1_Load(object sender, EventArgs e)
{
// 窗体加载事件处理方法(目前为空)
}
}
}
添加了时间刻度跟随时间扫描变化
using System;
using System.Windows.Forms;
using ZedGraph;
using System.Timers;
namespace ECGPlot
{
public partial class Form1 : Form
{
// 定义导联数量和每个导联的电压偏移量
private const int ChannelCount = 8;
private const int VoltageOffset = 700; // 每个导联的偏移量
// 用于存储每个导联的数据点列表和曲线对象
private PointPairList[] _dataLists;
private LineItem[] _curves;
// 定时器用于模拟ECG信号数据更新
private System.Timers.Timer _timer;
private int _currentIndex = 0;
private int _maxPoints = 800;
private double[][] _yValues;
private double _timeIncrement = 0.1;
private double _currentTime = 0;
private Random _random = new Random();
private DateTime[] _timeLabels;
public Form1()
{
InitializeComponent();
InitializeGraph(); // 初始化图表
StartTimer(); // 启动定时器
}
private void InitializeGraph()
{
// 获取图表区域对象
GraphPane myPane = zedGraphControl1.GraphPane;
// 设置图表标题和轴标题
myPane.Title.Text = "ECG Data";
myPane.XAxis.Title.Text = "Time";
myPane.YAxis.Title.Text = "Voltage";
// 初始化数据点列表和曲线数组
_dataLists = new PointPairList[ChannelCount];
_curves = new LineItem[ChannelCount];
_yValues = new double[ChannelCount][];
_timeLabels = new DateTime[_maxPoints];
for (int i = 0; i < ChannelCount; i++)
{
// 为每个导联创建数据点列表和曲线对象,并添加到图表中
_dataLists[i] = new PointPairList();
_curves[i] = myPane.AddCurve("", _dataLists[i], GetColor(i), SymbolType.None);
_yValues[i] = new double[_maxPoints];
}
// 移除图例
myPane.Legend.IsVisible = false;
// 设置X轴和Y轴的范围
myPane.XAxis.Scale.Min = 0;
myPane.XAxis.Scale.Max = _maxPoints;
myPane.YAxis.Scale.Min = -800;
myPane.YAxis.Scale.Max = 800 + VoltageOffset * (ChannelCount - 1);
// 显示网格线
myPane.XAxis.MajorGrid.IsVisible = true;
myPane.YAxis.MajorGrid.IsVisible = true;
// 隐藏Y=0的实线
myPane.YAxis.MajorGrid.IsZeroLine = false;
// 自定义Y轴刻度标注
Scale yScale = myPane.YAxis.Scale;
yScale.MajorStep = VoltageOffset;
yScale.MinorStep = VoltageOffset;
yScale.MajorStepAuto = false;
yScale.MinorStepAuto = false;
myPane.YAxis.ScaleFormatEvent += new Axis.ScaleFormatHandler(FormatYScale);
// 设置X轴为文本类型
myPane.XAxis.Type = AxisType.Text;
// 设置X轴刻度字体大小
myPane.XAxis.Scale.FontSpec.Size = 7; // 可以根据需要调整字体大小
myPane.XAxis.Scale.FontSpec.FontColor = System.Drawing.Color.Black;
// 初始化时间标签
DateTime startTime = DateTime.Now;
for (int i = 0; i < _maxPoints; i++)
{
_timeLabels[i] = startTime;
}
// 应用更改并刷新图表
zedGraphControl1.AxisChange();
}
private string FormatYScale(GraphPane pane, Axis axis, double val, int index)
{
// 自定义Y轴刻度标注
int leadIndex = (int)Math.Round(val / VoltageOffset);
if (leadIndex >= 0 && leadIndex < ChannelCount)
{
return $"Lead {leadIndex + 1}";
}
return "";
}
private System.Drawing.Color GetColor(int index)
{
// 定义一组颜色用于不同导联的曲线
System.Drawing.Color[] colors = {
//System.Drawing.Color.Black,
//System.Drawing.Color.Red,
//System.Drawing.Color.Blue,
//System.Drawing.Color.Green,
//System.Drawing.Color.Purple,
//System.Drawing.Color.Orange,
//System.Drawing.Color.Brown,
//System.Drawing.Color.Magenta
System.Drawing.Color.Black,
System.Drawing.Color.Black,
System.Drawing.Color.Black,
System.Drawing.Color.Black,
System.Drawing.Color.Black,
System.Drawing.Color.Black,
System.Drawing.Color.Black,
System.Drawing.Color.Black
};
// 根据索引返回颜色
return colors[index % colors.Length];
}
private void StartTimer()
{
// 创建并配置定时器
_timer = new System.Timers.Timer(1); // 100毫秒的更新频率
_timer.Elapsed += OnTimedEvent; // 绑定定时器事件
_timer.AutoReset = true; // 自动重置
_timer.Enabled = true; // 启用定时器
}
private void OnTimedEvent(Object source, ElapsedEventArgs e)
{
// 记录当前时间
DateTime currentTime = DateTime.Now;
// 为每个导联生成模拟ECG信号数据并更新曲线
for (int i = 0; i < ChannelCount; i++)
{
double voltage = _random.Next(-200, 200) + i * VoltageOffset; // 生成带偏移量的电压数据
_yValues[i][_currentIndex] = voltage;
if (_dataLists[i].Count < _maxPoints)
{
// 添加新的数据点
_dataLists[i].Add(_currentIndex, voltage);
}
else
{
// 更新现有数据点
_dataLists[i][_currentIndex].Y = voltage;
}
}
// 更新时间标签
_timeLabels[_currentIndex] = currentTime;
// 更新时间和当前索引
_currentTime += _timeIncrement;
_currentIndex = (_currentIndex + 1) % _maxPoints;
// 更新X轴刻度显示
zedGraphControl1.GraphPane.XAxis.Scale.TextLabels = GenerateTimeLabels();
// 使图表无效以触发重绘
zedGraphControl1.Invalidate();
}
private string[] GenerateTimeLabels()
{
string[] labels = new string[_maxPoints];
for (int i = 0; i < _maxPoints; i++)
{
labels[i] = _timeLabels[i].ToString("HH:mm:ss.fff");
}
return labels;
}
private void Form1_Load(object sender, EventArgs e)
{
// 窗体加载事件处理方法(目前为空)
}
}
}
添加了个竖线,扫描分界线
using System;
using System.Windows.Forms;
using ZedGraph;
using System.Timers;
namespace ECGPlot
{
public partial class Form1 : Form
{
// 定义导联数量和每个导联的电压偏移量
private const int ChannelCount = 8;
private const int VoltageOffset = 500; // 每个导联的偏移量
// 用于存储每个导联的数据点列表和曲线对象
private PointPairList[] _dataLists;
private LineItem[] _curves;
// 定时器用于模拟ECG信号数据更新
private System.Timers.Timer _timer;
private int _currentIndex = 0;
private int _maxPoints = 500;
private double[][] _yValues;
private double _timeIncrement = 0.1;
private double _currentTime = 0;
private Random _random = new Random();
private DateTime[] _timeLabels;
private LineObj _scanLine;
public Form1()
{
InitializeComponent();
InitializeGraph(); // 初始化图表
StartTimer(); // 启动定时器
}
private void InitializeGraph()
{
// 获取图表区域对象
GraphPane myPane = zedGraphControl1.GraphPane;
// 设置图表标题和轴标题
myPane.Title.Text = "ECG Data";
myPane.XAxis.Title.Text = "Time (hh:mm:ss.fff)";
myPane.YAxis.Title.Text = "Voltage";
// 初始化数据点列表和曲线数组
_dataLists = new PointPairList[ChannelCount];
_curves = new LineItem[ChannelCount];
_yValues = new double[ChannelCount][];
_timeLabels = new DateTime[_maxPoints];
for (int i = 0; i < ChannelCount; i++)
{
// 为每个导联创建数据点列表和曲线对象,并添加到图表中
_dataLists[i] = new PointPairList();
_curves[i] = myPane.AddCurve("", _dataLists[i], GetColor(i), SymbolType.None);
_yValues[i] = new double[_maxPoints];
}
// 移除图例
myPane.Legend.IsVisible = false;
// 设置X轴和Y轴的范围
myPane.XAxis.Scale.Min = 0;
myPane.XAxis.Scale.Max = _maxPoints;
myPane.YAxis.Scale.Min = -800;
myPane.YAxis.Scale.Max = 800 + VoltageOffset * (ChannelCount - 1);
// 显示网格线
myPane.XAxis.MajorGrid.IsVisible = true;
myPane.YAxis.MajorGrid.IsVisible = true;
// 隐藏Y=0的实线
myPane.YAxis.MajorGrid.IsZeroLine = false;
// 自定义Y轴刻度标注
Scale yScale = myPane.YAxis.Scale;
yScale.MajorStep = VoltageOffset;
yScale.MinorStep = VoltageOffset;
yScale.MajorStepAuto = false;
yScale.MinorStepAuto = false;
myPane.YAxis.ScaleFormatEvent += new Axis.ScaleFormatHandler(FormatYScale);
// 设置X轴为文本类型
myPane.XAxis.Type = AxisType.Text;
// 设置X轴刻度字体大小
myPane.XAxis.Scale.FontSpec.Size = 10; // 可以根据需要调整字体大小
// 初始化时间标签
DateTime startTime = DateTime.Now;
for (int i = 0; i < _maxPoints; i++)
{
_timeLabels[i] = startTime;
}
// 初始化扫描竖线
_scanLine = new LineObj(System.Drawing.Color.Black, 0, -800, 0, 800 + VoltageOffset * (ChannelCount - 1));
_scanLine.Line.Style = System.Drawing.Drawing2D.DashStyle.Dash;
_scanLine.IsClippedToChartRect = true;
myPane.GraphObjList.Add(_scanLine);
// 应用更改并刷新图表
zedGraphControl1.AxisChange();
}
private string FormatYScale(GraphPane pane, Axis axis, double val, int index)
{
// 自定义Y轴刻度标注
int leadIndex = (int)Math.Round(val / VoltageOffset);
if (leadIndex >= 0 && leadIndex < ChannelCount)
{
return $"Lead {leadIndex + 1}";
}
return "";
}
private System.Drawing.Color GetColor(int index)
{
// 定义一组颜色用于不同导联的曲线
System.Drawing.Color[] colors = {
System.Drawing.Color.Gray,
System.Drawing.Color.Gray,
System.Drawing.Color.Gray,
System.Drawing.Color.Gray,
System.Drawing.Color.Gray,
System.Drawing.Color.Gray,
System.Drawing.Color.Gray,
System.Drawing.Color.Gray,
};
// 根据索引返回颜色
return colors[index % colors.Length];
}
private void StartTimer()
{
// 创建并配置定时器
_timer = new System.Timers.Timer(50); // 100毫秒的更新频率
_timer.Elapsed += OnTimedEvent; // 绑定定时器事件
_timer.AutoReset = true; // 自动重置
_timer.Enabled = true; // 启用定时器
}
private void OnTimedEvent(Object source, ElapsedEventArgs e)
{
// 记录当前时间
DateTime currentTime = DateTime.Now;
// 为每个导联生成模拟ECG信号数据并更新曲线
for (int i = 0; i < ChannelCount; i++)
{
double voltage = _random.Next(-200, 200) + i * VoltageOffset; // 生成带偏移量的电压数据
_yValues[i][_currentIndex] = voltage;
if (_dataLists[i].Count < _maxPoints)
{
// 添加新的数据点
_dataLists[i].Add(_currentIndex, voltage);
}
else
{
// 更新现有数据点
_dataLists[i][_currentIndex].Y = voltage;
}
}
// 更新时间标签
_timeLabels[_currentIndex] = currentTime;
// 更新时间和当前索引
_currentTime += _timeIncrement;
_currentIndex = (_currentIndex + 1) % _maxPoints;
// 更新X轴刻度显示
zedGraphControl1.GraphPane.XAxis.Scale.TextLabels = GenerateTimeLabels();
// 更新扫描竖线位置
UpdateScanLine();
// 使图表无效以触发重绘
zedGraphControl1.Invalidate();
}
private string[] GenerateTimeLabels()
{
string[] labels = new string[_maxPoints];
for (int i = 0; i < _maxPoints; i++)
{
labels[i] = _timeLabels[i].ToString("HH:mm:ss.fff");
}
return labels;
}
private void UpdateScanLine()
{
// 移除旧的扫描竖线
zedGraphControl1.GraphPane.GraphObjList.Remove(_scanLine);
// 添加新的扫描竖线
_scanLine = new LineObj(System.Drawing.Color.Black, _currentIndex, -800, _currentIndex, 800 + VoltageOffset * (ChannelCount - 1));
_scanLine.Line.Style = System.Drawing.Drawing2D.DashStyle.Dash;
_scanLine.IsClippedToChartRect = true;
zedGraphControl1.GraphPane.GraphObjList.Add(_scanLine);
// 应用更改并刷新图表
zedGraphControl1.AxisChange();
}
private void Form1_Load(object sender, EventArgs e)
{
// 窗体加载事件处理方法(目前为空)
}
}
}
我修改了扫描线左边和右边是不同颜色
using System;
using System.Windows.Forms;
using ZedGraph;
using System.Timers;
namespace ECGPlot
{
public partial class Form1 : Form
{
// 定义导联数量和每个导联的电压偏移量
private const int ChannelCount = 8;
private const int VoltageOffset = 500; // 每个导联的偏移量
// 用于存储每个导联的数据点列表和曲线对象
private PointPairList[] _dataListsLeft;
private PointPairList[] _dataListsRight;
private LineItem[] _curvesLeft;
private LineItem[] _curvesRight;
// 定时器用于模拟ECG信号数据更新
private System.Timers.Timer _timer;
private int _currentIndex = 0;
private int _maxPoints = 500;
private double[][] _yValues;
private double _timeIncrement = 0.1;
private double _currentTime = 0;
private Random _random = new Random();
private DateTime[] _timeLabels;
private LineObj _scanLine;
public Form1()
{
InitializeComponent();
InitializeGraph(); // 初始化图表
StartTimer(); // 启动定时器
}
private void InitializeGraph()
{
// 获取图表区域对象
GraphPane myPane = zedGraphControl1.GraphPane;
// 设置图表标题和轴标题
myPane.Title.Text = "ECG Data";
myPane.XAxis.Title.Text = "Time";
myPane.YAxis.Title.Text = "Voltage";
// 初始化数据点列表和曲线数组
_dataListsLeft = new PointPairList[ChannelCount];
_dataListsRight = new PointPairList[ChannelCount];
_curvesLeft = new LineItem[ChannelCount];
_curvesRight = new LineItem[ChannelCount];
_yValues = new double[ChannelCount][];
_timeLabels = new DateTime[_maxPoints];
for (int i = 0; i < ChannelCount; i++)
{
// 为每个导联创建数据点列表和曲线对象,并添加到图表中
_dataListsLeft[i] = new PointPairList();
_dataListsRight[i] = new PointPairList();
_curvesLeft[i] = myPane.AddCurve("", _dataListsLeft[i], System.Drawing.Color.Black, SymbolType.None);
_curvesRight[i] = myPane.AddCurve("", _dataListsRight[i], System.Drawing.ColorTranslator.FromHtml("#CCCCCC"), SymbolType.None);
_yValues[i] = new double[_maxPoints];
// 初始化右边灰色波形
for (int j = 0; j < _maxPoints; j++)
{
_dataListsRight[i].Add(j, double.NaN); // 初始化为NaN,表示没有数据
}
}
// 移除图例
myPane.Legend.IsVisible = false;
// 设置X轴和Y轴的范围
myPane.XAxis.Scale.Min = 0;
myPane.XAxis.Scale.Max = _maxPoints;
myPane.YAxis.Scale.Min = -800;
myPane.YAxis.Scale.Max = 800 + VoltageOffset * (ChannelCount - 1);
// 显示网格线
myPane.XAxis.MajorGrid.IsVisible = true;
myPane.YAxis.MajorGrid.IsVisible = true;
// 隐藏Y=0的实线
myPane.YAxis.MajorGrid.IsZeroLine = false;
// 自定义Y轴刻度标注
Scale yScale = myPane.YAxis.Scale;
yScale.MajorStep = VoltageOffset;
yScale.MinorStep = VoltageOffset;
yScale.MajorStepAuto = false;
yScale.MinorStepAuto = false;
myPane.YAxis.ScaleFormatEvent += new Axis.ScaleFormatHandler(FormatYScale);
// 设置X轴为文本类型
myPane.XAxis.Type = AxisType.Text;
// 设置X轴刻度字体大小
myPane.XAxis.Scale.FontSpec.Size = 4; // 可以根据需要调整字体大小
// 初始化时间标签
DateTime startTime = DateTime.Now;
for (int i = 0; i < _maxPoints; i++)
{
_timeLabels[i] = startTime;
}
// 初始化扫描竖线
_scanLine = new LineObj(System.Drawing.Color.Black, 0, -800, 0, 800 + VoltageOffset * (ChannelCount - 1));
_scanLine.Line.Style = System.Drawing.Drawing2D.DashStyle.Dash;
_scanLine.IsClippedToChartRect = true;
myPane.GraphObjList.Add(_scanLine);
// 应用更改并刷新图表
zedGraphControl1.AxisChange();
}
private string FormatYScale(GraphPane pane, Axis axis, double val, int index)
{
// 自定义Y轴刻度标注
int leadIndex = (int)Math.Round(val / VoltageOffset);
if (leadIndex >= 0 && leadIndex < ChannelCount)
{
return $"Lead {leadIndex + 1}";
}
return "";
}
private void StartTimer()
{
// 创建并配置定时器
_timer = new System.Timers.Timer(50); // 50毫秒的更新频率
_timer.Elapsed += OnTimedEvent; // 绑定定时器事件
_timer.AutoReset = true; // 自动重置
_timer.Enabled = true; // 启用定时器
}
private void OnTimedEvent(Object source, ElapsedEventArgs e)
{
// 记录当前时间
DateTime currentTime = DateTime.Now;
// 为每个导联生成模拟ECG信号数据并更新曲线
for (int i = 0; i < ChannelCount; i++)
{
double voltage = _random.Next(-200, 200) + i * VoltageOffset; // 生成带偏移量的电压数据
_yValues[i][_currentIndex] = voltage;
if (_dataListsLeft[i].Count < _maxPoints)
{
// 添加新的数据点
_dataListsLeft[i].Add(_currentIndex, voltage);
}
else
{
// 更新现有数据点
_dataListsLeft[i][_currentIndex].Y = voltage;
}
// 更新右边灰色波形数据点
_dataListsRight[i][_currentIndex].Y = voltage;
}
// 更新时间标签
_timeLabels[_currentIndex] = currentTime;
// 更新时间和当前索引
_currentTime += _timeIncrement;
_currentIndex = (_currentIndex + 1) % _maxPoints;
// 更新曲线数据
UpdateCurves();
// 更新X轴刻度显示
zedGraphControl1.GraphPane.XAxis.Scale.TextLabels = GenerateTimeLabels();
// 更新扫描竖线位置
UpdateScanLine();
// 使图表无效以触发重绘
zedGraphControl1.Invalidate();
}
private void UpdateCurves()
{
for (int i = 0; i < ChannelCount; i++)
{
// 更新左边(黑色)部分
PointPairList leftPoints = _curvesLeft[i].Points as PointPairList;
leftPoints.Clear();
for (int j = 0; j < _maxPoints; j++)
{
if (j <= _currentIndex)
{
leftPoints.Add(j, _yValues[i][j]);
}
else
{
leftPoints.Add(j, double.NaN);
}
}
}
}
private string[] GenerateTimeLabels()
{
string[] labels = new string[_maxPoints];
for (int i = 0; i < _maxPoints; i++)
{
labels[i] = _timeLabels[i].ToString("HH:mm:ss.fff");
}
return labels;
}
private void UpdateScanLine()
{
// 移除旧的扫描竖线
zedGraphControl1.GraphPane.GraphObjList.Remove(_scanLine);
// 添加新的扫描竖线
_scanLine = new LineObj(System.Drawing.Color.Black, _currentIndex, -800, _currentIndex, 800 + VoltageOffset * (ChannelCount - 1));
_scanLine.Line.Style = System.Drawing.Drawing2D.DashStyle.Dash;
_scanLine.IsClippedToChartRect = true;
zedGraphControl1.GraphPane.GraphObjList.Add(_scanLine);
// 应用更改并刷新图表
zedGraphControl1.AxisChange();
}
private void Form1_Load(object sender, EventArgs e)
{
// 窗体加载事件处理方法(目前为空)
}
}
}
第一次扫描时间刻度的设置
using System;
using System.Windows.Forms;
using ZedGraph;
using System.Timers;
using System.Drawing;
using System.IO;
namespace ECGPlot
{
public partial class Form1 : Form
{
// 定义导联数量和每个导联的电压偏移量
private const int ChannelCount = 8;
private const int VoltageOffset = 500; // 每个导联的偏移量
// 用于存储每个导联的数据点列表和曲线对象
private PointPairList[] _dataListsLeft;
private PointPairList[] _dataListsRight;
private LineItem[] _curvesLeft;
private LineItem[] _curvesRight;
// 定时器用于模拟ECG信号数据更新
private System.Timers.Timer _timer;
private int _currentIndex = 0;
private int _maxPoints = 1000;
private double[][] _yValues;
private double _timeIncrement = 0.1;
private double _currentTime = 0;
private Random _random = new Random();
private DateTime[] _timeLabels;
private LineObj _scanLine;
private bool _firstScan = true;
public Form1()
{
InitializeComponent();
InitializeGraph(); // 初始化图表
InitializeDataFile(); // 初始化数据文件
StartTimer(); // 启动定时器
}
private void InitializeGraph()
{
// 获取图表区域对象
GraphPane myPane = zedGraphControl1.GraphPane;
// 设置图表标题和轴标题
myPane.Title.Text = "ECG Data";
myPane.XAxis.Title.Text = "Time";
myPane.YAxis.Title.Text = "Voltage";
// 初始化数据点列表和曲线数组
_dataListsLeft = new PointPairList[ChannelCount];
_dataListsRight = new PointPairList[ChannelCount];
_curvesLeft = new LineItem[ChannelCount];
_curvesRight = new LineItem[ChannelCount];
_yValues = new double[ChannelCount][];
_timeLabels = new DateTime[_maxPoints];
for (int i = 0; i < ChannelCount; i++)
{
// 为每个导联创建数据点列表和曲线对象,并添加到图表中
_dataListsLeft[i] = new PointPairList();
_dataListsRight[i] = new PointPairList();
_curvesLeft[i] = myPane.AddCurve("", _dataListsLeft[i], System.Drawing.Color.Black, SymbolType.None);
_curvesRight[i] = myPane.AddCurve("", _dataListsRight[i], Color.FromArgb(100, Color.Black), SymbolType.None);
_yValues[i] = new double[_maxPoints];
// 初始化右边灰色波形
for (int j = 0; j < _maxPoints; j++)
{
_dataListsRight[i].Add(j, double.NaN); // 初始化为NaN,表示没有数据
}
}
// 移除图例
myPane.Legend.IsVisible = false;
// 设置X轴和Y轴的范围
myPane.XAxis.Scale.Min = 0;
myPane.XAxis.Scale.Max = _maxPoints;
myPane.YAxis.Scale.Min = -800;
myPane.YAxis.Scale.Max = 800 + VoltageOffset * (ChannelCount - 1);
// 网格线
myPane.XAxis.MajorGrid.IsVisible = false; // 关闭纵向主要网格线
myPane.XAxis.MinorGrid.IsVisible = false; // 关闭纵向次要网格线
myPane.YAxis.MajorGrid.IsVisible = true;
// 隐藏Y=0的实线
myPane.YAxis.MajorGrid.IsZeroLine = false;
// 自定义Y轴刻度标注
Scale yScale = myPane.YAxis.Scale;
yScale.MajorStep = VoltageOffset;
yScale.MinorStep = VoltageOffset;
yScale.MajorStepAuto = false;
yScale.MinorStepAuto = false;
myPane.YAxis.ScaleFormatEvent += new Axis.ScaleFormatHandler(FormatYScale);
myPane.YAxis.Scale.FontSpec.Size = 8;
myPane.YAxis.Scale.FontSpec.Family = "Times New Roman"; // 设置字体为
// 设置X轴为文本类型
myPane.XAxis.Type = AxisType.Text;
// 设置X轴刻度字体大小
myPane.XAxis.Scale.FontSpec.Size = 5; // 可以根据需要调整字体大小
myPane.XAxis.Scale.FontSpec.Family = "Times New Roman"; // 设置字体为
// 初始化时间标签
DateTime startTime = DateTime.Now;
for (int i = 0; i < _maxPoints; i++)
{
_timeLabels[i] = startTime;
}
// 初始化扫描竖线
_scanLine = new LineObj(System.Drawing.Color.Black, 0, -800, 0, 800 + VoltageOffset * (ChannelCount - 1));
_scanLine.Line.Style = System.Drawing.Drawing2D.DashStyle.Dash;
_scanLine.IsClippedToChartRect = true;
myPane.GraphObjList.Add(_scanLine);
// 应用更改并刷新图表
zedGraphControl1.AxisChange();
}
private string FormatYScale(GraphPane pane, Axis axis, double val, int index)
{
// 自定义Y轴刻度标注
int leadIndex = (int)Math.Round(val / VoltageOffset);
if (leadIndex >= 0 && leadIndex < ChannelCount)
{
return $"Lead {leadIndex + 1}";
}
return "";
}
private void StartTimer()
{
// 创建并配置定时器
_timer = new System.Timers.Timer(50); // 50毫秒的更新频率
_timer.Elapsed += OnTimedEvent; // 绑定定时器事件
_timer.AutoReset = true; // 自动重置
_timer.Enabled = true; // 启用定时器
}
private void OnTimedEvent(Object source, ElapsedEventArgs e)
{
// 记录当前时间
DateTime currentTime = DateTime.Now;
// 为每个导联生成模拟ECG信号数据并更新曲线
for (int i = 0; i < ChannelCount; i++)
{
double voltage = _random.Next(-200, 200) + i * VoltageOffset; // 生成带偏移量的电压数据
_yValues[i][_currentIndex] = voltage;
if (_dataListsLeft[i].Count < _maxPoints)
{
// 添加新的数据点
_dataListsLeft[i].Add(_currentIndex, voltage);
}
else
{
// 更新现有数据点
_dataListsLeft[i][_currentIndex].Y = voltage;
}
// 更新右边灰色波形数据点
_dataListsRight[i][_currentIndex].Y = voltage;
}
// 更新时间标签
_timeLabels[_currentIndex] = currentTime;
// 实时保存数据到文件
SaveCurrentDataToFile(_currentIndex, currentTime);
// 更新时间和当前索引
_currentTime += _timeIncrement;
_currentIndex = (_currentIndex + 1) % _maxPoints;
// 更新曲线数据
UpdateCurves();
// 更新X轴刻度显示
zedGraphControl1.GraphPane.XAxis.Scale.TextLabels = GeneratePartialTimeLabels();
// 更新扫描竖线位置
UpdateScanLine();
// 使图表无效以触发重绘
zedGraphControl1.Invalidate();
}
private void UpdateCurves()
{
for (int i = 0; i < ChannelCount; i++)
{
// 更新左边(黑色)部分
PointPairList leftPoints = _curvesLeft[i].Points as PointPairList;
leftPoints.Clear();
for (int j = 0; j < _maxPoints; j++)
{
if (j <= _currentIndex)
{
leftPoints.Add(j, _yValues[i][j]);
}
else
{
leftPoints.Add(j, double.NaN);
}
}
}
}
private string[] GeneratePartialTimeLabels()
{
string[] labels = new string[_maxPoints];
for (int i = 0; i < _maxPoints; i++)
{
if (i <= _currentIndex || !_firstScan)
{
labels[i] = _timeLabels[i].ToString("HH:mm:ss.fff");
}
else
{
labels[i] = ""; // 未扫描到的位置使用空字符串
}
}
// 仅在第一次扫描后将_firstScan设为false
if (_currentIndex == _maxPoints - 1)
{
_firstScan = false;
}
return labels;
}
private void UpdateScanLine()
{
// 移除旧的扫描竖线
zedGraphControl1.GraphPane.GraphObjList.Remove(_scanLine);
// 添加新的扫描竖线
_scanLine = new LineObj(System.Drawing.Color.Black, _currentIndex, -800, _currentIndex, 800 + VoltageOffset * (ChannelCount - 1));
_scanLine.Line.Style = System.Drawing.Drawing2D.DashStyle.Solid;
_scanLine.IsClippedToChartRect = true;
zedGraphControl1.GraphPane.GraphObjList.Add(_scanLine);
// 应用更改并刷新图表
zedGraphControl1.AxisChange();
}
private void SaveCurrentDataToFile(int index, DateTime time)
{
string filePath = "ECGData.txt"; // 可以修改为需要保存的文件路径
try
{
using (StreamWriter writer = new StreamWriter(filePath, true)) // 以追加模式打开文件
{
// 写入索引
writer.Write($"{index},");
// 写入8个导联的数据
for (int j = 0; j < ChannelCount; j++)
{
writer.Write($"{_yValues[j][index]},");
}
// 写入时间
writer.WriteLine(time.ToString("HH:mm:ss.fff"));
}
}
catch (Exception ex)
{
MessageBox.Show($"Error saving data: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void InitializeDataFile()
{
string filePath = "ECGData.txt"; // 可以修改为需要保存的文件路径
try
{
using (StreamWriter writer = new StreamWriter(filePath, false)) // 以覆盖模式打开文件
{
// 写入表头
writer.WriteLine("Index,Lead1,Lead2,Lead3,Lead4,Lead5,Lead6,Lead7,Lead8,Time");
}
}
catch (Exception ex)
{
MessageBox.Show($"Error initializing data file: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void btnSaveData_Click(object sender, EventArgs e)
{
using (SaveFileDialog saveFileDialog = new SaveFileDialog())
{
saveFileDialog.Filter = "Text files (*.txt)|*.txt";
saveFileDialog.Title = "Save ECG Data";
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
SaveDataToFile(saveFileDialog.FileName);
}
}
}
private void SaveDataToFile(string filePath)
{
try
{
using (StreamWriter writer = new StreamWriter(filePath))
{
// 写入表头
writer.WriteLine("Index,Lead1,Lead2,Lead3,Lead4,Lead5,Lead6,Lead7,Lead8,Time");
for (int i = 0; i < _maxPoints; i++)
{
// 写入索引
writer.Write($"{i},");
// 写入8个导联的数据
for (int j = 0; j < ChannelCount; j++)
{
writer.Write($"{_yValues[j][i]},");
}
// 写入时间
writer.WriteLine(_timeLabels[i].ToString("HH:mm:ss.fff"));
}
}
MessageBox.Show("Data saved successfully.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show($"Error saving data: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void Form1_Load(object sender, EventArgs e)
{
// 窗体加载事件处理方法(目前为空)
}
}
}
提高响应速度
在Windows窗体应用程序中使用System.Timers.Timer
,其回调在不同于UI线程的线程上运行。如果你要更新UI控件(如ZedGraphControl),最好使用System.Windows.Forms.Timer
,因为它在UI线程上执行。下面是你如何改进代码以使用System.Windows.Forms.Timer
来避免线程间的调度问题并提高响应速度。
此外,确保图表在更新时不会频繁刷新,这样可以减少卡顿。
修改后的代码
using System;
using System.Windows.Forms;
using ZedGraph;
using System.Drawing;
using System.IO;
namespace ECGPlot
{
public partial class Form1 : Form
{
// 定义导联数量和每个导联的电压偏移量
private const int ChannelCount = 8;
private const int VoltageOffset = 500; // 每个导联的偏移量
// 用于存储每个导联的数据点列表和曲线对象
private PointPairList[] _dataListsLeft;
private PointPairList[] _dataListsRight;
private LineItem[] _curvesLeft;
private LineItem[] _curvesRight;
// 定时器用于模拟ECG信号数据更新
private System.Windows.Forms.Timer _timer;
private int _currentIndex = 0;
private int _maxPoints = 1101;
private double[][] _yValues;
private double _timeIncrement = 0.1;
private double _currentTime = 0;
private Random _random = new Random();
private DateTime[] _timeLabels;
private LineObj _scanLine;
private bool _firstScan = true;
public Form1()
{
InitializeComponent();
InitializeGraph(); // 初始化图表
InitializeDataFile(); // 初始化数据文件
StartTimer(); // 启动定时器
}
private void InitializeGraph()
{
// 获取图表区域对象
GraphPane myPane = zedGraphControl1.GraphPane;
// 设置图表标题和轴标题
myPane.Title.Text = "ECG Data";
myPane.XAxis.Title.Text = "Time";
myPane.YAxis.Title.Text = "Voltage";
// 初始化数据点列表和曲线数组
_dataListsLeft = new PointPairList[ChannelCount];
_dataListsRight = new PointPairList[ChannelCount];
_curvesLeft = new LineItem[ChannelCount];
_curvesRight = new LineItem[ChannelCount];
_yValues = new double[ChannelCount][];
_timeLabels = new DateTime[_maxPoints];
for (int i = 0; i < ChannelCount; i++)
{
// 为每个导联创建数据点列表和曲线对象,并添加到图表中
_dataListsLeft[i] = new PointPairList();
_dataListsRight[i] = new PointPairList();
_curvesLeft[i] = myPane.AddCurve("", _dataListsLeft[i], System.Drawing.Color.Black, SymbolType.None);
_curvesRight[i] = myPane.AddCurve("", _dataListsRight[i], Color.FromArgb(100, Color.Black), SymbolType.None);
_yValues[i] = new double[_maxPoints];
// 初始化右边灰色波形
for (int j = 0; j < _maxPoints; j++)
{
_dataListsRight[i].Add(j, double.NaN); // 初始化为NaN,表示没有数据
}
}
// 移除图例
myPane.Legend.IsVisible = false;
// 设置X轴和Y轴的范围
myPane.XAxis.Scale.Min = 0;
myPane.XAxis.Scale.Max = _maxPoints;
myPane.YAxis.Scale.Min = -800;
myPane.YAxis.Scale.Max = 800 + VoltageOffset * (ChannelCount - 1);
// 设置X轴主刻度步长,使刻度标签不那么密集
myPane.XAxis.Scale.MajorStep = 100; // 可以根据需要调整这个值
// 网格线
myPane.XAxis.MajorGrid.IsVisible = false; // 关闭纵向主要网格线
myPane.XAxis.MinorGrid.IsVisible = false; // 关闭纵向次要网格线
myPane.YAxis.MajorGrid.IsVisible = true;
// 隐藏Y=0的实线
myPane.YAxis.MajorGrid.IsZeroLine = false;
// 自定义Y轴刻度标注
Scale yScale = myPane.YAxis.Scale;
yScale.MajorStep = VoltageOffset;
yScale.MinorStep = VoltageOffset;
yScale.MajorStepAuto = false;
yScale.MinorStepAuto = false;
myPane.YAxis.ScaleFormatEvent += new Axis.ScaleFormatHandler(FormatYScale);
myPane.YAxis.Scale.FontSpec.Size = 8;
myPane.YAxis.Scale.FontSpec.Family = "Times New Roman"; // 设置字体为
// 设置X轴为文本类型
myPane.XAxis.Type = AxisType.Text;
// 设置X轴刻度字体大小
myPane.XAxis.Scale.FontSpec.Size = 8; // 可以根据需要调整字体大小
myPane.XAxis.Scale.FontSpec.Family = "Times New Roman"; // 设置字体为
// 初始化时间标签
DateTime startTime = DateTime.Now;
for (int i = 0; i < _maxPoints; i++)
{
_timeLabels[i] = startTime;
}
// 初始化扫描竖线
_scanLine = new LineObj(System.Drawing.Color.Black, 0, -800, 0, 800 + VoltageOffset * (ChannelCount - 1));
_scanLine.Line.Style = System.Drawing.Drawing2D.DashStyle.Dash;
_scanLine.IsClippedToChartRect = true;
myPane.GraphObjList.Add(_scanLine);
// 应用更改并刷新图表
zedGraphControl1.AxisChange();
}
private string FormatYScale(GraphPane pane, Axis axis, double val, int index)
{
// 自定义Y轴刻度标注
int leadIndex = (int)Math.Round(val / VoltageOffset);
if (leadIndex >= 0 && leadIndex < ChannelCount)
{
return $"Lead {leadIndex + 1}";
}
return "";
}
private void StartTimer()
{
// 创建并配置定时器
_timer = new System.Windows.Forms.Timer();
_timer.Interval = 50; // 50毫秒的更新频率
_timer.Tick += OnTimedEvent; // 绑定定时器事件
_timer.Start(); // 启用定时器
}
private void OnTimedEvent(Object source, EventArgs e)
{
// 记录当前时间
DateTime currentTime = DateTime.Now;
// 为每个导联生成模拟ECG信号数据并更新曲线
for (int i = 0; i < ChannelCount; i++)
{
double voltage = _random.Next(-200, 200) + i * VoltageOffset; // 生成带偏移量的电压数据
_yValues[i][_currentIndex] = voltage;
if (_dataListsLeft[i].Count < _maxPoints)
{
// 添加新的数据点
_dataListsLeft[i].Add(_currentIndex, voltage);
}
else
{
// 更新现有数据点
_dataListsLeft[i][_currentIndex].Y = voltage;
}
// 更新右边灰色波形数据点
_dataListsRight[i][_currentIndex].Y = voltage;
}
// 更新时间标签
_timeLabels[_currentIndex] = currentTime;
// 实时保存数据到文件
SaveCurrentDataToFile(_currentIndex, currentTime);
// 更新时间和当前索引
_currentTime += _timeIncrement;
_currentIndex = (_currentIndex + 1) % _maxPoints;
// 更新曲线数据
UpdateCurves();
// 更新X轴刻度显示
zedGraphControl1.GraphPane.XAxis.Scale.TextLabels = GeneratePartialTimeLabels();
// 更新扫描竖线位置
UpdateScanLine();
// 使图表无效以触发重绘
zedGraphControl1.Invalidate();
}
private void UpdateCurves()
{
for (int i = 0; i < ChannelCount; i++)
{
// 更新左边(黑色)部分
PointPairList leftPoints = _curvesLeft[i].Points as PointPairList;
leftPoints.Clear();
for (int j = 0; j < _maxPoints; j++)
{
if (j <= _currentIndex)
{
leftPoints.Add(j, _yValues[i][j]);
}
else
{
leftPoints.Add(j, double.NaN);
}
}
}
}
private string[] GeneratePartialTimeLabels()
{
string[] labels = new string[_maxPoints];
for (int i = 0; i < _maxPoints; i++)
{
if (i <= _currentIndex || !_firstScan)
{
labels[i] = _timeLabels[i].ToString("HH:mm
:ss.fff");
}
else
{
labels[i] = ""; // 未扫描到的位置使用空字符串
}
}
// 仅在第一次扫描后将_firstScan设为false
if (_currentIndex == _maxPoints - 1)
{
_firstScan = false;
}
return labels;
}
private void UpdateScanLine()
{
// 移除旧的扫描竖线
zedGraphControl1.GraphPane.GraphObjList.Remove(_scanLine);
// 添加新的扫描竖线
_scanLine = new LineObj(System.Drawing.Color.Black, _currentIndex, -800, _currentIndex, 800 + VoltageOffset * (ChannelCount - 1));
_scanLine.Line.Style = System.Drawing.Drawing2D.DashStyle.Solid;
_scanLine.IsClippedToChartRect = true;
zedGraphControl1.GraphPane.GraphObjList.Add(_scanLine);
// 应用更改并刷新图表
zedGraphControl1.AxisChange();
}
private void SaveCurrentDataToFile(int index, DateTime time)
{
string filePath = "ECGData.txt"; // 可以修改为需要保存的文件路径
try
{
using (StreamWriter writer = new StreamWriter(filePath, true)) // 以追加模式打开文件
{
// 写入索引
writer.Write($"{index},");
// 写入8个导联的数据
for (int j = 0; j < ChannelCount; j++)
{
writer.Write($"{_yValues[j][index]},");
}
// 写入时间
writer.WriteLine(time.ToString("HH:mm:ss.fff"));
}
}
catch (Exception ex)
{
MessageBox.Show($"Error saving data: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void InitializeDataFile()
{
string filePath = "ECGData.txt"; // 可以修改为需要保存的文件路径
try
{
using (StreamWriter writer = new StreamWriter(filePath, false)) // 以覆盖模式打开文件
{
// 写入表头
writer.WriteLine("Index,Lead1,Lead2,Lead3,Lead4,Lead5,Lead6,Lead7,Lead8,Time");
}
}
catch (Exception ex)
{
MessageBox.Show($"Error initializing data file: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void btnSaveData_Click(object sender, EventArgs e)
{
using (SaveFileDialog saveFileDialog = new SaveFileDialog())
{
saveFileDialog.Filter = "Text files (*.txt)|*.txt";
saveFileDialog.Title = "Save ECG Data";
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
SaveDataToFile(saveFileDialog.FileName);
}
}
}
private void SaveDataToFile(string filePath)
{
try
{
using (StreamWriter writer = new StreamWriter(filePath))
{
// 写入表头
writer.WriteLine("Index,Lead1,Lead2,Lead3,Lead4,Lead5,Lead6,Lead7,Lead8,Time");
for (int i = 0; i < _maxPoints; i++)
{
// 写入索引
writer.Write($"{i},");
// 写入8个导联的数据
for (int j = 0; j < ChannelCount; j++)
{
writer.Write($"{_yValues[j][i]},");
}
// 写入时间
writer.WriteLine(_timeLabels[i].ToString("HH:mm:ss.fff"));
}
}
MessageBox.Show("Data saved successfully.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show($"Error saving data: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void Form1_Load(object sender, EventArgs e)
{
// 窗体加载事件处理方法(目前为空)
}
}
}
解释
- 将定时器从
System.Timers.Timer
更改为System.Windows.Forms.Timer
,使其在UI线程上执行更新操作。 System.Windows.Forms.Timer
的回调在UI线程上运行,消除了跨线程更新控件的问题。- 调整
_timer.Interval
来控制刷新频率,这里设置为50毫秒。 - 在
OnTimedEvent
方法中直接更新UI,消除了跨线程调用的开销。
能够改善响应速度和卡顿问题。