WinForm学习 --简单的模拟时钟程序

今天学习GDI+,试着想写一个模拟时钟的小程序,原以为很简单实现;但其实还有些复杂,特别是利用三角函数的那部分,让我四处找资料恶补了一下高中数学才算弄清楚,现在就回顾一下这个程序吧.

程序的目的是要模拟出时钟的效果,那首先就是要画出这个时钟的样子。不考虑美观,一个时钟最简单的组成是一个圆形的表盘,三根直线代表的时针、分针和秒针。

<img缺失>

看起来很简单吧,但要怎么样画呢?让我们一步一步来吧:

1.画表盘

Graphics g = this.CreateGraphics(); //创建一个Graphics对象

Pen myPen = new Pen(Color.Blue,1); //创建一个自定义画笔对象

//创建一个正方形rect
int myRect_X = this.ClientRectangle.Right/4;
int myRect_Y = this.ClientRectangle.Bottom/4;
int myRect_Width;
int myRect_Height;
if(this.ClientRectangle.Height< this.ClientRectangle.Width)
{

    myRect_Width = this.ClientRectangle.Height/2;
    myRect_Height = this.ClientRectangle.Height/2;
}
else
{
    myRect_Width = this.ClientRectangle.Width/2;
    myRect_Height = this.ClientRectangle.Width/2;
}

Rectangle rect = new Rectangle(myRect_X,myRect_Y,myRect_Width,myRect_Height);

g.DrawEllipse(myPen,rect); //画出一个内切于矩形rect的圆

Graphics.DrawEllipse方法有几种重载方式,其中较常用的就是Graphics.DrawEllipse(Pen,Rectangle),这个重载方法需要提供一个Rectangle参数,然后根据这个矩形的形状内切出椭圆,若这个矩形是正方形,则切出来的是一个正圆。

这段程序最关键的地方就在于得到这个矩形了,因为它间接决定了表盘的位置与大小,Rectangle的构造函数需要四个参数,矩形左上角的X、Y坐标,宽度与高度。

这里的ClientRectangle所代表的是控件的工作区,它也是一个Rectangle类型的对象,是指控件的边界减去非工作区元素(如滚动条、边框、标题栏和菜单)。相应的Right,Bottom,Width,Height分别代表了右边缘的X坐标,下边缘的Y坐标,宽度与高度。

为了使这个表盘适应窗口的变化,这里将这些值全部设置为相对与ClientRectangle相关属性的值,如下图:

<img缺失>

这样当窗口大小变化时,这个圆的外切矩形也随之改变,假如这个窗口的宽度比高度大太多,或反之,则可能会出现表盘的部分超出工作区域无法显示,为了防止这种情况,加入了条件判断语句。如果宽度大于高度,则圆的直径取高度值;反之则取宽度值。当然高度等于宽度时,圆是在工作区域正中间。

2.画表针

将表盘画好后,就可以开始画表针了,这也是最复杂的一步

//得到中心点坐标
int centerPoint_X = rect.Right - rect.Width/2;
int centerPoint_Y = rect.Bottom - rect.Height/2;
Point centerPoint = new Point(centerPoint_X,centerPoint_Y);

int s_Len = rect.Width*0.45; //秒针长度
int m_Len = rect.Width*0.35//分针长度
int l_Len = rect.Width*0.25 //时针长度

//得到当前时间
int h = DateTime.Now.Hour;
int m = DateTime.Now.Minute;
int s = DateTime.Now.Second;

//得到秒针顶点坐标
int sec_X = (int)(centerPoint.X + Math.Sin(6*Math.PI/180*s)*s_Len);
int sec_Y = (int)(centerPoint.Y - Math.Cos(6*Math.PI/180*s)*s_Len);
Point secPoint = new Point(sec_X,sec_Y);

//得到分针顶点坐标
int min_X = (int)(centerPoint.X + Math.Sin(6*Math.PI/180*m)*m_Len);
int min_Y = (int)(centerPoint.Y - Math.Cos(6*Math.PI/180*m)*m_Len);
Point minPoint = new Point(min_X,min_Y);

//得到时针顶点坐标
int hour_X = (int)(centerPoint.X + Math.Sin(h*30+m/2)*Math.PI/180*h_Len);
int hour_Y = (int)(centerPoint.Y - Math.Cos(h*30+m/2)*Math.PI/180*h_Len);
Point hourPoint = new Point(hour_X,hour_Y);

//以不同的颜色连接
myPen = new Pen(Color.Blue,1);
g.DrawLine(myPen,centerPoint,secPoint); //连接原点和秒针顶点
myPen = new Pen(Color.Green,2);
g.DrawLine(myPen,centerPoint,minPoint); //连接原点和分针顶点
myPen = new Pen(Color.Red,3);
g.DrawLine(myPen,centerPoint,hourPoint); //连接原点和时针顶点
g.Dispose(); //释放Graphics对象使用的所有资源

要画出一条直线,必须要得到这个直线的两个顶点坐标,在这个程序里,三根表针有一个共同的原点(表盘中心点),然后如何得到另外一个点就是我们接下来要做的工作了。

<img缺失>

我们先来回顾一下三角函数的概念,sin值是 对边/斜边 得到,cos值是 邻边/斜边 得到,要求出这个点的坐标就是求出这个三角形的两条边,一条边的长度为X,另一条的长度为Y。现在我们已经知道的是斜边的长度,要求出这两条边,就要先知道弧度值。

我们先来考虑最长的秒针,它每一秒都会移动一个角度,这个角度我们可以计算出来:

360/60=6

得到6度,这不难理解,因为整个圆是360度,一般的时钟在相邻小时之间又有5个刻度,所以一共是12*5=60个刻度,然后用360/60=6得到秒针一秒走6度,在程序里必须要将角度转化成弧度才能进行sin运算,所以还要乘以 PI/180(PI为圆周率)。

有了这个弧度值,我们就可以得到每一秒秒针的顶点坐标了(以在第一区间考虑):

sec_X = centerPoint.X + sin(6*PI/180)*当前秒钟值*s_Len
sce_Y = centerPoint.Y - cos(6*PI/180)*当前秒钟值*s_Len

接下来处理时针,因为我们这只是个很简单的模拟,所以就不需要对细节作过多的处理,就让它每过一分钟就跳到下一个刻度,这样求时针的顶点坐标和求秒针的就是一样的算法:

min_X = centerPoint.X + sin(6*PI/180)*当前分钟值*m_Len
min_Y = centerPoint.Y - cos(6*PI/180)*当前分钟值*m_Len

时针的处理方式要不同,因为时针的取值只有12个数字,而分钟和秒针都有60个数字,如果以之前的方式处理时针,效果就是时针到点的那一瞬间它一下子转30度,当然不会有这样效果的时钟。那么我们该如何处理这个棘手的时针呢?

假如现在是06:45:30,在整6点时它的度数是30*6,整7点是30*7,为了不至于将改变全在最后刻发生,我们加入时针值来参与计算,这样当前时间就是h*30+m/2,m为60时 m/2=30 ,也就是 m/2这个值在0--30之间,当角度变化为30度时,就指到了下一个整点。

这样我们得到了时针的算法:

hour_X = centerPoint.X + sin((h*30+m/2)*PI/180)*h_Len
hour_Y = centerPoint.Y - cos((h*30+m/2)*PI/180)*h_Len

最关键的代码处理了,剩下的工作就简单了,我们在程序里加入一个标签lblTime,来显示当前时间:

string TimeInString = "";
int hour = DateTime.Now.Hour;
int min = DateTime.Now.Minute;
int sec = DateTime.Now.Second;
TimeInString = (hour<10)? "0"+hour.ToString():hour.ToString();
TimeInString += (min<10)? "0"+min.ToString():min.ToString();
TimeInString == (sec<10)? "0"+sec.ToString():sec.ToString();
lblTime.Text = TimeInString; 

程序最后运行效果:

<img缺失>

时间: 2024-10-05 16:35:04

WinForm学习 --简单的模拟时钟程序的相关文章

JAVA学习第六十课 — UDP协议 &amp;基于多线程模拟简单的QQ聊天程序

UDP传输 UDP有发送端和接受端,有两大类,DatagramSocket.DatagramPacket 建立发送端和接收端 建立数据包 调用Socket的接收发送方法 关闭Socket 注意:发送端和接收端是两个独立的运行程序 UDP发送端演示 DatagramPacket(byte[] buf, int length, InetAddress address, int port) 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号. public static voi

java学习的一些预备知识和一些简单的java小程序

1先了解一些基本的dos命令 dir     ·列出当前目录下的文件和文件夹 md      创建文件夹 rd        删除文件夹(文件下没有子文件) del "文件夹名"    可以删除带子文件的文件夹 cd..    使文件夹目录一层一层后退 cd\    回到根目录 haha>1.txt    创建文档,并将haha写入到1.txt文档中 del 1.txt    删除文件(不走回收站) del  *.txt    只删除txt文件 exit    退出命令行 set

【java学习】Servlet简单的表单程序(一)

此文用于java学习,在此小记. 在此小Demo中使用到了Servlet,所以有必要了解一下Servlet的相关知识.(Servlet的相关知识摘抄自http://blog.csdn.net/jiuqiyuliang/article/details/36424981) Servlet是Sun公司提供的一门用于开发动态web网页的技术.Sun公司在API中提供了一个servlet接口,我们如果想使用java程序开发一个动态的web网页,只需要实现servelet接口,并把类部署到web服务器上就可

STM32学习笔记2-系统时钟知识及程序配置

一:基本知识 1.  STM32F103ZE有5个时钟源:HSI.HSE.LSI.LSE.PLL. ①.HSI是快速内部时钟,RC振荡器,频率为8MHz,精度不高.   ②.HSE是快速外部时钟,可接石英/陶瓷谐振器,或者接外部时 钟源,频率范围为4MHz~16MHz. ③.LSI是低速内部时钟,RC振荡器,频率为40kHz,提供低功耗时钟. ④.LSE是低速外部时钟,接频率为32.768kHz的石英晶体. ⑤.PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2.HSE或者HSE/2. 倍频

在VR中模拟用鼠标操作电脑并实现简单画图的小程序

(图没有录好,明天换一下) 一.概述 1.实现的基本操作是: 1)用手柄抓住黄色的方块代表手抓住鼠标. 2)通过移动手柄模拟鼠标移动,电脑屏幕上的光标跟着移动. 3)当光标移动到一个Button上时,Button高亮,离开时Button取消高亮,点击Button触发点击事件. 4)当点击Button之后,打开一个画图程序,可以用光标在颜色选择区选择一种颜色,然后在画图区根据光标的移动轨迹,画出选择颜色的光标移动路径的曲线: 2.脚本 1)ComputerController挂在代表电脑的Canv

好程序员前端学习路线分享模拟JavaScript中面向对象技术

好程序员前端学习路线分享模拟JavaScript中面向对象技术,在C#和Java语言中,面向对象是以类的方式实现的,特别是继承这个特性,类的方式继承表现出了强大的功能,而且也易于学习.JavaScript不是纯的面向对象的语言,而是基于对象的语言,对象的继承是以原型函数的形式继承的,很多初学者刚开始接触的时候不太理解,但是JavaScript这种以原型函数的形式实现面向对象技术,不仅是可行的,而且还为面向对象技术提供了动态继承的功能,本文主要讨论了JavaScript的面向对象技术.?一.原型对

C# Winform学习--- 实现石头剪刀布的游戏

本文使用winform实现简单的石头剪刀布的游戏,主要实现,电脑随机出拳,玩家手动点击出拳:实现简易背景图片3秒切换:简易统计信息. 1.效果图 2.实现代码 新建一个windows窗体程序,用数字1代表石头,用数字2代表剪刀,用数字3代表布,结果取玩家和电脑出拳之差,有三种结果 玩家赢: -1,2 平手: 0 玩家输: 其它值 新建3个类: 1)Computer.cs 电脑随机出拳 using System; using System.Collections.Generic; using Sy

C# Winform学习---MDI窗体的设计,PictureBox控件(图片上一页下一页),Timer控件,MenuStrip控件

一.MDI窗体的设计 1.MDI简介 MDI(Multiple Document Interface)就是所谓的多文档界面,与此对应就有单文档界面 (SDI), 它是微软公司从Windows 2.0下的Microsoft Excel电子表格程序开始引入的,Excel电子表格用户有时需要同时操作多份表格,MDI正好为这种操作多表格提供了很大的方便,于是就产生了MDI程序 2.效果图: 如下图所示,多窗体嵌套,其中一个是父窗体,其条是子窗体. 横向排列下面的窗体: 纵向排列下面的窗体: 关闭全部子窗

[Python学习] 简单网络爬虫抓取博客文章及思想介绍

        前面一直强调Python运用到网络爬虫方面非常有效,这篇文章也是结合学习的Python视频知识及我研究生数据挖掘方向的知识.从而简单介绍下Python是如何爬去网络数据的,文章知识非常简单,但是也分享给大家,就当简单入门吧!同时只分享知识,希望大家不要去做破坏网络的知识或侵犯别人的原创型文章.主要包括: 1.介绍爬取CSDN自己博客文章的简单思想及过程 2.实现Python源码爬取新浪韩寒博客的316篇文章 一.爬虫的简单思想      最近看刘兵的<Web数据挖掘>知道,在研