七段数码管在工业控制中有着很广泛的应用,例如用来显示温度、数量、重量、日期、时间,还可以用来显示比赛的比分等,具有显示醒目、直观的优点。笔者采用VC++6.0开发了七段数码管显示控件,用在支持ActiveX技术的软件系统中(如VB,VC,EXCEL等),取得了很好的效果。本文分为两部分,第一部分介绍如何用VC++开发七段数码管显示控件,第二部分用VB建立一个数字时钟,用例子来说明如何在VB6.0中使用该控件。 |
一、七段数码管显示控件的制作 |
1、原理:首先将数码管分为七段,如下图所示: |
![]() |
根据数码(0,1,2,3,4,5,6,7,8,9)来决定七段中的某一段或某几段进行绘制,例如如果数码为0,则显示0、1、2、3、4、5段;数码为1,则显示1、2段,依次类推。 |
2、运行AppWizard来生成SevenSegNum工程。通过从File菜单选择New,然后在Project选项卡上选定MFC ActiveX ControlWizard .命名工程名为SevenSegNum,接受其他默认设置。 |
3、使用ClassWizard(快捷键Ctrl+W)在CSevenSegNumCtrl类中重载OnDraw函数。在SevenSegNumCtrl.CPP文件中编辑如下所示代码: |
void CSevenSegNumCtrl::OnDraw( CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid) |
{ |
//共七段,每段分为7个点,画7个封闭的多边形,表示7段的每一段 |
POINT p0[7],p1[7],p2[7],p3[7],p4[7],p5[7],p6[7]; |
int iLineWidth;//段的宽度 |
int iUnit=3;//将段的宽度分为3份,决定每段的起始和终止部分的斜线夹角 |
//取控件宽度和高度数值小的数值来决定段宽 |
if(rcBounds.Height() < rcBounds.Width()){ |
iLineWidth=rcBounds.Height()/10; //缺省段宽为控件宽度或高度的1/10 |
} |
else{ |
iLineWidth=rcBounds.Width()/10; |
} |
//第零个段 |
p0[0].x=iLineWidth/iUnit; |
p0[0].y=iLineWidth/iUnit; |
p0[1].x=iLineWidth/iUnit*(iUnit-1); |
p0[1].y=rcBounds.top; |
p0[2].x=rcBounds.right - iLineWidth/iUnit*(iUnit-1); |
p0[2].y=rcBounds.top; |
p0[3].x=rcBounds.right - iLineWidth/iUnit; |
p0[3].y=iLineWidth/iUnit; |
p0[4].x=rcBounds.right - iLineWidth; |
p0[4].y=iLineWidth; |
p0[5].x=iLineWidth; |
p0[5].y=iLineWidth; |
p0[6].x=iLineWidth/iUnit; |
p0[6].y=iLineWidth/iUnit; |
//第一个段 |
p1[0].x=rcBounds.right - iLineWidth/iUnit; |
p1[0].y=iLineWidth/iUnit*2; |
p1[1].x=rcBounds.right ; |
p1[1].y=iLineWidth/iUnit*3 ; |
p1[2].x=rcBounds.right; |
p1[2].y=rcBounds.Height()/2 - iLineWidth/iUnit ; |
p1[3].x=rcBounds.right - iLineWidth/iUnit; |
p1[3].y=rcBounds.Height()/2; |
p1[4].x=rcBounds.right - iLineWidth; |
p1[4].y=rcBounds.Height()/2 - iLineWidth/iUnit*(iUnit-1); |
p1[5].x=rcBounds.right - iLineWidth; |
p1[5].y=iLineWidth/iUnit*(iUnit+1); |
p1[6].x=rcBounds.right - iLineWidth/iUnit; |
p1[6].y=iLineWidth/iUnit*2; |
//第二个段 |
p2[0].x=rcBounds.right - iLineWidth/iUnit; |
p2[0].y=rcBounds.Height()/2; |
p2[1].x=rcBounds.right ; |
p2[1].y=rcBounds.Height()/2 + iLineWidth/iUnit ; |
p2[2].x=rcBounds.right; |
p2[2].y=rcBounds.Height() - iLineWidth/iUnit*3 ; |
p2[3].x=rcBounds.right - iLineWidth/iUnit; |
p2[3].y=rcBounds.Height() - iLineWidth/iUnit*2; |
p2[4].x=rcBounds.right - iLineWidth; |
p2[4].y=rcBounds.Height() - iLineWidth/iUnit*(iUnit+1); |
p2[5].x=rcBounds.right - iLineWidth; |
p2[5].y=rcBounds.Height()/2 + iLineWidth/iUnit*(iUnit-1); |
p2[6].x=rcBounds.right - iLineWidth/iUnit; |
p2[6].y=rcBounds.Height()/2; |
//第三个段 |
p3[0].x=iLineWidth/iUnit; |
p3[0].y=rcBounds.Height()- iLineWidth/iUnit; |
p3[1].x=iLineWidth/iUnit*(iUnit-1); |
p3[1].y=rcBounds.bottom; |
p3[2].x=rcBounds.right - iLineWidth/iUnit*(iUnit-1); |
p3[2].y=rcBounds.bottom; |
p3[3].x=rcBounds.right - iLineWidth/iUnit; |
p3[3].y=rcBounds.bottom -iLineWidth/iUnit; |
p3[4].x=rcBounds.right - iLineWidth; |
p3[4].y=rcBounds.Height() -iLineWidth; |
p3[5].x=iLineWidth; |
p3[5].y=rcBounds.Height()-iLineWidth; |
p3[6].x=iLineWidth/iUnit; |
p3[6].y=rcBounds.Height() - iLineWidth/iUnit; |
//第四个段 |
p4[0].x=iLineWidth/iUnit; |
p4[0].y=rcBounds.Height()/2; |
p4[1].x=rcBounds.left ; |
p4[1].y=rcBounds.Height()/2 + iLineWidth/iUnit ; |
p4[2].x=rcBounds.left; |
p4[2].y=rcBounds.Height() - iLineWidth/iUnit*3 ; |
p4[3].x=iLineWidth/iUnit; |
p4[3].y=rcBounds.Height() - iLineWidth/iUnit*2; |
p4[4].x=iLineWidth; |
p4[4].y=rcBounds.Height() - iLineWidth/iUnit*(iUnit+1); |
p4[5].x=iLineWidth; |
p4[5].y=rcBounds.Height()/2 + iLineWidth/iUnit*(iUnit-1); |
p4[6].x=iLineWidth/iUnit; |
p4[6].y=rcBounds.Height()/2; |
//第五个段 |
p5[0].x=iLineWidth/iUnit; |
p5[0].y=iLineWidth/iUnit*2; |
p5[1].x=rcBounds.left ; |
p5[1].y=iLineWidth/iUnit*3 ; |
p5[2].x=rcBounds.left; |
p5[2].y=rcBounds.Height()/2 - iLineWidth/iUnit ; |
p5[3].x=iLineWidth/iUnit; |
p5[3].y=rcBounds.Height()/2; |
p5[4].x=iLineWidth; |
p5[4].y=rcBounds.Height()/2 - iLineWidth/iUnit*(iUnit-1); |
p5[5].x=iLineWidth; |
p5[5].y=iLineWidth/iUnit*(iUnit+1); |
p5[6].x=iLineWidth/iUnit; |
p5[6].y=iLineWidth/iUnit*2; |
//第六个段 |
p6[0].x=iLineWidth/iUnit*2; |
p6[0].y=rcBounds.Height()/2; |
p6[1].x=iLineWidth/iUnit*(iUnit+1); |
p6[1].y=rcBounds.Height()/2 - iLineWidth/2 ; |
p6[2].x=rcBounds.right - iLineWidth/iUnit*(iUnit+1); |
p6[2].y=rcBounds.Height()/2 - iLineWidth/2 ; |
p6[3].x=rcBounds.right - iLineWidth/iUnit*2; |
p6[3].y=rcBounds.Height()/2; |
p6[4].x=rcBounds.right - iLineWidth/iUnit*(iUnit+1); |
p6[4].y=rcBounds.Height()/2 + iLineWidth/2 ; |
p6[5].x=iLineWidth/iUnit*(iUnit+1); |
p6[5].y=rcBounds.Height()/2 + iLineWidth/2 ; |
p6[6].x=iLineWidth/iUnit*2; |
p6[6].y=rcBounds.Height()/2; |
//产生封闭多边形的内部颜色画刷(分为要显示的段的颜色画刷,不需显示的段的颜色画刷),控件的背景颜色画刷 |
CBrush brRgn,brRect,brBackRgn; |
brRgn.CreateSolidBrush(lForeColor1); |
brRect.CreateSolidBrush(lBackColor); |
brBackRgn.CreateSolidBrush(lForeColor2); |
//填充控件背景 |
pdc->FillRect(rcBounds,&brRect); |
//根据上面的7个数组,产生7个区域,绘制7个段 |
CRgn rgn0,rgn1,rgn2,rgn3,rgn4,rgn5,rgn6; |
rgn0.CreatePolygonRgn(p0,7,ALTERNATE); |
rgn1.CreatePolygonRgn(p1,7,ALTERNATE); |
rgn2.CreatePolygonRgn(p2,7,ALTERNATE); |
rgn3.CreatePolygonRgn(p3,7,ALTERNATE); |
rgn4.CreatePolygonRgn(p4,7,ALTERNATE); |
rgn5.CreatePolygonRgn(p5,7,ALTERNATE); |
rgn6.CreatePolygonRgn(p6,7,ALTERNATE); |
//根据数码的不同,决定显示的段 |
switch(chVal){ |
case 0: |
pdc->FillRgn(&rgn0,&brRgn), pdc->FillRgn(&rgn1,&brRgn); |
pdc->FillRgn(&rgn2,&brRgn), pdc->FillRgn(&rgn3,&brRgn); |
pdc->FillRgn(&rgn4,&brRgn), pdc->FillRgn(&rgn5,&brRgn); |
pdc->FillRgn(&rgn6,&brBackRgn); |
break; |
case 1: |
pdc->FillRgn(&rgn0,&brBackRgn), pdc->FillRgn(&rgn1,&brRgn); |
pdc->FillRgn(&rgn2,&brRgn), pdc->FillRgn(&rgn3,&brBackRgn); |
pdc->FillRgn(&rgn4,&brBackRgn),pdc->FillRgn(&rgn5,&brBackRgn); |
pdc->FillRgn(&rgn6,&brBackRgn); |
break; |
case 2: |
pdc->FillRgn(&rgn0,&brRgn), pdc->FillRgn(&rgn1,&brRgn); |
pdc->FillRgn(&rgn2,&brBackRgn),pdc->FillRgn(&rgn3,&brRgn); |
pdc->FillRgn(&rgn4,&brRgn), pdc->FillRgn(&rgn5,&brBackRgn); |
pdc->FillRgn(&rgn6,&brRgn); |
break; |
case 3: |
pdc->FillRgn(&rgn0,&brRgn), pdc->FillRgn(&rgn1,&brRgn); |
pdc->FillRgn(&rgn2,&brRgn), pdc->FillRgn(&rgn3,&brRgn); |
pdc->FillRgn(&rgn4,&brBackRgn),pdc->FillRgn(&rgn5,&brBackRgn); |
pdc->FillRgn(&rgn6,&brRgn); |
break; |
case 4: |
pdc->FillRgn(&rgn0,&brBackRgn),pdc->FillRgn(&rgn1,&brRgn); |
pdc->FillRgn(&rgn2,&brRgn), pdc->FillRgn(&rgn3,&brBackRgn); |
pdc->FillRgn(&rgn4,&brBackRgn),pdc->FillRgn(&rgn5,&brRgn); |
pdc->FillRgn(&rgn6,&brRgn); |
break; |
case 5: |
pdc->FillRgn(&rgn0,&brRgn),pdc->FillRgn(&rgn1,&brBackRgn); |
pdc->FillRgn(&rgn2,&brRgn), pdc->FillRgn(&rgn3,&brRgn); |
pdc->FillRgn(&rgn4,&brBackRgn),pdc->FillRgn(&rgn5,&brRgn); |
pdc->FillRgn(&rgn6,&brRgn); |
break; |
case 6: |
pdc->FillRgn(&rgn0,&brRgn), pdc->FillRgn(&rgn1,&brBackRgn); |
pdc->FillRgn(&rgn2,&brRgn), pdc->FillRgn(&rgn3,&brRgn); |
pdc->FillRgn(&rgn4,&brRgn),pdc->FillRgn(&rgn5,&brRgn); |
pdc->FillRgn(&rgn6,&brRgn); |
break; |
case 7: |
pdc->FillRgn(&rgn0,&brRgn), pdc->FillRgn(&rgn1,&brRgn); |
pdc->FillRgn(&rgn2,&brRgn), pdc->FillRgn(&rgn3,&brBackRgn); |
pdc->FillRgn(&rgn4,&brBackRgn), pdc->FillRgn(&rgn5,&brBackRgn); |
pdc->FillRgn(&rgn6,&brBackRgn); |
break; |
case 8: |
pdc->FillRgn(&rgn0,&brRgn),pdc->FillRgn(&rgn1,&brRgn); |
pdc->FillRgn(&rgn2,&brRgn), pdc->FillRgn(&rgn3,&brRgn); |
pdc->FillRgn(&rgn4,&brRgn), pdc->FillRgn(&rgn5,&brRgn); |
pdc->FillRgn(&rgn6,&brRgn); |
break; |
case 9: |
pdc->FillRgn(&rgn0,&brRgn), pdc->FillRgn(&rgn1,&brRgn); |
pdc->FillRgn(&rgn2,&brRgn), pdc->FillRgn(&rgn3,&brRgn); |
pdc->FillRgn(&rgn4,&brBackRgn), pdc->FillRgn(&rgn5,&brRgn); |
pdc->FillRgn(&rgn6,&brRgn); |
break; |
default: |
pdc->FillRgn(&rgn0,&brRgn), pdc->FillRgn(&rgn1,&brRgn); |
pdc->FillRgn(&rgn2,&brRgn), pdc->FillRgn(&rgn3,&brRgn); |
pdc->FillRgn(&rgn4,&brRgn), pdc->FillRgn(&rgn5,&brRgn); |
pdc->FillRgn(&rgn6,&brRgn); |
break; |
} |
} |
4、增加四个私有变量到CSevenSegNumCtrl类。在SevenSegNumCtrl.h中增加如下所示的变量。 |
private: |
long lBackColor; |
long lForeColor2; |
long lForeColor1; |
short chVal; |
5、增加函数。用快捷键Ctrl+W 打开ClassWizard,选择Automation选项卡,在Class name编辑框选择CSevenSegNumCtrl类,单击Add Method按钮,弹出Add Method对话框,在External name编辑框内输入SetValue,在Return type编辑框内选择void类型,在Parameter list列表中增加参数chValue,类型为short,按OK按钮返回MFC ClassWizard选项卡,单击Edit code按钮,在void CSevenSegNumCtrl::SetValue(short chValue) 函数中输入如下代码: |
void CSevenSegNumCtrl::SetValue(short chValue) |
{ |
chVal=chValue; |
//刷新控件 |
CRect rc; |
GetClientRect(&rc); |
InvalidateRect(rc,TRUE); |
} |
类似上面步骤,添加如下三个函数。 |
void CSevenSegNumCtrl::SetBackColor(long Backcolor) |
{ |
lBackColor=Backcolor; |
CRect rc; |
GetClientRect(&rc); |
InvalidateRect(rc,TRUE); |
} |
void CSevenSegNumCtrl::SetForeColor1(long ForeColor1) |
{ |
lForeColor1=ForeColor1; |
CRect rc; |
GetClientRect(&rc); |
InvalidateRect(rc,TRUE); |
} |
void CSevenSegNumCtrl::SetForeColor2(long ForeColor2) |
{ |
lForeColor2=ForeColor2; |
CRect rc; |
GetClientRect(&rc); |
InvalidateRect(rc,TRUE); |
} |
6、添加如下代码到CsevenSegNumCtrl类的构造函数中,用以初始化变量,如下所示: |
CSevenSegNumCtrl::CSevenSegNumCtrl() |
{ |
InitializeIIDs(&IID_DSevenSegNum, &IID_DSevenSegNumEvents); |
chVal=48; //初始显示为0 |
lForeColor1=RGB(255,0,0); //数码颜色 |
lForeColor2=RGB(166,154,151); //不显示的段的颜色 |
lBackColor=RGB(192,192,192); //控件背景 |
} |
7、编译该工程,名为SevenSegNum Control的控件已经加入注册表,可以被支持ActiveX技术的软件使用了。 |
到现在为止,七段数码管显示控件开发完毕,该控件可以自由拉伸缩放,可以设置控件的背景色、需要显示的段的颜色、不需要显示的段的颜色等功能。 |
二、七段数码管显示控件应用举例 |
下面用VB6.0(英文版)开发一个数字时钟的例子,来说明该控件的使用方法。 |
1、新建一个Standard Exe工程,通过从Project菜单选择Components(或Ctrl+T),在Components对话框已经注册的控件列表中找到SevenSegNum ActiveX Control Module项,单击该项左边的方框,选择该控件,按确定退出。这时可以看到在左边的Toolbox中七段数码管控件。将该控件放置到窗体上。 |
2、参照步骤1,再加入5个七段数码管控件,调整这6个控件的大小、位置等。 |
3、设置窗体的Caption属性为“数字时钟” |
4、向窗体添加4个Shape控件,这4个控件的Shape属性均为3(3-Circle),BackStyle属性均为1(1-opaque),BackColor属性均为红色(&H000000FF&),BorderColor属性设置为与窗体的背景色相同,调整这4个Shape控件的大小,使之与七段数码管控件以及窗体比较协调。 |
5、在窗体上放置一个Timer控件,Timer控件的Interval属性等于1000(即每秒显示1次) |
6、双击Timer控件,输入如下代码: |
Private Sub Timer1_Timer() |
Dim s As Variant |
s = Time |
'设置控件的背景色 |
SevenSegNum1.SetBackColor RGB(192, 192, 192) |
SevenSegNum2.SetBackColor RGB(192, 192, 192) |
SevenSegNum3.SetBackColor RGB(192, 192, 192) |
SevenSegNum4.SetBackColor RGB(192, 192, 192) |
SevenSegNum5.SetBackColor RGB(192, 192, 192) |
SevenSegNum6.SetBackColor RGB(192, 192, 192) |
'设置控件显示段的颜色 |
SevenSegNum1.SetForeColor1 RGB(255, 0, 0) |
SevenSegNum2.SetForeColor1 RGB(255, 0, 0) |
SevenSegNum3.SetForeColor1 RGB(255, 0, 0) |
SevenSegNum4.SetForeColor1 RGB(255, 0, 0) |
SevenSegNum5.SetForeColor1 RGB(255, 0, 0) |
SevenSegNum6.SetForeColor1 RGB(255, 0, 0) |
'设置控件不显示段的颜色 |
SevenSegNum1.SetForeColor2 RGB(188, 188, 188) |
SevenSegNum2.SetForeColor2 RGB(188, 188, 188) |
SevenSegNum3.SetForeColor2 RGB(188, 188, 188) |
SevenSegNum4.SetForeColor2 RGB(188, 188, 188) |
SevenSegNum5.SetForeColor2 RGB(188, 188, 188) |
SevenSegNum6.SetForeColor2 RGB(188, 188, 188) |
'设置控件显示的数码 |
SevenSegNum1.SetValue int(Mid(s, 1, 1)) |
SevenSegNum2.SetValue int(Mid(s, 2, 1)) |
SevenSegNum3.SetValue int(Mid(s, 4, 1)) |
SevenSegNum4.SetValue int(Mid(s, 5, 1)) |
SevenSegNum5.SetValue int(Mid(s, 7, 1)) |
SevenSegNum6.SetValue int(Mid(s, 8, 1)) |
End Sub |
按F5运行,一个数字时钟就制作完毕。如下图所示: |
![]() |
程序在PIII500,128M内存,Win98,VB6.0 VC6.0环境下调试通过。 本文作者 烟台鲁宝钢管有限责任公司 马近科 |