蒲应文
摘要 本文主要讲解具有赛事管理和排名功能的赛场计分程序的实现过程。 关键词 VB,计分,赛事,排名 一、引言 在工作中举行的各种演讲比赛、歌咏比赛等活动,除了抽签这一要务外,更重要的任务就是计分了。计分员要为每个选手记录各评委的分数,并计算总成绩和平均分,最后还要排列名次,计分员在整个赛事过程中都要忙碌的计算,为了准确性,分数计算一般都要重复一次,这样很是浪费人力资源,而且效率很低,于是,我们可以编写一个专门用于赛事计分的程序,让它能录入各评委为每位选手打的分数,并自动计算成绩和排名。 二、编程分析与核心代码 本文设计的这个计分员程序提供赛事管理和计算排名功能,赛事管理包括新建赛事、打开赛事、关闭赛事。新建赛事时需要用户提供选手数、评委数、计分小数位、最高打分、最低打分、是否去掉高低分等赛事相关参数等信息。为了增强程序灵活性,让它能适应不同赛事中选手和评委数量不定这一情况,程序采用动态创建数据库技术,为不同的赛事创建独立的数据库,以便保存赛事数据。 1.参数及姓名初始化 在新建赛事数据库前,需要确定赛事的基本参数,这些参数通过5个组合框提供选择和1个复选按钮设置,核心代码如下: '定义窗体级变量 Dim Num As Integer '选手数 Dim Pws As Integer '评委数 Dim Dec As Integer '小数位 Dim Max As Integer '最高打分 Dim Min As Integer '最低打分 Dim Cut As Integer '去高低 Dim Xm() As String '选手姓名 '初始化参数选择项 Private Sub Form_Load() For I = 1 To 100 If I>2 Then Combo1.AddItem I '选手数3~100人 If I>2 And I<=30 Then Combo2.AddItem I '评委数3~30 If I<=4 Then Combo3.AddItem I '小数位1~4 If I>9 Then Combo4.AddItem I '最高分10~100 If I<100 Then Combo5.AddItem I '最低分0~99 Next End Sub '最低分选择项控制 Private Sub Combo4_Click() Combo5.Clear For I = 0 To Val(Combo4.Text) - 1 Combo5.AddItem I '添加选择项 Next End Sub '参数及姓名初始化功能函数 Sub Init() '当前赛事各参数初始化 Num = Val(Combo1.Text) '选手数 '按照以上方法设置以下变量值:评委数、小数位、最高打分、最低打分,代码略 Cut = -Check1.Value '去高低 '输入选手姓名 ReDim Xm(0 To Num - 1) For I = 0 To Num - 1 S = "" '清空上次输入 While S="" '防止输入空值 S=InputBox$("第" & (I+1) & "个选手:","输入姓名") Wend Xm(I) = S '保存选手姓名 Next End Sub 2.根据赛事参数创建数据库 在VB中,使用ADO开发数据库应用程序时,要引用对象库“Microsoft ActiveX Data Objects 2.x Library”,它是VB数据库最核心的对象群之一,也是VB数据库开发人员经常所引用的对象库。在程序运行过程中创建数据库和表及字段,还要引用对象库“Microsoft ADO Ext 2.x. For DDL Security”,简称为ADOX,是对ADO对象和编程模型的扩展,包括创建、修改和删除模式对象,如表格和过程。ADOX的主要对象如下: Catalog:包含描述数据源模式目录的集合 Table:表示数据库表,包括列、索引和关键字 Column:表示表、索引或关键字的列 创建的赛事数据库包括2个数据表,一个是mPara表,用于存放赛事参数,一个是mData表,用于存放赛事各选手的数据,核心代码如下: Dim pStr As String '数据库链接 '创建数据库的功能函数 Function CreateMdb(dbName As String) As Boolean On Error GoTo Errs Dim Cat As New ADOX.Catalog '数据库对象 pStr="Provider=Microsoft.Jet.OLEDB.4.0;Data Source="+dbName Cat.Create pStr '创建 Cat.ActiveConnection=pStr '连接 Dim Tbl As New Table '数据表对象 Tbl.ParentCatalog = Cat Tbl.Name = "mPara" '参数表 Tbl.Columns.Append "选手数",adInteger '按照上一行的格式添加以下adInteger字段:评委数、小数位、最高打分、最低打分、去高低,代码略 Cat.Tables.Append Tbl '建立 Set Tbl = New Table Tbl.Name = "mData" '数据表 Tbl.Columns.Append "序号",adInteger Tbl.Columns.Append "姓名",adVarWChar,250 For I = 1 To Pws '添加评委打分字段 Tbl.Columns.Append "评委" & I, adSingle Next '按照上面的格式添加以下adSingle字段:总分数、最高分、最低分、有效分、平均分,代码略 Tbl.Columns.Append "排名", adInteger Cat.Tables.Append Tbl '建立 CreateMdb=True : Exit Function Errs: CreateMdb = False End Function 3.根据选手数添加记录到数据库 在新建赛事数据库后,还需要把参数写入到mPara表,并根据选手数自动添加记录到mData表。在添加选手记录时,根据出场顺序自动填充“序号”和“姓名”字段的值,其他字段的值置0。数据库的操作使用ADODB对象,实现的核心代码如下: Dim Rs As New ADODB.Recordset Dim Cn As New ADODB.Connection '初始化记录的功能函数 Sub AddRec() Cn.Open pStr '打开数据库链接 Rs.Open "mPara",Cn,adOpenKeyset,adLockPessimistic '打开参数表记录 Rs.AddNew '添加新记录 Rs.Fields("选手数").Value = Num '按照上一行的方法设置以下字段的值:评委数、小数位、最高打分、最低打分、去高低,代码略 Rs.Update : Rs.Close '保存记录 Rs.Open "mData",Cn,adOpenKeyset,adLockPessimistic '打开数据表记录 For I = 1 To Num Rs.AddNew '添加新记录 Rs.Fields("序号").Value = I Rs.Fields("姓名").Value=Xm(I-1) For K = 2 To Rs.Fields.Count - 1 Rs.Fields(K).Value=0 '其他字段 Next Rs.Update '保存记录数据 Next Rs.Close '关闭数据库操作 End Sub 4.装入数据到网格 在数据库创建成功后,接下来就是把数据表中的记录数据读入到网格控件,以便进行分数的录入。无论是新建的数据,还是打开的历史数据库,都先从参数表中读取当前赛事的参数,然后从数据表中读取数据到MSHFlexGrid网格控件中,实现的核心代码如下: '装载选手数据的功能函数 Function DataLoad() As Boolean '打开mPara参数表,代码同前 Num = Rs.Fields("选手数").Value '按照上面的方法读取以下参数值:评委数、小数位、 '最高打分、最低打分、去高低,代码略 '打开mData数据表,代码同前 With Msh '初始化网格控件的行、列 .Clear : .Rows = 1 .Cols = Rs.Fields.Count For I=0 To Rs.Fields.Count-1 .TextArray(I)=Rs.Fields(I).Name Next '设置网格标题 While Not Rs.EOF .Rows = .Rows + 1 For I = 0 To Rs.Fields.Count - 1 If Rs.Fields(I).Type=4 Then '单精度字段整理小数位 .TextMatrix(.Rows-1,I)=Round(Rs.Fields(I).Value,Dec) Else '其他字段直接读取 .TextMatrix(.Rows-1,I)=Rs.Fields(I).Value End If Next Rs.MoveNext '下一个选手记录 Wend If .Rows>1 Then .FixedRows=1 '置固定行 End With End Function 调用以上各功能函数实现数据库创建的控制代码如下: Dim MdbFn As String '数据库文件名,其值通过CommonDialog对话框控件设置,代码略 Private Sub Command1_Click() If Cn.State<>0 Then Cn.Close : Cn=Null Call Init '初始化参数和姓名 If CreateMdb(MdbFn) Then '创建数据库 Call AddRec '添加记录 MsgBox "赛事数据库创建成功!" Call DataLoad '装载数据 Else MsgBox "赛事数据库创建失败!" End If End Sub 5.打开历史赛事数据库 如果计分中途退出了程序,那么可以重新打开数据继续录入工作。核心代码如下: Private Sub mOpen_Click() ComDlg.ShowOpen '打开对话框选择数据库文件 MdbFn = ComDlg.FileName Cn.ConnectionString="PROVIDER=Microsoft.Jet.OLEDB.4.0;Data Source="+MdbFn '链接数据库 Cn.Open '打开数据库链接 Call DataLoad '转载历史数据 End Sub 6.分数录入的方法和实现 由于使用了不具有编辑功能的MSHFlexGrid网格控件,那么,需要用文本框为它增加输入功能,在网格单元格上回车后,将文本框显示到当前网格位置并激活输入,输入完毕后,在文本框中回车保存数据到数据库和网格中,通过选手的序号(从网格中获取)实现数据库记录的定位。实现的核心代码如下: '修改单字段值的功能函数 Function FieldModi(ByVal ID As Integer, ByVal tField As String, ByVal tValue As Single) As Boolean Rs.Open "select * from mData where 序号=" & ID,Cn,adOpenKeyset,adLockPessimistic Rs.Fields(tField).Value=tValue Rs.Update : Rs.Close End Function '网格控件回车进入录入状态 Private Sub Msh_KeyPress(KeyAscii As Integer) If KeyAscii=13 Then '回车 '只能输入各评委打分字段的值 If Msh.Col>1 And Msh.Col<Msh.Cols-6 Then '调整文本框的位置和大小 Text1.Left=Msh.Left+Msh.CellLeft Text1.Top=Msh.Top+Msh.CellTop Text1.Width=Msh.CellWidth-15 Text1.Height=Msh.CellHeight-45 '初始化文本框的数据,以便修改 Text1.Text=Msh.TextMatrix(Msh.Row,Msh.Col) '自动选择文本,以便覆盖输入 Text1.SelLength=Len(Text1.Text) Text1.Visible=True '显示控件 Text1.SetFocus '激活输入 Text1.ZOrder '将控件置顶 End If End If End Sub '鼠标双击单元格也进入录入状态 Private Sub Msh_DblClick() Call Msh_KeyPress(13) End Sub '输入数据并保存 Private Sub Text1_KeyPress(KeyAscii As Integer) If KeyAscii<48 Or KeyAscii>57 Then '0~9 '屏蔽Esc、退格、回车和小数点以外的按键 If KeyAscii<>27 And KeyAscii<>8 And KeyAscii<>13 And Chr(KeyAscii)<>"." Then KeyAscii = 0 '屏蔽 Else If KeyAscii=27 Then 'Esc键取消输入 Msh.SetFocus : Exit Sub End If If KeyAscii=13 Then '回车保存数据 Text1.Text=CStr(Round(Val(Text1.Text),Dec)) '整理小数 If Text1.Text<>Msh.TextMatrix(Msh.Row, Msh.Col) Then '已修改 If Val(Text1.Text)>Max Or Val(Text1.Text)<Min Then '超出打分范围 MsgBox "输入超出范围,请重新输入!" Msh.SetFocus: Exit Sub End If Msh.TextMatrix(Msh.Row,Msh.Col)=Text1.Text Dim No As Integer '序号 No=Msh.TextMatrix(Msh.Row,0) Dim Fn As String '字段名 Fn=Msh.TextArray(Msh.Col) Call FieldModi(No,Fn,Val(Text1.Text)) '写入数据库 End If Msh.SetFocus End If End If End If End Sub '输入框失去焦点时自动隐藏 Private Sub Text1_LostFocus() Text1.Visible = False End Sub 7.计算分数与排名 在计算时,程序从各评委的打分中找出最高分和最低分,并计算所有评委打分的总分数,根据去高低分参数,计算选手的有效分(去高低分后的分数)和平均分(有效分的平均值),并将计算结果写入到数据库。在排名时,根据各选手的平均分,依次找出未排名选手中的最高分,并把当前名次保存到Pos数组,完成排名计算后,把名次写入到数据库。实现的核心代码如下: '计算分数和排名的功能函数 Sub CalcSort() ReDim Avg(1 To Num) '平均分 ReDim Pos(1 To Num) '名次 '打开mData表,代码同前 N = 0 '计算成绩 While Not Rs.EOF '各选手 iMax=-1 : iMin=Max+1 : Sum1=0 For I=1 To Pws '读取评委打分 iVal=Rs.Fields("评委" & I).Value '找最高分和最低分 If iVal>iMax Then iMax=iVal If iVal<iMin Then iMin=iVal Sum1=Sum1+iVal '计算总分数 Next Sum2=Sum1+(iMax+iMin)*Cut '计算有效分 N = N + 1 '记录平均分 Avg(N)=Sum2/(Pws+Cut*2) '将计算结果写入到数据库 Rs.Fields("总分数").Value=Sum1 '按照以上格式写入以下字段值:最高分、最低分、有效分、平均分,代码略 Rs.Update : Rs.MoveNext Wend '计算名次 For I = 1 To Num '第1~Num名 iMax = -1 For K = 1 To Num '找最高分 If Pos(K) = 0 Then '未排名 If Avg(K) > iMax Then iMax = Avg(K) '记录最高分 N = K '第I名的位置是N End If End If Next Pos(N) = I '记录名次 Next N = 0 '写入各选手的名次 Rs.MoveFirst While Not Rs.EOF N = N + 1 '第N个选手 Rs.Fields("排名").Value=Pos(N) Rs.Update : Rs.MoveNext Wend Rs.Close '关闭数据库操作 End Sub 调用以上功能函数即可实现计算与排名。 8.关闭赛事数据库 在比赛结束后,需要关闭数据库,以保证数据的安全,关闭赛事目的就是关闭数据库链接,核心代码如下: Private Sub mClose_Click() Cn.Close: Set Cn = Nothing End Sub 三、结语 本文实现了一个完整的赛事计分与排名程序,支持10分制和100分制赛事,根据最高打分和最低打分参数,实现了超范围输入的检测与非法输入。程序中没有使用具有编辑功能的网格控件DataGrid,而是使用了数据库浏览网格控件MSHFlexGrid,是因为DataGrid控件实现部分字段修改有一定的难度,而MSHFlexGrid控件则有利于提供数据的安全性。本文给出了数据库操作的大部分方法,其中,动态创建数据库、为MSHFlexGrid控件增加编辑功能,都是值得学习的。限于篇幅,报表和证书打印功能就留给读者朋友进一步完善了!另外,在创赛事建数据库时,可以把“评委1~评委Pws”等字段名用各评委的真实姓名来创建,评委姓名的处理方法可按选手姓名那样输入,这样记录的数据更真实,但录入的时候就存在对应关系了。 本程序采用了数据库技术,为保证程序能在其他计算机上独立运行,除用VB自带的打包制作安装包外,还可以使用VB-PowerWrap这工具将执行程序和动态库捆绑为一个能独立运行的执行程序,实现程序的免安装和绿色化。程序调试环境:WinXp+VB6,运行界面如下图所示。
|