2019独角兽企业重金招聘Python工程师标准>>>

这是直接在Winform的基础上进行绘制的。接下来,我对时钟进行了封装,封装成一个名为CSharpQuartz的类,效果如下:

这是把时钟封装后,实现的一种效果,CSharpQuartz内部开辟了一个线程,与系统时间,保持同步,每秒刷新一次。所采用的技术也就是GDI和多线程及事件委托。把时钟封装成对象后,还为其添加了OnChanged事件,用于对象提供外部

处理之用。接下来就简单的说下,做次小程序的一些准备工作吧。

这也是最近偶尔听到有朋友问怎样做时钟的事,想来,其实也简单的,只是需要一些耐心和细心,这里主要还利用一些三角函数进行计算。上图看似很简单,其实也有很多小细节需要注意。我就把大致绘制的过程简单说下:

首先,我们需要定义一个圆,来作为时钟的轮廓,这里是通过设置时钟的直径及winform的宽高,来计算出时钟在窗体居中的位置。绘制圆的代码就更简单了

float w = 300f, h = 300f;
float x = (this.Width - w) / 2;
float y = (this.Height - h) / 2;float d = w;//直径
float r = d / 2;//半径graphics.DrawEllipse(pen, new RectangleF(x, y, w, h));//绘制圆

接下来,我们需要计算圆的一周遍布的12个时间点。然后把这些时间点和圆心连在一起,就形成了上图我们看到的不同时间点的线段。圆心的查找非常简单,圆心的坐标点,其实就是x轴+半径r,y轴+半径r:


PointF pointEclipse = new PointF(x + r, y + r);

开始分表绘制12个点与圆心的连线,我这里是以9点为起点,此时,脑海中呈现这样的画面

时针一圈12格,每格也就是 Math.PI/6

比如我们计算10点在圆上的坐标P10.

10点所在的点,与x轴的夹角呈30度。那么10点在x上的坐标应该是,x1=x+r-r*Cos(30),以此类推,不难求出12个点的位置,具体代码如下:

/// <summary>
/// <![CDATA[画时刻线 这里是以9点这个时间坐标为起点 进行360度]]>
/// </summary>
/// <param name="graphics"><![CDATA[画布]]></param>
/// <param name="x"><![CDATA[圆x坐标]]></param>
/// <param name="y"><![CDATA[圆y坐标]]></param>
/// <param name="r"><![CDATA[圆x坐标]]></param>
private void DrawQuartzLine(Graphics graphics, float x, float y, float r)
{//圆心PointF pointEclipse = new PointF(x + r, y + r);float labelX, labelY;//文本坐标float angle = Convert.ToSingle(Math.PI / 6);//角度 30度Font font = new Font(FontFamily.GenericSerif, 12);float _x, _y;//圆上的坐标点using (Brush brush = new System.Drawing.SolidBrush(Color.Red)){using (Pen pen = new Pen(Color.Black, 0.6f)){//一天12H,将圆分为12份 for (int i = 0; i <= 11; i++){PointF p10;//圆周上的点float pAngle = angle * i;float x1, y1;//三、四象限if (pAngle > Math.PI){if ((pAngle - Math.PI) > Math.PI / 2)//钝角大于90度 {//第三象限x1 = Convert.ToSingle(r * Math.Cos(Math.PI * 2 - pAngle));y1 = Convert.ToSingle(r * Math.Sin(Math.PI * 2 - pAngle));_x = x + r - x1;_y = y + r + y1;labelX = _x - 8;labelY = _y;}else{//第四象限x1 = Convert.ToSingle(r * Math.Cos(pAngle - Math.PI));y1 = Convert.ToSingle(r * Math.Sin(pAngle - Math.PI));_x = x + r + x1;_y = y + r + y1;labelX = _x;labelY = _y;}}//一、二象限else if (pAngle > Math.PI / 2)//钝角大于90度{//第一象限x1 = Convert.ToSingle(r * Math.Cos(Math.PI - pAngle));y1 = Convert.ToSingle(r * Math.Sin(Math.PI - pAngle));_x = x + r + x1;_y = y + r - y1;labelX = _x;labelY = _y - 20;}else{//第二象限x1 = Convert.ToSingle(r * Math.Cos(pAngle));y1 = Convert.ToSingle(r * Math.Sin(pAngle));_x = x + r - x1;_y = y + r - y1;labelX = _x - 15;labelY = _y - 20;}//上半圆 分成12份,每份 30度if (i + 9 > 12){graphics.DrawString((i + 9 - 12).ToString(), font, brush, labelX, labelY);}else{if (i + 9 == 9){labelX = x - 13;labelY = y + r - 6;}graphics.DrawString((i + 9).ToString(), font, brush, labelX, labelY);}p10 = new PointF(_x, _y);graphics.DrawLine(pen, pointEclipse, p10);}}}
}

为了辅助计算,我又添加了x轴与y轴的线,就是我们在效果图中看到的垂直于水平两条线段。

/// <summary>
/// <![CDATA[绘制象限]]>
/// </summary>
/// <param name="graphics"><![CDATA[画布]]></param>
/// <param name="x"><![CDATA[圆x坐标]]></param>
/// <param name="y"><![CDATA[圆y坐标]]></param>
/// <param name="r"><![CDATA[圆半径]]></param>
private void DrawQuadrant(Graphics graphics, float x, float y, float r)
{float w = r * 2;float extend = 100f;using (Pen pen = new Pen(Color.Black, 1)){#region  绘制象限PointF point1 = new PointF(x - extend, y + r);//PointF point2 = new PointF(x + w + extend, y + r);PointF point3 = new PointF(x + r, y - extend);//PointF point4 = new PointF(x + r, y + w + extend);graphics.DrawLine(pen, point1, point2);graphics.DrawLine(pen, point3, point4);#endregion 绘制象限}}

接下来,该绘制指针(时、分、秒),就是我们效果图中看到的,红绿蓝,三条长短不一的线段,秒针最长,这是和本地系统时间同步,所以要根据当前时间,计算出指针所在的位置。

/// <summary>
/// <![CDATA[绘制时、分、秒针]]>
/// </summary>
/// <param name="graphics"><![CDATA[画布]]></param>
/// <param name="x"><![CDATA[圆x坐标]]></param>
/// <param name="y"><![CDATA[圆y坐标]]></param>
/// <param name="r"><![CDATA[圆半径]]></param>
private void DrawQuartzShot(Graphics graphics, float x, float y, float r)
{if (this.IsHandleCreated){this.Invoke(new Action(() =>{//当前时间DateTime dtNow = DateTime.Now;int h = dtNow.Hour;int m = dtNow.Minute;int s = dtNow.Second;float ha = Convert.ToSingle(Math.PI * 2 / 12);//每小时所弧度 360/12格=30float hm = Convert.ToSingle(Math.PI * 2 / 60);float hs = Convert.ToSingle(Math.PI * 2 / 60);float x1, y1, offset = 60f;using (Pen pen = new Pen(Color.Green, 4)){//时针h = h >= 12 ? h - 12 : h;double angle = h * ha;//当前时针所占弧度x1 = x + r + Convert.ToSingle(Math.Sin(angle) * (r - offset));//通过调整offset的大小,可以控制时针的长短y1 = y + r - Convert.ToSingle(Math.Cos(angle) * (r - offset));//圆心PointF pointEclipse = new PointF(x + r, y + r);PointF pointEnd = new PointF(x1, y1);graphics.DrawLine(pen, pointEclipse, pointEnd);//画45度角//分针using (Pen penYellow = new Pen(Color.Red, 2)){offset = 30;//分double angelMinutes = hm * m;//每分钟弧度x1 = x + r + Convert.ToSingle(Math.Sin(angelMinutes) * (r - offset));//通过调整offset的大小,可以控制时针的长短y1 = y + r - Convert.ToSingle(Math.Cos(angelMinutes) * (r - offset));graphics.DrawLine(penYellow, pointEclipse, new PointF(x1, y1));//画45度角}//秒针using (Pen penYellow = new Pen(Color.Blue, 2)){offset = 20;//分double angelSeconds = hs * s;//每秒钟弧度x1 = x + r + Convert.ToSingle(Math.Sin(angelSeconds) * (r - offset));//通过调整offset的大小,可以控制时针的长短y1 = y + r - Convert.ToSingle(Math.Cos(angelSeconds) * (r - offset));graphics.DrawLine(penYellow, pointEclipse, new PointF(x1, y1));//画45度角}}this.lblTime.Text = string.Format("当前时间:{0}:{1}:{2}", h, m, s);}));}
}

最后,开辟一个线程,来同步更新时针的状态。

/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Quartz_Load(object sender, EventArgs e)
{timer = new Thread(() =>{if (_graphics == null){_graphics = this.CreateGraphics();_graphics.SmoothingMode = SmoothingMode.HighQuality; //高质量_graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; //高像素偏移质量}while (true){_graphics.Clear(this.BackColor);DrawCaller(_graphics);System.Threading.Thread.Sleep(1000);}});timer.IsBackground = true;
}

每秒钟,更新一次,其实就是重绘。

完成了以上几个步骤,我们就完成GDI绘制时钟的工作,后来,把它封装成一个名为CSharpQuartz的对象,具体代码已经托管到码云上,点击此处

这就是我们开篇第一张效果图,带有Quartz字样的,至此,关于GDI绘制时钟与系统时间同步的小程序就这样完成。时间仓促,某些计算方法买来得及仔细推敲,不足之处,大家多提意见。

转载于:https://my.oschina.net/lichaoqiang/blog/1612040

GDI绘制时钟效果,与系统时间保持同步,基于Winform相关推荐

  1. html5canvas绘制时钟,JavaScript html5 canvas绘制时钟效果

    本文实例讲述了JavaScript+html5 canvas绘制时钟效果.分享给大家供大家参考,具体如下: HTML部分: canvas绘图 JavaScript部分: function init() ...

  2. 在MFC中,利用GDI绘制橡皮筋效果-直线,圆,椭圆,矩形

    这段时间学习了GDI和GDI+:如果想实现橡皮筋效果,还是离不开GDI.虽然GDI+也能实现,但比较麻烦,有局限性,必须用到双缓冲. 下面贴出GDI绘制橡皮筋效果的示例代码 ZKCADView.h: ...

  3. 苹果当前系统时间与服务器不一致,苹果iOS14时钟Bug与系统时间不同 比系统时间慢了3小时...

    苹果iOS14时钟Bug与系统时间不同 苹果刚发布了iOS14正式版,新增了类似于安卓负一屏的功能,可以添加时钟组件.近日,网友发现iOS14时钟出现Bug,时钟插件显示时间比系统时间慢了整整三个小时 ...

  4. 如何解决Ubuntu与Windows双系统时间不同步

    导读 不知道有没朋友跟我一样是 Ubuntu 和 Windows 双系统?今天有朋友问到我,当他从 Ubuntu 系统重新启动到 Windows 时,会发现 Windows 中的时间变了,他问我有没办 ...

  5. linux系统取消时间同步,Linux系统时间不同步问题

    问题 : data命令查看系统时间与实际实际一致,但日志中的实际却与实际时间差了整整12个小时,可能原因是什么?如何处理.原因和解决办法? //查看时间 [root@localhost ~]# dat ...

  6. Win8系统如何设置时间自动同步方法 电脑系统时间不能同步怎么设置

    Win8系统如何设置时间自动同步方法 电脑系统时间不能同步怎么设置 我们在使用电脑的时候,总是会遇到很多的电脑难题.当我们在遇到了需要在win8系统中对时间设置自动同步的时候,我们应该怎么操作呢?今天 ...

  7. [2014.5.22][UBUNTU]Ubuntu与Windows系统时间不同步的问题

    安装Ubuntu+Windows双系统时会遇到Windows和Ubuntu系统时间不同步的问题,这是由于Windows系统默认读取主板bios等硬件系统时间作为OS的当地时间;而MAc,Linux类的 ...

  8. Linux系统时间不同步问题

    问题 : data命令查看系统时间与实际实际一致,但日志中的实际却与实际时间差了整整12个小时,可能原因是什么?如何处理.原因和解决办法? 原因1.长期未登录,造成Linux系统时间 不同步. 处理办 ...

  9. win7与internet时间同步出错_win7系统时间不同步怎么办|win7系统时间同步出错的解决方法...

    2015-03-25 15:51:14 让我们的win7系统同步internet时间能让我们的系统获得最准确的时间,不过有的用户发现在设置internet时间同步的时候总是提示"同步时出错& ...

最新文章

  1. 日志分析工具splunt
  2. SAP MM IV中的Duplicated Invoice Check功能的测试
  3. 石大ACM2587解题报告
  4. 孙庆新:做产品,感觉从何而来
  5. 【Android 插件化】VirtualApp 源码分析 ( 添加应用源码分析 | LaunchpadAdapter 适配器 | 适配器添加元素 | PackageAppData 元素 )
  6. Firefox无法加载12306自家证书
  7. 【Scratch】青少年蓝桥杯_每日一题_7.01_正五边形组成的美丽春花
  8. 实现序列化与反序列化,一定要绕开这些坑!
  9. redis编译安装:make 的新错误--collect2: ld returned 1 exit status
  10. 洛谷P3902 递增
  11. IDL与C#混合编程技术
  12. noip2016参赛感想
  13. pip 批量完全卸载包
  14. 关于降低软件开发过程中沟通成本的思考
  15. 不使用脚手架构建vue项目
  16. Flink 实践教程-进阶(11):SQL 关联:Regular Join
  17. 树莓派学习笔记(通过网线连接)
  18. 深度剖析 Vue3 如何通过虚拟DOM更新页面
  19. 计算机图形学--实时光线追踪
  20. Oracle导出DMP文件的两种方法

热门文章

  1. php curl模拟post提交,php curl模拟post提交数据示例
  2. 5m 云服务器2核4g_阿里云服务器2核4gb
  3. python出租车计费标准_用Python绘制出租车出发点的动态热力图
  4. 中级软件测试笔试题100精讲_数字IC设计职位经典笔试面试100题(71~80)
  5. vue中的组件导航守卫,个人理解
  6. 2021-01-13 Linux下安装lua开发环境 Ubuntu
  7. python求二维数组各行最大值_python+numpy按行求一个二维数组的最大值方法
  8. 一台服务器能承载多少用户_一台入门级服务器能为你的办公应用带来哪些效率?评测告诉你...
  9. python 类组合_python类与对象的组合与继承
  10. 删除链表重复节点 python_java删除链表中重复的节点(保留一个节点)