C#编写多导联扫描式的波形图Demo

本代码调用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)
        {
            // 窗体加载事件处理方法(目前为空)
        }
    }
}

注释解释:

  1. 全局变量定义:定义导联数量、每个导联的电压偏移量,以及存储数据和曲线的变量。
  2. 构造函数:调用 InitializeGraphStartTimer 方法初始化图表和启动定时器。
  3. InitializeGraph 方法:初始化图表区域,设置标题和轴标题,创建每个导联的曲线对象,并设置轴的范围和网格。
  4. GetColor 方法:定义一组颜色,根据索引返回颜色用于不同导联的曲线。
  5. StartTimer 方法:创建并配置定时器,设置定时器事件处理方法。
  6. OnTimedEvent 方法:在定时器触发时生成模拟ECG信号数据,为每个导联添加或更新数据点,并刷新图表。
  7. 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)
        {
            // 窗体加载事件处理方法(目前为空)
        }
    }
}

解释

  1. 将定时器从System.Timers.Timer更改为System.Windows.Forms.Timer,使其在UI线程上执行更新操作。
  2. System.Windows.Forms.Timer的回调在UI线程上运行,消除了跨线程更新控件的问题。
  3. 调整_timer.Interval来控制刷新频率,这里设置为50毫秒。
  4. OnTimedEvent方法中直接更新UI,消除了跨线程调用的开销。
    能够改善响应速度和卡顿问题。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

好玩的Matlab(NCEPU)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值