ToB企服应用市场:ToB评测及商务社交产业平台

标题: WPF开发随笔收录-心电图曲线绘制 [打印本页]

作者: 泉缘泉    时间: 2022-8-11 19:07
标题: WPF开发随笔收录-心电图曲线绘制
一、前言

项目中之前涉及到胎儿心率图曲线的绘制,最近项目中还需要添加心电曲线和血样曲线的绘制功能。今天就来分享一下心电曲线的绘制方式;
二、正文

1、胎儿心率曲线的绘制是通过DrawingVisual来实现的,这里的心电曲线我也是采用差不多相同的方式来实现的,只是两者曲线的数据有所区别。心电图的数据服务器端每秒发送至客户端一个数据包,一个数据包钟心电的数据大概一百个左右,看过心电图的应该知道,心电图的效果是匀速绘制出来的,而不是一次性将一百个点绘制出来;项目中是通过将数据存到数据缓冲区,然后通过线程定时推送数据到绘图端,线程里会根据缓冲区现有数据量来动态控制数据的快慢;这里的例子我就直接通过定时推数据来直接演示如何实现;
2、新建个项目,添加一个类继承FrameworkElement,然后加上对应的数据接收和绘制功能,这里直接贴出所有代码,具体细节之前写绘制高性能曲线时写过了,不清楚的可以参考之前的;(实际上绘图部分用Canvas实现也可以,用DrawingVisual其实每次推送了一个数据,整个视图都重新绘制了,我之所以用这个是因为我要支持自动缩放功能)
  1. public class EcgDrawingVisual : FrameworkElement
  2. {
  3.     private readonly List<Visual> visuals = new List<Visual>();
  4.     private DrawingVisual Layer;
  5.     private Pen ecg_pen = new Pen(Brushes.Orange, 1.5);
  6.     private int?[] ecg_points = new int?[2000];
  7.     private int currentStart = 0;
  8.     private double y_offset = 0;
  9.     private int ecg_max = 60;
  10.     private int ecg_min = -25;
  11.     public EcgDrawingVisual()
  12.     {
  13.         ecg_pen.Freeze();
  14.         Layer = new DrawingVisual();
  15.         visuals.Add(Layer);
  16.     }
  17.     public void SetupData(int ecg)
  18.     {
  19.         ecg_points[currentStart] = ecg;
  20.         for (int i = 1; i <= 20; i++)
  21.         {
  22.             ecg_points[currentStart + i] = null;
  23.         }
  24.         currentStart++;
  25.         if (currentStart >= RenderSize.Width / 2)
  26.         {
  27.             currentStart = 0;
  28.         }
  29.         DrawEcgLine();
  30.         InvalidateVisual();
  31.     }
  32.     private void DrawEcgLine()
  33.     {
  34.         var scale = RenderSize.Height / (ecg_max - ecg_min);
  35.         y_offset = ecg_min * -scale;
  36.         DrawingContext dc = Layer.RenderOpen();
  37.         Matrix mat = new Matrix();
  38.         mat.ScaleAt(1, -1, 0, RenderSize.Height / 2);
  39.         dc.PushTransform(new MatrixTransform(mat));
  40.         for (int i = 0, left = 0; left < RenderSize.Width; i++, left += 2)
  41.         {
  42.             if (ecg_points[i] == null || ecg_points[i + 1] == null) continue;
  43.             dc.DrawLine(ecg_pen, new Point(left, ecg_points[i].Value * scale + y_offset), new Point(left + 2, ecg_points[i + 1].Value * scale + y_offset));
  44.         }
  45.         dc.Pop();
  46.         dc.Close();
  47.     }
  48.     protected override int VisualChildrenCount => visuals.Count;
  49.     protected override Visual GetVisualChild(int index)
  50.     {
  51.         return visuals[index];
  52.     }
  53.     protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
  54.     {
  55.         base.OnRenderSizeChanged(sizeInfo);
  56.     }
  57.     protected override void OnRender(DrawingContext drawingContext)
  58.     {
  59.         drawingContext.DrawRectangle(Brushes.White, null, new Rect(0, 0, RenderSize.Width, RenderSize.Height));
  60.         base.OnRender(drawingContext);
  61.     }
  62. }
复制代码
3、主界面添加这个控件,然后后台添加对应的推送数据的线程,这里我是定时每隔十毫秒推送一个数据给到绘图端。
  1. public partial class MainWindow : Window
  2. {
  3.     private List<int> points = new List<int>() { 4, 4, 3, -1, -2, -2, -2, -2, -2, -2, -2, -2, -4, -3, 25, 37, 8, -7, -5, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -1, -1, 3, 5, 8, 9, 9, 10, 9, 7, 5, 1, -1, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -1, 1, 3 };
  4.     private bool flag = true;
  5.     private int currentIndex = 0;
  6.     public MainWindow()
  7.     {
  8.         InitializeComponent();
  9.         new Thread(() =>
  10.         {
  11.             while (flag)
  12.             {
  13.                 Thread.Sleep(10);
  14.                 this.Dispatcher.BeginInvoke(new Action(() =>
  15.                 {
  16.                     if (currentIndex == points.Count) currentIndex = 0;
  17.                     ecgDrawingVisual.SetupData(points[currentIndex]);
  18.                     currentIndex++;
  19.                 }));
  20.             }
  21.         }).Start();
  22.     }
  23.     protected override void OnClosed(EventArgs e)
  24.     {
  25.         base.OnClosed(e);
  26.         flag = false;
  27.     }
  28. }
复制代码
4、最终实现效果

 
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4