C# 曲线控件 曲线绘制 实时曲线 多曲线控件 开发
Prepare
本文将使用一个NuGet公开的组件来实现曲线的显示,包含了多种显示的模式和配置来满足各种不同的应用场景,方便大家进行快速的开发系统。
联系作者及加群方式(激活码在群里发放):http://www.hslcommunication.cn/Cooperation
在Visual Studio 中的NuGet管理器中可以下载安装,也可以直接在NuGet控制台输入下面的指令安装:
Install-Package HslCommunication
NuGet安装教程 http://www.cnblogs.com/dathlin/p/7705014.html
更强大的历史曲线控件参考:https://www.cnblogs.com/dathlin/p/10291327.html
Summary
曲线控件属于组件里诸多控件中的一种,为什么单独拿出来写一篇博客呢,就是因为曲线控件相对于其他控件都要复杂很多,并不是几个属性那么简单,下面列举了本曲线控件的特性:
- 提供便捷的API调用即可显示曲线信息内容,不需要复杂的配置
- 曲线界面的一些信息可以自由定制,比如坐标轴的颜色,是否显示虚线等等
- 高度的大小自适应,无论你怎么调整控件的大小,始终都能友好显示
- 支持数据拉伸填充和像素点填充两种模式,具体区别参照下面的代码
- 多曲线支持,支持同时显示多个曲线信息,每个曲线可独立的指定颜色,线宽等等。
- 支持左右两个参考系,就是说一个控件中允许显示2种数据跨度不一致的曲线,每种曲线可以显示多条不同的曲线
其他控件的说明地址:http://www.cnblogs.com/dathlin/p/8150516.html
组件的完整API说明:http://www.cnblogs.com/dathlin/p/7703805.html
要想使用组件的控件,除了使用NuGet来安装组件外,还需要将组件的dll文件(在你的项目的packages里面可以找到,如果你本来就是引用本地的,就直接拖拽本地的即可)拖拽到工具栏:
拖拽完成后效果如下:
先定义一个方法,获取指定范围的,指定个数的随机数数组,供下方的代码调用
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的数据需要显示,那么就要先设置左右纵轴的数据跨度
接下来就可以编写显示的代码了,一下数据随机:
private void userButton1_Click( object sender, EventArgs e )
{
userCurve1.SetLeftCurve( "A", GetRandomValueByCount( 300, 0, 200 ), Color.DodgerBlue );
}
显示结果如下:
看吧,相当简单方便,如果你觉得目前的数据太密了,想要宽松一点,希望数据拉伸满整个X轴,没问题,因为目前默认的模式是像素点模式,所以切换为拉伸模式即可。
然后在运行看看效果:
接下来我们要对曲线“A”进行数据更新,我们假设你的data数组的数据已经更新了,有可能只更新了一个数据,有可能全部更新了,在数据更新的时候就不需要在指定颜色了,因为指定了颜色也没有用了。
private void userButton3_Click( object sender, EventArgs e )
{
// 假设你的data数组已经更新了 // 之前已经给A指定过颜色了,以后后续的数据更新不需要重新指定,指定了也无效
// 如果需要重新设置颜色,或是线宽,需要先RemoveCurve,然后重新创建曲线信息
userCurve1.SetLeftCurve( "A", GetRandomValueByCount( 300, 100, 200 ) );
}
从上面的数据更新我们发现,只要更新了数据,就不停的调用数据显示,那么我们就可以显示实时数据了,而唯一的麻烦之处在于我们需要维护自己的data数组。所以当前的这种方式只适合静态数据显示
2.举个经典的例子
当我们需要显示一些统计数据的时候,比如说我要显示十二个月的销售金额,那么我们应该怎么写
我们先选择拉伸模式,然后设置拉伸模式下最大的数据量为12;
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的销售金额
}
效果图如下:
如果我有两条曲线需要显示,以方便对比的话:
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条曲线或者是更多的曲线,以此类推。重复设定数据即可,只是每条曲线的关键字需要区分开来。
控件支持移除曲线,主要包含了下面的两个方法,移除单个的曲线,或者是移除所有的曲线。
userCurve1.RemoveCurve( "A" ); // 移除指定的曲线
userCurve1.RemoveAllCurve( ); // 移除所有的曲线
在上面设置曲线数据的时候发现,是通过调用 SetLeftCurve 方法来设置曲线信息的,这个方法有个left单词,很明显是设置左曲线的,控件里还有设置右曲线的,SetLeftCurve 方法就是设置右曲线,那么这里的左右曲线都是指什么呢?
我们在上面的曲线控件上看到,纵轴的刻度线分左右两边,那么设置左曲线就是以左边的刻度线为标准绘制的曲线,而设置右曲线则以右刻度线为标准,在上图中,左右刻度的信息是一致的,所以无所谓左曲线还是右曲线,但是如果我们设置不一致后,那么我们就可以实现显示2种不同范围的数据信息,例如我们右曲线设置为0-10,再随便显示2条曲线
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数组,我们设置左坐标轴为数据的上下限
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个数据了,当然在实时显示的情况下,不需要你管那么多,你只需要负责定期往里面塞数据即可。
第一步先进行初始化:先增加指定名字的曲线信息,曲线颜色,曲线宽度等等
private void userButton4_Click( object sender, EventArgs e )
{
// 这里传入了数组长度为空的数据,不能传NULL
userCurve1.SetLeftCurve( "B", new float[] { }, Color.Tomato );
}
我们再写一个按钮,启动定时器,去新增数据,来模拟我们从其他设备读取到的数据信息:
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。
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个点,仅仅在拉伸模式下有效果
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,那么想要在一个控件里显示,也是可以实现的。先调整左右的坐标范围。
此处仍然使用像素点模式,我们接下来写初始化代码和新增数据的代码:
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的一条线,颜色为红色
userCurve1.AddLeftAuxiliary( 192, Color.Red);
移除的代码为
userCurve1.RemoveAuxiliary( 192 );
辅助线效果如下:
如果你新增的辅助线和原有的虚线重叠时,原有的虚线会自动屏蔽掉。
6.背景颜色调整
我修改下背景为暗黑色,瞬间就有黑科技的效果了。当然,线条的颜色可以调整的更加好一点
4.结束语
感谢阅读。
C# 曲线控件 曲线绘制 实时曲线 多曲线控件 开发的更多相关文章
- C# ZedGraph实时多条曲线数据更新实例
C# ZedGraph实时多条曲线数据更新实例 先看展示效果 1.创建曲线实例添加必要的元素 public class LineChannel { public LineChannel(int id, ...
- canvas绘制二次贝塞尔曲线----演示二次贝塞尔四个参数的作用
canvas中绘制二次贝塞尔曲线的方法为ctx.quadraticCurveTo(x1,y1,x2,y2); 四个参数分别为两个控制点的坐标.开始点即当前canvas中目前的点,如果想从指定的点开始, ...
- 【九天教您南方cass 9.1】 07 绘制与标注圆曲线和细部点的方法
同学们大家好,欢迎收看由老王测量上班记出品的cass9.1视频课程 我是本节课主讲老师九天. 我们讲课的教程附件也是共享的,请注意索取测量空间中. [点击索取cass教程]5元立得 (给客服说暗号:“ ...
- 用OpenGL进行曲线、曲面的绘制
实验目的 理解Bezier曲线.曲面绘制的基本原理:理解OpenGL中一维.二维插值求值器的用法. 掌握OpenGL中曲线.曲面绘图的方法,对比不同参数下的绘图效果差异: 代码1:用四个控制点绘制一条 ...
- Caffe---Pycaffe 绘制loss和accuracy曲线
Caffe---Pycaffe 绘制loss和accuracy曲线 <Caffe自带工具包---绘制loss和accuracy曲线>:可以看出使用caffe自带的工具包绘制loss曲线和a ...
- Caffe---自带工具 绘制loss和accuracy曲线
Caffe自带工具包---绘制loss和accuracy曲线 为什么要绘制loss和accuracy曲线?在训练过程中画出accuracy 和loss曲线能够更直观的观察网络训练的状态,以便更好的优化 ...
- 在Image控件中绘制文字
//Canvas 在Image控件中绘制文字 procedure TForm1.Button1Click(Sender: TObject);begin image1.Canvas.Font.Size ...
- 双缓冲绘图和窗口控件的绘制——ATL ActiveX 窗口控件生成向导绘制代码OnDraw的一个错误 .
双缓冲绘图和窗口控件的绘制 ---ATL ActiveX 窗口控件生成向导绘制代码OnDraw的一个错误 cheungmine 我们通常使用ATL COM组件,生成一个带窗口的ActiveX控件,然后 ...
- easyui时间控件用js实时获取选定的时间的取法
easyui时间控件用js实时获取选定的时间的取法var datetime=$("#id").datetimebox("getValue");不能用 $(& ...
随机推荐
- linux 查看日志命令
linux中命令cat.more.less均可用来查看文件内容, 区别:cat是一次性显示整个文件的内容,还可以将多个文件连接起来显示,它常与重定向符号配合使用,适用于文件内容少的情况:more和le ...
- java的泛型与反射机制
什么是泛型? 泛型,即“参数化类型”.顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参) ...
- html5 meta标签的认知储备
在开发移动或者PC端的时候除了'<meta charset="UTF-8">'这个设置编码格式的meta标签,还有一些其他方面的设置 一.<meta name=& ...
- HTTP及RFC解析。
HTTP协议描述的是发送方与接收方的通信协议,通过两方的自觉遵守而存在,当然有不少的浏览器并没有百分百遵守这份协议.HTTP是运行于应用层的协议,基于TCP协议而运作.基本上是客户/服务器对答模式,其 ...
- 509. Fibonacci Number斐波那契数列
网址:https://leetcode.com/problems/fibonacci-number/ 原始的斐波那契数列 运用自底向上的动态规划最佳! 可以定义vector数组,但是占用较多内存空间 ...
- 水题系列一:Circle
问题描述:Circle 小明在玩游戏,他正在玩一个套圈圈的游戏.他手里有 L 种固定半径的圆圈,每一种圆 圈都有其固定的数量.他要把这些圆圈套进 N 个圆形槽中的一个.这些圆形槽都有一个最 小半径和最 ...
- [LeetCode] 23. Merge k Sorted Lists ☆☆☆☆☆
转载:https://leetcode.windliang.cc/leetCode-23-Merge-k-Sorted-Lists.html 描述 Merge k sorted linked list ...
- [LeetCode] 43. Multiply Strings ☆☆☆(字符串相乘)
转载:43. Multiply Strings 题目描述 就是两个数相乘,输出结果,只不过数字很大很大,都是用 String 存储的.也就是传说中的大数相乘. 解法一 我们就模仿我们在纸上做乘法的过程 ...
- pandas dataframe 过滤——apply最灵活!!!
按照某特定string字段长度过滤: import pandas as pd df = pd.read_csv('filex.csv') df['A'] = df['A'].astype('str') ...
- 套接字编程,创建套接字socket
1.套接字地址结构: struct sockaddr { sa_family_t sa_family; char sa_data[14]; }; 其中,成员sa_family表示套接字的协议族类型,对 ...