做个漂亮的桌面小钟
软件世界
我们有时会见到一些让我们心里觉得痒痒的小程序,比如漂亮的桌面小钟(如(图1)、(图2)所示),是不是自己也想做一个来玩呢?下面,你就可以跟着我来做一个属于自己的桌面小钟。为了方便,我们就直接把图中所示的漂亮小钟界面拿来用了,当然你也可以自己设计一个喜欢的小钟界面。


数字时钟篇
1.前期准备
我们可以在(图1)看到这是大富翁5里面的人物图片。现在我们就要用到屠龙宝刀──eXeScope,这个软件的功能非常强大,可以直接看到应用程序里面的资源文件并可以提取和修改,很多软件的汉化都是用的它。如(图3)所示我们把其中的图片文件全都提取出来,这些图片包括从0到9的数字和代表小时与分间隔的小点(如(图4)、(图5))以及大富翁5中的12幅任务肖像,分别命名为100.bmp到122.bmp。下面我们要做的就是把这些图片做成资源文件,然后让应用程序来调用资源文件。我们先编辑一个名为myres.rc的文本文件,内容如下:



100 bitmap 100.bmp
……(依此类推)
122 bitmap 122.bmp
然后转到DOS窗口中用DELPHI6\BIN目录下的BRCC32.EXE程序将上述文件编译成资源文件myres.res,这样我们的准备工作就做完了,接着编程。
2.基本原理
为了能够快速写出能表现任意形状窗口的程序,我使用了最新的Delphi6.0。在Delphi6.0中窗口属性增加了几个特别有用的项目,如AlphaBlend、AlphaBlendValue和TransparentColor、TransparentColorValue。前两个是让你的窗口有半透明效果,只要把AlphaBlend设为True然后再设定一下透明度AlphaBlendValue其设定值为0~255,当为0时窗口就全透明255则是不透明窗口,当然这项效果只在Win2000以上的操作系统中才能表现出来,Win98下就看不到这个效果了。而在我们这个程序中着重要用到的就是TransparentColor、TransparentColorValue这两个属性,前者是判断窗口是否针对所设定的颜色透明,后者则是设定这种颜色。通过这两个属性,你只要在窗口中加入一个图片然后再把TransparentColorValue设为要透明的地方的颜色就行了。在我们的程序中(如(图3))要把人物肖像周围的部分作全透明处理,这部分的颜色应为clLime,我们只要把TransparentColorValue设为clLime就可以得到如(图1)一样的任意形状的窗口了。
当我们把人物的肖像画到窗口中之后,下一步就是要在该显示时间的地方画上数字。首先我们用函数GetLocalTime得到当前的时间,然后再把得到的时间分解为单个的数字,最后再把资源文件中与数字相对应的图片画到窗口上就可以了。下面就是我所作的小钟的源代码。
3.源代码
在窗口上放一个Timer控件,名称设为Timer,Interval设为1000,即一秒钟。窗口的Name改为Main。
unit Unit1;
interface
uses
Windows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,Dialogs,ExtCtrls,StdCtrls;
type
Tmain = class(TForm)
Timer:TTimer;
procedure FormCreate(Sender:TObject);
procedure FormMouseMove(Sender:TObject; Shift:TShiftState; X,Y:Integer);
procedure TimerTimer(Sender:TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
main:Tmain;
ii:integer =0;//全局变量定义所要显示人物背景的ID
mybusy :boolean = false;//定义判断时钟的小点是否闪动的变量
mystart:boolean=true;//定义是否处于程序启动阶段把12幅背景图全部显示一遍
implementation
{$R *.dfm}
{$R big.res}//包含前面所准备好的资源文件
procedure Tmain.FormCreate(Sender:TObject);
begin
main.Width:=300;
main.Height:=225;
end;
//不用标题框也能移动程序窗口,这项技巧的原理在很多地方都有比较详细的叙述,在这里就不用我多说了
procedure Tmain.FormMouseMove(Sender:TObject; Shift:TShiftState; X,
Y:Integer);
begin
if ssleft in shift then
begin
releasecapture;
main.perform(WM_syscommand,$F012,0);
end;
end;
//下面就是这个程序的核心过程,Timer控件的设定为1000即一秒钟。
procedure Tmain.TimerTimer(Sender:TObject);
var myTime:SYSTEMTIME;//定义系统时间变量
myht:word;//定义传递hour十位数的变量
myhh:word;//定义传递hour个位数的变量
mymt:word;//定义传递minute十位数的变量
mymm:word;//定义传递minute个位数的变量
aBitmap:Tbitmap;
myDest,mySource:Trect;
Begin
//得到系统时间
GetLocalTime(myTime);
aBitmap := TBitmap.Create;
//下面是判断是否程序是刚刚启动如果是则将12个人物的肖像全部显示一遍
If mystart then//以全局变量mystart来判断
begin
ii:=ii+1;
if ii<13 then
begin
aBitmap.LoadFromResourceID(hInstance,110+ii);
main.Canvas.Draw(0,0,abitmap);//将载入的图片直接画到窗口上去,而不使用控件
end
else mystart:=false;
end
else
begin
//如果12个人物的肖像已经全部过了一遍,那么每分钟钟面换一个人物。在这个地方我为了使钟面显示不至于很枯燥就让每分钟显示的肖像在12个人物中随机产生,即使用random函数
if mytime.wSecond=0 then
begin
aBitmap.LoadFromResourceID(hInstance,111+random(12));
main.Canvas.Draw(0,0,abitmap);
end;
end;
//下面是在钟面上画上跳动的小点
if mybusy then
begin
aBitmap.LoadFromResourceID(hInstance,110);
myDest:= Rect(210,162,224,194);
mySource:= Rect(0,0,14,32);
//将代表亮点的图片画到窗口的指定位置
main.Canvas.CopyRect(myDest,abitmap.Canvas,mySource);
mybusy:=false;
end
else
begin
aBitmap.LoadFromResourceID(hInstance,111);
myDest:= Rect(210,162,224,194);
mySource:= Rect(210,162,224,194);
//将完整肖像图片上的灰色小点部分画到窗口上,代表小点暗下去
main.Canvas.CopyRect(myDest,abitmap.Canvas,mySource);
mybusy:=true;
end;
//画出hour的十位数到指定位置
myht:=mytime.wHour div 10;
aBitmap.LoadFromResourceID(hInstance,100+myht);
myDest:= Rect(156,166,176,190);
mySource:= Rect(0,0,20,24);
main.Canvas.CopyRect(myDest,abitmap.Canvas,mySource);
//画出hour的个位数到指定位置
myhh:=mytime.wHour-myht*10;
aBitmap.LoadFromResourceID(hInstance,100+myhh);
myDest:= Rect(186,166,206,190);
mySource:= Rect(0,0,20,24);
main.Canvas.CopyRect(myDest,abitmap.Canvas,mySource);
//画出Minute的十位数到指定位置
mymt:=mytime.wMinute div 10;
aBitmap.LoadFromResourceID(hInstance,100+mymt);
myDest:= Rect(228,166,248,190);
mySource:= Rect(0,0,20,24);
main.Canvas.CopyRect(myDest,abitmap.Canvas,mySource);
//画出Minute的个位数到指定位置
mymm:=mytime.wMinute-mymt*10;
aBitmap.LoadFromResourceID(hInstance,100+mymm);
myDest:= Rect(258,166,276,190);
mySource:= Rect(0,0,20,24);
main.Canvas.CopyRect(myDest,abitmap.Canvas,mySource);
abitmap.Free;
end;
End.
这样,我们就做出了一个非常漂亮的桌面小钟程序。在这里我是用了现成小钟做界面,当然你也可以自己画一个界面。到这里,程序中显示的时间均是以数字表示的,后面我们将要做一个用指针来表示时间的模拟小钟。
模拟时钟篇
1.前期的准备
和已经完成的数字小钟一样,我们仍然用现成的钟面,将(图2)所示小钟程序中的图片挖出来(如(图6))。同样我们将其ID命名为200加到资源文件myres.res中去。

2.基本原理
在这个地方小钟每秒钟里所要显示的5个状态被画在了同一幅图里面。由于实际上小钟的钟摆要再摆回头,也就是要在一秒钟里面显示小钟的8个状态。这样我们就要使用到Canvas的CopyRect方法。这个方法就是将一幅图片中由Source限定的Rect部分画到想要的画布中去并由Dest指定目标Rect。在本程序中SourceRect部分就要每1/8秒计算一次。
在这个模拟的时钟程序里面最重要的当然就是如何画出指针的问题了。这里我使用了一个过程procedure myarrow(Radius,BackRadius,HandWidth:integer; HandColor:TColor;Angle:Real);其中的Radius是指针的长度,BackRadius是秒针的尾部长度,HandWidth是指针的宽度,HandColor是指针的颜色,Angle是指针的角度。在这个地方我以小钟钟盘中点垂直向上为坐标轴,那么每个指针的角度可以表示为(当然这里的角度是以弧度表示的):
时针的角度= 2×π×(h+m/60)/12
分针的角度= 2×π×m/60
秒针的角度= 2×π×s/60
由于窗口是以钟盘中点向右为坐标轴的,正好和我们前面定义的坐标轴相差-90度也就是+270度,表示为弧度即为3π/2,这样在确定指针顶点计算中就要给每个指针的角度加上3π/2。然后再通过三角运算就可以得到顶点坐标了。表达式为:
X=86+半径×cos(角度+3π/2)
Y=86 +半径×sin(角度+3π/2)
得到了小钟指针的算法,我们就可以来编写程序了,其源代码如下:
在窗口上方有一个Timer控件,名称设为Timer,Interval设为125,即1/8秒。窗口的Name改为Main。代码如下:
unit Unit2;
interface
uses
Windows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms, Dialogs,StdCtrls,ExtCtrls;
type
Tmain = class(TForm)
Timer:TTimer;
procedure FormCreate(Sender:TObject);
procedure TimerTimer(Sender:TObject);
procedure myarrow(Radius,BackRadius,HandWidth:integer; HandColor:TColor; Angle:Real);
procedure FormMouseMove(Sender:TObject; Shift:TShiftState; X,Y:Integer);
private
{ Private declarations }
public
{ Public declarations }
end;
var
main:Tmain;
ii:integer =-1;
myforward:boolean =true;//定义判断小钟的钟摆摆动方向的全局变量
implementation
{$R *.dfm}
{$R myres.res}//包含前面所准备好的资源文件
//画出小钟指针的过程
procedure Tmain.myarrow(Radius,BackRadius,HandWidth:integer; HandColor:TColor; Angle:Real);
var
X,Y:integer;
Begin
//设定所画指针的宽度及颜色
with main.Canvas.Pen do
begin
Width := HandWidth;
Color := HandColor;
end;
with main.Canvas do
begin
Angle :=(Angle + 3*PI / 2);//针对坐标轴修正角度
//画出秒针的小尾巴
X := 86- Round(BackRadius * co(Angle)) ;
Y := 86 - Round(BackRadius * sin(Angle));
MoveTo(86,86);
LineTo(X,Y);
//画出各指针
X := 86+ Round(Radius * cos(Angle));
Y := 86 + Round(Radius * sin(Angle));
MoveTo(86,86);
LineTo(X,Y);
end;
end;
procedure Tmain.FormCreate(Sender:TObject);
begin
main.Height:=256;
main.Width:=172;
end;
procedure Tmain.TimerTimer(Sender:TObject);
var aBitmap:Tbitmap;
myDest,mySource:Trect;
Angle:real;
Radius,BackRadius,HandWidth:integer;
HandColor:TColor;
myTime:SYSTEMTIME;
h:word;
m:word;
s:word;
begin
//针对钟摆的方向,画出图6中所显示的合适的部分
if myforward:then ii:=ii+1;
else ii:=ii-1;
if ii>3 then
myforward:=false;
if ii<1 then
myforward:=true;
aBitmap:= TBitmap.Create;
aBitmap.LoadFromResourceID(hInstance,206);//载入小钟背景图案
myDest:= rect(0,0,172,256);
mySource :=rect(ii*172,0,ii*172+172,256);//计算所应该画的部分的位置,时间间隔是1/8秒
main.canvas.copyrect(myDest,abitmap.Canvas,mySource);
aBitmap.free;
//得到系统时间
GetLocalTime(myTime);
h:=mytime.wHour;
m:=mytime.wMinute;
s:=mytime.wsecond;
//定义时针、分针及秒针的半径,尾部半径,宽度、颜色及计算其角度
radius:= 20;
backradius:=0;
handwidth:=5;
handcolor:=clblue;
Angle := 2 * PI * (h + m / 60) / 12;
myarrow(Radius,BackRadius,Handwidth,HandColor,Angle);
radius:= 28;
backradius:=0;
handwidth:=4;
handcolor:=clblue;
Angle := 2 * PI * m / 60;
myarrow(Radius,BackRadius,HandWidth,HandColor,Angle);
radius:= 30;
backradius:=10;
handwidth:=2;
handcolor:=clred;
Angle := 2 * PI * s / 60;
myarrow(Radius,BackRadius,HandWidth,HandColor,Angle);
//画出秒针在钟盘中心点周围的小圈
main.Canvas.Ellipse(83,83,89,89);
end;
//方便地使用鼠标移动小钟
procedure Tmain.FormMouseMove(Sender:TObject;Shift:TShiftState;X,
Y:Integer);
begin
if ssleft in shift then
begin
releasecapture;
main.perform(WM_syscommand,$F012,0);
end;
end;
end.
到这里,我们漂亮的小钟程序就全部写好了,你只要潇洒地按下F9就可以见到你的杰作了。怎么样?还不错吧!当然你还可以给小钟加上报时、设定时间甚至是更换皮肤的功能,去努力做吧!
注:本程序在Delphi6.0、Windows 2000下测试通过,文中的源代码图片均可在http://bigmasoft.home.chinaren.com下载。