Prepare


本文将使用一个NuGet公开的组件来实现曲线的显示,包含了多种显示的模式和配置来满足各种不同的应用场景,方便大家进行快速的开发系统。

联系作者及加群方式(激活码在群里发放):Cooperation - HslCommunication 官网

在Visual Studio 中的NuGet管理器中可以下载安装,也可以直接在NuGet控制台输入下面的指令安装:

1

Install-Package HslCommunication

NuGet安装教程  VS使用Nuget教程详解 Visual studio 安装第三方的组件库 - dathlin - 博客园

更强大的历史曲线控件参考:C# 历史曲线控件 基于时间的曲线控件 可交互的高级曲线控件 HslControls曲线控件使用教程 - dathlin - 博客园

Summary


曲线控件属于组件里诸多控件中的一种,为什么单独拿出来写一篇博客呢,就是因为曲线控件相对于其他控件都要复杂很多,并不是几个属性那么简单,下面列举了本曲线控件的特性:

  • 提供便捷的API调用即可显示曲线信息内容,不需要复杂的配置
  • 曲线界面的一些信息可以自由定制,比如坐标轴的颜色,是否显示虚线等等
  • 高度的大小自适应,无论你怎么调整控件的大小,始终都能友好显示
  • 支持数据拉伸填充和像素点填充两种模式,具体区别参照下面的代码
  • 多曲线支持,支持同时显示多个曲线信息,每个曲线可独立的指定颜色,线宽等等。
  • 支持左右两个参考系,就是说一个控件中允许显示2种数据跨度不一致的曲线,每种曲线可以显示多条不同的曲线

其他控件的说明地址:C# 时间控件 竖直进度条 饼图显示 仪表盘 按钮基础控件库 - dathlin - 博客园

组件的完整API说明:HslCommunication组件库使用说明 - dathlin - 博客园

要想使用组件的控件,除了使用NuGet来安装组件外,还需要将组件的dll文件(在你的项目的packages里面可以找到,如果你本来就是引用本地的,就直接拖拽本地的即可)拖拽到工具栏:

拖拽完成后效果如下:

先定义一个方法,获取指定范围的,指定个数的随机数数组,供下方的代码调用

1

2

3

4

5

6

7

8

9

private float[] GetRandomValueByCount( int count, float min, float max )

{

    float[] data = new float[count];

    for (int i = 0; i < data.Length; i++)

    {

        data[i] = (float)random.NextDouble( ) * (max - min) + min;

    }

    return data;

}

1.单曲线使用


把控件拖拽到窗口界面上去后,现在界面如下,你可以随意的拉伸大小,调整到一个虚线看着比较清晰的时刻停止:

我们看到左右纵轴的数据跨度都是0-100,现在我们有个需求,手里有300个0-200的数据需要显示,那么就要先设置左右纵轴的数据跨度

接下来就可以编写显示的代码了,一下数据随机:

1

2

3

4

private void userButton1_Click( object sender, EventArgs e )

{

    userCurve1.SetLeftCurve( "A", GetRandomValueByCount( 300, 0, 200 ), Color.DodgerBlue );

}

显示结果如下:

看吧,相当简单方便,如果你觉得目前的数据太密了,想要宽松一点,希望数据拉伸满整个X轴,没问题,因为目前默认的模式是像素点模式,所以切换为拉伸模式即可。

然后在运行看看效果:

接下来我们要对曲线“A”进行数据更新,我们假设你的data数组的数据已经更新了,有可能只更新了一个数据,有可能全部更新了,在数据更新的时候就不需要在指定颜色了,因为指定了颜色也没有用了。

1

2

3

4

5

6

7

8

private void userButton3_Click( object sender, EventArgs e )

{

    // 假设你的data数组已经更新了

    // 之前已经给A指定过颜色了,以后后续的数据更新不需要重新指定,指定了也无效

    // 如果需要重新设置颜色,或是线宽,需要先RemoveCurve,然后重新创建曲线信息

    userCurve1.SetLeftCurve( "A", GetRandomValueByCount( 300, 100, 200 ) );

}

从上面的数据更新我们发现,只要更新了数据,就不停的调用数据显示,那么我们就可以显示实时数据了,而唯一的麻烦之处在于我们需要维护自己的data数组。所以当前的这种方式只适合静态数据显示

2.举个经典的例子


当我们需要显示一些统计数据的时候,比如说我要显示十二个月的销售金额,那么我们应该怎么写

我们先选择拉伸模式,然后设置拉伸模式下最大的数据量为12;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

private void userButton9_Click( object sender, EventArgs e )

{

    // 模拟的数据

    string[] text = new string[]

    {

        "一月",

        "二月",

        "三月",

        "四月",

        "五月",

        "六月",

        "七月",

        "八月",

        "九月",

        "十月",

        "十一月",

        "十二月"

    };

    userCurve1.SetCurveText( text );

    userCurve1.SetLeftCurve( "A", GetRandomValueByCount( 12, 0, 200 ), Color.Tomato );            // 每个月用户1的销售金额

}

效果图如下:

如果我有两条曲线需要显示,以方便对比的话:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

private void userButton9_Click( object sender, EventArgs e )

{

    // 模拟的数据

    string[] text = new string[]

    {

        "一月",

        "二月",

        "三月",

        "四月",

        "五月",

        "六月",

        "七月",

        "八月",

        "九月",

        "十月",

        "十一月",

        "十二月"

    };

    userCurve1.SetCurveText( text );

    userCurve1.SetLeftCurve( "A", GetRandomValueByCount( 12, 0, 200 ), Color.Tomato );            // 每个月用户1的销售金额

    userCurve1.SetLeftCurve( "B", GetRandomValueByCount( 12, 0, 200 ), Color.DodgerBlue );            // 每个月用户2的销售金额

}

图形效果如下:

即时3条曲线或者是更多的曲线,以此类推。重复设定数据即可,只是每条曲线的关键字需要区分开来。

控件支持移除曲线,主要包含了下面的两个方法,移除单个的曲线,或者是移除所有的曲线。

1

2

userCurve1.RemoveCurve( "A" );  // 移除指定的曲线

userCurve1.RemoveAllCurve( );   // 移除所有的曲线

在上面设置曲线数据的时候发现,是通过调用 SetLeftCurve 方法来设置曲线信息的,这个方法有个left单词,很明显是设置左曲线的,控件里还有设置右曲线的,SetLeftCurve 方法就是设置右曲线,那么这里的左右曲线都是指什么呢?

我们在上面的曲线控件上看到,纵轴的刻度线分左右两边,那么设置左曲线就是以左边的刻度线为标准绘制的曲线,而设置右曲线则以右刻度线为标准,在上图中,左右刻度的信息是一致的,所以无所谓左曲线还是右曲线,但是如果我们设置不一致后,那么我们就可以实现显示2种不同范围的数据信息,例如我们右曲线设置为0-10,再随便显示2条曲线

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

private void userButton9_Click( object sender, EventArgs e )

{

    // 模拟的数据

    string[] text = new string[]

    {

        "一月",

        "二月",

        "三月",

        "四月",

        "五月",

        "六月",

        "七月",

        "八月",

        "九月",

        "十月",

        "十一月",

        "十二月"

    };

    userCurve1.SetCurveText( text );

    userCurve1.SetLeftCurve( "A", GetRandomValueByCount( 12, 0, 200 ), Color.Tomato );            // 每个月用户1的销售金额

    userCurve1.SetLeftCurve( "B", GetRandomValueByCount( 12, 0, 200 ), Color.DodgerBlue );            // 每个月用户2的销售金额

    userCurve1.SetRightCurve( "C", GetRandomValueByCount( 12, 3, 6 ), Color.LimeGreen );

    userCurve1.SetRightCurve( "D", GetRandomValueByCount( 12, 3, 6 ), Color.Orchid );

}

效果图如下:

高级使用举例,动态坐标轴

根据上面的情况,我们看到如果我们获取到的一组数据,范围不确定的,需要来动态调整的,比如我们有一个12个数据的float数组,我们设置左坐标轴为数据的上下限

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

private void userButton10_Click( object sender, EventArgs e )

{

    // 模拟的数据

    string[] text = new string[]

    {

        "一月",

        "二月",

        "三月",

        "四月",

        "五月",

        "六月",

        "七月",

        "八月",

        "九月",

        "十月",

        "十一月",

        "十二月"

    };

    float[] data = GetRandomValueByCount( 12, 40, 150 );

    userCurve1.ValueMaxLeft = (float)Math.Ceiling( data.Max( ) );// 向上取整

    userCurve1.ValueMinLeft = (float)Math.Floor( data.Min( ) );// 向下取整

    userCurve1.SetCurveText( text );

    userCurve1.SetLeftCurve( "A", data, Color.Tomato );            // 每个月用户1的销售金额

}

效果图如下:

可以看到,不停的刷新数据后,左坐标轴的数据一直在更新中。

总结下:如果每次都是强行更新所有的数据,也能达到实时刷新的效果,但是如果是一个数据一个数据的采集显示,将按照下面的实时数据模式使用更加合理

3.实时数据显示使用


当我们需要显示一些实时数据时,也就是说,每隔1秒(随便举个栗子)就有新的数据采集上来,然后追加到曲线中去,曲线进行挪动,通常就是这种情况。

3.1 像素点模式(请确认 IsAbscissaStrech 为False):

我们先讲解默认的模式,所谓像素点模式是指在横轴上,一个像素点显示一个数据,如果你的横轴像素长度为1000,那么你就可以显示1000个数据了,当然在实时显示的情况下,不需要你管那么多,你只需要负责定期往里面塞数据即可。

第一步先进行初始化:先增加指定名字的曲线信息,曲线颜色,曲线宽度等等

1

2

3

4

5

private void userButton4_Click( object sender, EventArgs e )

{

    // 这里传入了数组长度为空的数据,不能传NULL

    userCurve1.SetLeftCurve( "B"new float[] { }, Color.Tomato );

}

我们再写一个按钮,启动定时器,去新增数据,来模拟我们从其他设备读取到的数据信息:

1

2

3

4

5

6

7

8

9

10

private void userButton5_Click( object sender, EventArgs e )

{

    Timer timer = new Timer( );

    timer.Interval = 100;

    timer.Tick += ( sender1, e1 ) =>

    {

        userCurve1.AddCurveData( "B", random.Next( 50, 201 ) );

    };

    timer.Start( );

}

如上面的两个按钮信息,必须先点击按钮4进行曲线初始化,按钮5的点击才有效果。最终你会看到曲线每隔100ms刷新一次,不停的有新的数据递增。

当曲线数量超过当前可显示的点数时,曲线会自动的往左挪动,即时你拉伸的整个控件,它依然可以正常的工作,可显示的数据点数会自动更新,内存中会缓存2048个数据点来支持拉伸的效果转换。

当然,它也支持一次更新多个数据,虽然这种情况很少,只是需要注意的是,一次更新的数据必须少于2048。

1

userCurve1.AddCurveData( "B"new float[] { random.Next( 50, 201 ), random.Next( 50, 201 ), random.Next( 50, 201 ) } );

2.2 拉伸模式(请确认 IsAbscissaStrech 为True):

拉伸模式的意思是无论你的data数组有多少个点,都强行按照最大数据点拉伸完整个横轴界面

先设置 StrechDataCountMax 属性为 300 ,意思是强行显示300个点,最大300个点,仅仅在拉伸模式下有效果

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

private void userButton4_Click( object sender, EventArgs e )

{

    // 这里传入了数组长度为空的数据,不能传NULL

    userCurve1.SetLeftCurve( "B"new float[] { }, Color.Tomato); // 指定上限500个数据,该上限只对拉伸模式有效

}

private void userButton5_Click( object sender, EventArgs e )

{

    Timer timer = new Timer( );

    timer.Interval = 100;

    timer.Tick += ( sender1, e1 ) =>

    {

        userCurve1.AddCurveData( "B", random.Next( 50, 201 ) );

    };

    timer.Start( );

}

我们再看拉伸模式的曲线:

你再拉伸控件试试看?拉伸模式的意思是无论你的控件多少大小,你规定了300个点,它就是300个点,即使你的控件拉伸了,它还是300个点,只是没有那么密罢了

至于一次增加多个数据是和上面的像素点模式是一致的。

2.3 模式区别及选择

像素点模式下,随便看着数据比较密,但是好处在于分辨率高的显示器,可以显示的数据更多。

拉伸模式虽然在控件拉伸的情况下显示的数据量不会增长,但是可以控制疏密程度。

各有优劣,建议先使用像素点模式,看看效果怎么样,一般数据变化都是慢慢来的,所以曲线不会像测试数据那样乱串。如果数据乱串比较厉害,再使用拉伸模式。

3.多曲线,双坐标使用


多曲线和单曲线模式很相似,无非是多几条曲线而已,每条曲线的操作,新增数据都是一模一样的,只是多曲线的模式都是统一的,要么全部是像素点模式,要么全部是拉伸模式,所有的特性和上两节是相似的。

为了说明使用,举个例子,你有多个设备(2个及以上),每个设备都有一个温度信息,现在要进行实时数据的直接对比,当然最好将三条曲线放到一起显示。

我们命名三个曲线为“A”,“B”,“C” 然后假设所有的数据都是100-200之间,数据A是160-180随机,数据B是150-170随机,数据C是155-165随机

此处测试方便,使用了 像素点模式。在 拉伸模式 下代码也是一致的

我们接下来看一种相当复杂的使用场景,假设我们有一台设备,需要监控4条曲线,2条温度,2条压力,温度的范围是0-200,压力的范围为0-5 mpa,那么想要在一个控件里显示,也是可以实现的。先调整左右的坐标范围。

此处仍然使用像素点模式,我们接下来写初始化代码和新增数据的代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

private void userButton4_Click( object sender, EventArgs e )

{

    userCurve1.SetLeftCurve( "A"new float[] { }, Color.Tomato );            // 温度1

    userCurve1.SetLeftCurve( "B"new float[] { }, Color.DodgerBlue );        // 温度2

    userCurve1.SetRightCurve( "C"new float[] { }, Color.LimeGreen );         // 压力1

    userCurve1.SetRightCurve( "D"new float[] { }, Color.Purple );            // 压力2

}

private void userButton5_Click( object sender, EventArgs e )

{

    Timer timer = new Timer( );

    timer.Interval = 100;

    timer.Tick += ( sender1, e1 ) =>

    {

        userCurve1.AddCurveData(

            new string[] { "A""B""C""D" },

            new float[] { random.Next( 160, 181 ), random.Next( 150, 171 ), (float)random.NextDouble( ) * 2.5f + 1, (float)random.NextDouble( ) * 1f } );

    };

    timer.Start( );

}

效果如下图:

4.横坐标文本格式调整


上述的实时曲线在显示的时候,我们看到横坐标的文本是小时加分钟的模式,如果我们改成只显示分钟和秒钟怎么办

这个属性就是DateTime的格式化字符串,理论上你可以获取到任务时间相关的文本信息,按照当前的需求,调整成 mm:ss 即可

5.辅助线添加


我想在实时数据显示中设置一条曲线数据的报警上限的辅助线,用来提醒以及更加至关的查看信息,是否异常,可以调用控件的方法来完成

新增的辅助线是左右两个参考坐标系区分出来了,比如我新增左辅助线,192的一条线,颜色为红色

1

userCurve1.AddLeftAuxiliary( 192, Color.Red);

移除的代码为

1

userCurve1.RemoveAuxiliary( 192 );

辅助线效果如下:

如果你新增的辅助线和原有的虚线重叠时,原有的虚线会自动屏蔽掉。

6.背景颜色调整


我修改下背景为暗黑色,瞬间就有黑科技的效果了。当然,线条的颜色可以调整的更加好一点

4.结束语


感谢阅读。

C# 曲线控件 曲线绘制 实时曲线 多曲线控件 开发相关推荐

  1. 基于mschart控件,绘制工控采集类曲线,可对图像进行缩放,拖动滚动条显示放大后的图像

    基于mschart控件,绘制工控采集类曲线,可对图像进行缩放,拖动滚动条显示放大后的图像,并动态显示曲线上的数值点 在工控.采集.测试类项目中经常需要对采集接收回来的数据进行图像.曲线显示,本人在项目 ...

  2. chart控件做实时曲线显示_组态王实时趋势曲线控件介绍,让你对此不再陌生

    一.组态王实时趋势控件的特点 1. 通过TCPIP获得实时数据,数据服务器可以是任何一台运行组态王的机器,而不需进行组态王网络配置. 2. 最多可以显示20条曲线. 3. 可以设置每条曲线的绘制方式, ...

  3. teechart绘制实时曲线_快速学会CAD绘制传输线路图纸

    一工具 CAD工程设计软件 二方法和步骤 万事开头难,遇到不懂的知识刚开始都有畏难的情绪,只要有决心学习,诚心想学会一项技能,那学会学好它就只是时间问题了.我们常常面临时间紧.需要快速入门甚至熟练地解 ...

  4. 使用keras绘制实时的loss与acc曲线

    废话不多说,直接上代码,代码有注释,不懂得评论问博主即可 # -*- coding: utf-8 -*- import keras from keras.models import Sequentia ...

  5. python画两条曲线_查找在matplotlib中绘制的两条曲线之间的区域(在区域之间填充)...

    我有两条曲线的x和y值列表,它们都有奇怪的形状,而且我没有任何函数.我需要做两件事:(1)绘制它并对曲线之间的区域进行着色,如下图所示:(2)找到曲线之间该着色区域的总面积. 在matplotlib中 ...

  6. R语言使用pROC包绘制ROC曲线并使用smooth函数绘制平滑的ROC曲线(方法包括:binormal、density、fitdistr、logcondens、logcondens.smooth)

    R语言使用pROC包绘制ROC曲线并使用smooth函数绘制平滑的ROC曲线(方法包括:binormal.density.fitdistr.logcondens.logcondens.smooth) ...

  7. R语言使用pROC包在同一图中绘制两条ROC曲线并通过假设检验检验ROC曲线的AUC或者偏AUC的差异(输出p值)

    R语言使用pROC包在同一图中绘制两条ROC曲线并通过假设检验检验ROC曲线的AUC或者偏AUC的差异(输出p值) 目录

  8. 校准曲线(calibration curve)是什么?如何绘制校准曲线(calibration curve)?如何通过过校准曲线进行分析?什么是高估?什么是低估?

    校准曲线(calibration curve)是什么? 如何绘制校准曲线(calibration curve)? 如何通过过校准曲线进行分析? 什么是高估? 什么是低估? 注意:横纵坐标的差异,但是本 ...

  9. ROC曲线是什么?ROC曲线是怎么绘制的?ROC曲线的横纵坐标是什么?如何用Python绘制?AUC又是什么?

    ROC曲线是什么?ROC曲线是怎么绘制的?ROC曲线的横纵坐标是什么?AUC又是什么? metrics.roc_auc_score metrics.roc_curve ROC= Receiver Op ...

  10. python画直方图成绩分析-使用Python绘制直方图和正态分布曲线

    本文主要介绍两个内容: 如何使用记事本生成包含某一数据集的CSV文件: 如何使用Python绘制给定数据集的直方图和正态分布曲线. 1. 使用记事本创建CSV文件 ① 新建一个文本文件,打开后输入数据 ...

最新文章

  1. ae 创建图像等高线 蒙版_Pixelmator ——图像处理软件
  2. QT的QMediaPlaylist类的使用
  3. HDU - 3571 HDU CakeMan(bfs+最短路必经点)
  4. em算法 实例 正态分布_Petuum提出序列生成学习算法通用框架
  5. 《Fluid Engine Development》 学习笔记3-光滑粒子流体动力学
  6. 多处理器系统下的伪共享(false sharing)问题
  7. python编程实例
  8. Ubuntu 安装 Libmodbus
  9. android 年月日倒计时,手机日期倒计时
  10. 无线网检查服务器在那,无线网络服务器地址在哪里找
  11. 三相 AC-DC 变换电路(B 题)-- 2021 年全国大学生电子设计竞赛
  12. 鸿蒙与混沌的区别,混沌的近义词(混沌鸿蒙同义词)
  13. 马哥教育N63期-第三周作业
  14. web service 优缺点
  15. 跨境电商平台有哪些?各国电商平台及品类概览
  16. 全国天气预报信息数据接口 API
  17. “无须”与“无需”最简易区别法
  18. angular中copy和extend用法实例
  19. 程序猿生成二维码的三种方法(在线接口+在线网站+本地程序)
  20. Linux查看所有子文件夹及文件的数量

热门文章

  1. 通俗易懂的UART协议帧格式
  2. weblogic jdk版本升级
  3. 基于微信小程序驾校报名系统(微信小程序毕业设计)
  4. Spring事务(2)使用Spring事务完成转账小例子
  5. 网络工程师中级(知识产权与标准化)
  6. 数据库--分库分表中间件--选型/对比/框架
  7. matlab差值报错,matlab插值介绍
  8. Qt 利用海康摄像头的ISAPI协议进行抓图等操作
  9. vue中防止用户频繁点击按钮
  10. 梯度、散度、旋度、拉普拉斯算子