Delphi 下IntraWeb网络考试系统开发综合实例Word下载.docx
《Delphi 下IntraWeb网络考试系统开发综合实例Word下载.docx》由会员分享,可在线阅读,更多相关《Delphi 下IntraWeb网络考试系统开发综合实例Word下载.docx(28页珍藏版)》请在冰豆网上搜索。
用户帐号,有索引,唯一
12
用户的真实姓名
20
所在学校
Subject
任教科目
userphone
15
联系电话
表三:
Papername(记录试卷名称、科目及具体题目的数量及分值)
papername
30
试卷名
bywho
输入者
inputtime
输入时间
试卷所属科目
试卷状态(练习、删除、考试、统考)
isrepeat
是/否
是否能重复进行测试
Singleti
数字/字节
单选题数量
Singlescore
分值
Multi
多选题数量
Mulscore
Fillti
填空题数量
Fillscore
judgeti
判断题数量
judgescore
canrandom
试题是否能随机顺序出卷
在这里出卷的时候就可以设置好基本的参数,如是否可以随机顺序,因为有的题目必须按一定顺序出,就不能用随机组卷的方法来考试。
统考由管理员或教师设定,删除只能由管理员作最后处理,教师可以删除自己出的卷,但只是假删除,完全删除要由管理员执行。
表四:
Sysset(设置与记录考试试卷的主要参数)
Paperid
长整数
试卷编号
Trainstatus
练习开关状态
Teststatus
测试开关状态
Mustlog
是否必须是注册用户
checkpm
考试完毕后是否能查看排名
可以多套试卷同时存在,每套试卷有独立的参数控制,为减少数据库重复及节约数据空间,试卷名用编号而不用文本,不然的话30字节长度比4个字节长度要大得太多了!
表五:
Tiku(具体试卷题目)
Content
memo
题干
Type
题型
Key1
200
选择1
Key2
选择2
Key3
选择3
Key4
选择4
Key5
选择5
Turekey
正确答案
paperid
papername表里的自动编号
程序中可以是对四种题型进行试卷编制,但在此范例程序中只对选择题(单选和多选)进行示范。
表六:
Result(用户测试及练习的成绩)
编号
用户名
用户编号(唯一)
paperstatus
试卷类型
Testtime
16
测试开始时间
usetime
5
测试所用时间
Percent
正确率
试卷编号(唯一)
Userip
数字/整型
测试机IP
tinum
总题量
numright
正确数量
Point
成绩
2007-2-2
第二天:
登陆窗口的制作与代码实现
环境:
Delphiforwin32+IntraWeb8.0
File-New-Other-IntrawebApplicationWizard,出现如下窗口
(新建程序.jpg), (参数.jpg)
确定,进入Delphi界面,可以看到有四个文件选项(还有一个是Delphi的欢迎窗口,如1图),
我们在这一阶段要做的就是用Unit1来做登陆界面,而UsersessionUnit是IW程序中线程控制的关键单位,我们的所有数据库链接都放在上面,所有session由IW自动管理,我们只要把表面的工作做好就行了。
到IWstandard中拖出一个TIWregion控件,作用是像在WORD中的表格,用来定位,这是我比较喜欢的做法,再拖入TIWLabel、TIWEdit和TIWButton控件各两个,还可以放入一个标签来做网站名,最后效果如图:
(图2)
注意:
密码输入的文本框要把密码提示(PasswordPrompt)打开,不然谁愿意明码输入自己的密码?
类型那一个下拉控件是为了使学生和教师在同一个窗口实现登陆的,记得在Items属性中加入学生用户和教师用户两行属性值。
运行的效果图如下:
(登陆效果图)
接下来,我们来用代码实现从数据库中验证用户并完成登录:
我们从dbGo工具栏中把TADOconnection(按自己习惯改名,我把它改成con1)和TADOQuery(改名为qry1)两个控件拖到UserSession单元中,选择con1,编辑Database-ConnectionString,点击
编辑数据库的连接字符串,建议大家先把第一天做好的数据库文件放到应用程序所在的目录下。
(两数据连接图)
看出这两张图中所演示的连接路径有什么不同没有?
左边的是绝对路径,而右边的用的是相对路径,右边的才是我们要的结果!
!
为了布署方便,我们把数据库文件和应用程序文件放在同一目录下。
所以上面的路径由“..\data\test.mdb”就变成了“test.mdb”。
回到Unit1单元的界面,点击:
File-UseUnit,选择Usersession,因为我们要在这个单元调用UsersessionUnit的数据控件,所以要在程序中引用该单元文件。
再拖入一个TADOQuery控件,设置Qry1的Connection为IWusersession->
con1,到这里,我们就做好了程序与数据库的连接了。
如果调试时出现以上错误,说明在ServerController单元中还有一个重要的参数没有设置,选择ServerController单元,在设计模式下到Properties属性栏中,找到ComInitialization,选择为MultiThreaded。
再运行测试就不会有这个错误提示了。
代码实现:
登陆按钮的代码:
procedureTIWForm1.IWButton1Click(Sender:
TObject);
varun:
integer;
//设置一个整数变量,用来记录查询的记录数,而不是反复做同一查询和统计。
begin//判断用户信息的有效性
if(iwedit1.Text='
'
)or(iwedit2.Text='
)then//如果用户名或密码有一个是空的话触发动作
begin
webapplication.ShowMessage('
请输入完整的帐号或密码!
);
//显赫警告信息
exit;
//退出操作,不进行下一步动作
end;
qry1.Close;
//关闭查询
qry1.SQL.Clear;
//将查询语句全部清空
ifiwcombobox1.ItemIndex<
>
1then//通过类型下拉框的值判断是学生用记还是教师用户登录
begin//选择学生用户时查询suser表
qry1.SQL.Add('
select*fromsuserwhereuserid='
+quotedstr(iwedit1.text)
+'
anduserpass='
+quotedstr(iwedit2.Text));
end
else
begin//选择教师用户时查询Tuser表
select*fromtuserwhereusername='
qry1.Connection.Connected:
=true;
//激活数据库连接控件
qry1.Open;
//执行查询
un:
=qry1.RecordCount;
//统计查询到的记录数量,并把数值赋于变量UN
//ifqry1.RecordCount
ifun=0thenbegin
没有此用户'
//如果没有记录说明没有此用户,并给出提示。
endelse
用户存在!
//记录不为0,说明用户的信息正确可以进入下一操作
Tfrmselect.create(webapplication).show;
//构建选择窗口并显示此窗口
说明:
如何显示下一窗口?
这一句是在IW中显示另一个窗口的语法格式,关键是Tfrmselect怎么定的,这就要根据你下一个想显示的窗口是什么名称了。
现在先为明天的工作作个前期准备:
点击File-New–Other–NewForm–ApplicationForm–OK,这样就建立了一个新的Form。
把此窗口的名称改为容易记的名字,如Frmselect(这就是上面的显示下一窗口要用的名称!
),保存文件,文件名也用个容易记的,如select,后缀名不用理它,给软件自动加上。
确定现在是在Unit1页面上,然后点击:
File–UseUnit–选择刚才保存的文件Select.dfm。
OK,运行测试,当用户信息正确后就能够进入到下一个窗口了。
2007-2-3
第三天:
选择试卷窗口的设计与代码实现
今天的任务量比较大,涉及到usersession的运用、SQL查询、数据显示、窗口跳转、
今天要实现的是把用户登录后从数据库检索出来的信息放入到Session中,在选择窗口中显示用户的真实姓名以增加程序的人性化,然后用户可以在此窗口中检索各科的练习卷或考试卷并进入相关测试。
这里与数据库的交互操作很多,是整个程序中最重要的部分之一。
界面效果如下,有的控件是在下一次工作时再起作用的:
第一部分:
Session准备与应用
因为线程存储的数据都是在服务器端,所以是安全的,那用户如何把需要的东西放进Session呢?
这就要用到UsersessionUint这个单元,例如我在这个单元的代码前段部分地方定义的几个变量:
private
{Privatedeclarations}
public
{Publicdeclarations}
//试卷参数用的变量
varpapername,paperid,paperstatus:
string;
//字符串变量,储存试卷名、试卷编号、
isrepeat:
boolean;
//是否可重复
varsingleti,singlescore,multi,mulscore,fillti,fillscore,judgeti,judgescore:
//各种题型的数目及给分值
//用户参数
varusername,userid,rightkey:
//储存用户真实姓名、学生ID号,正确答案
varrightno,wrongno,result,testtimes:
//分别是正确、错误数目、成绩、测试记录数
end;
如何把值存到变量中去?
在UsersessionUint单元的代码部分,找到Public定义处,按规则定义好几个字符串变量用以保存用户登录后及从数据库获取的数据。
要保存到这里的变量的话要在引用的单元里引用UsersessionUint单元,然后用:
UsersessionUint.papername:
=’你要赋的值’,其它在usersession中定义的变量都是这样操作。
如何引用变量中先前存入的值?
有一点是必须要明确的,那就是只要你要引用非自身单元的东西的话一定要在uses中加入那个单元的name(也就是你在Form中的name属性)。
如果要取胜值的话反过来用就行了,如:
iwlabel1.caption:
=usersession.papername。
变量可以随时加,随时用,但自己一定要记得用了什么变量,不要为同一件事重复地用不同的变量,这样对服务器的负担还是有一定影响的。
第二部分:
窗口的初始化
在显示这个窗口之前,当然需要从数据库中取出相关的参数来初始化界面,比如要看看有没有统一考试的题目,有的话把试卷名显示出来,没有的话就隐藏不必要的控件。
在这一个窗口中我写的初始代码如下:
procedureTfrmselect.IWAppFormCreate(Sender:
iwlabel3.Caption:
=usersession.username+'
欢迎您进入网络练习系统!
;
{根据上一登录界面查询到的用户记录信息,把用户的真实姓名人性化地显示到这一页面的最顶上}
usersession.qry1.Close;
//这里直接操作usersession单元里的query查询控件
usersession.qry1.SQL.Clear;
//查询语句清空
usersession.qry1.SQL.Add('
select*frompapername'
+'
wherestatus='
统考'
'
//增加查询语句,查询系统中设定为统考的席卷,为后面界面初始化准备。
usersession.con1.Connected:
//执行查询操作前先把与数据库的连接控件打开
usersession.qry1.Open;
//执行查询命令
begin
//如果查询到的刻录数为0则说明没有统考试卷设定,则显示统考卷的区域可以隐藏,
ifusersession.qry1.RecordCount=0then
begin
rgtrain.Top:
=rgtest.Top;
//为了界面的美观,把下region的位置移到与上面的高度一样。
rgtest.Visible:
=false;
//设置上区域块为不可见属性
end
else //如果有刻录,则取出papername属性中的值并在iwtext控件中显示出来
texttip.lines.add(usersession.qry1.FieldByName('
papername'
).AsString);
//下面的语句不论有没有记录都要做的
usersession.qry1.close;
//关闭查询
=False;
//关闭连接
根据我的经验,写数据库的相关程序,如果不用保持与数据库连接的话尽可能关闭与数据库相关的操作,特别是小型数据库(如:
access)是有连接限制的,而且要与数据库交互的话也不要直接用数据感知控件,而要通过一个connection,我就是用这一个办法解决了我第一次写网络考试程序只能30左右的人连接上去的疑难。
第三部分:
试卷的筛选
也就是按钮“搜”的代码了,这里要从前面的科目和试卷类型两个下拉选择框中获取参数,要选取是哪一学科,是考试卷还是练习卷。
在它下面的是一个IWlistbox控件,它的数据源是旁边放的,操作的过程大致如下:
通过usersession窗口中的Query数据控件执行查询,然后把qry1查询到的数据记录用一个循环把试卷名这一列的值全部读取到列表框中去,用户选择了哪一份试卷后,再按下按钮“选中并进入”,就把各类参数都存入相关的session变量中去并打开对应的练习或考试窗口。
要实现这个目的的方法有很多,我这里的仅是比较“笨”的方法。
“搜”的代码并不复杂,但却是对IW及数据库操作的基本功,请看:
procedureTfrmselect.IWButton1Click(Sender:
varsu,ty:
//设两个字符串变量来储存用户选择的科目和类型,到查询语句中使用
//webapplication.ShowMessage(inttostr(iwcombobox1.itemindex));
{我经常用显示信息来检验程序错误的}
ifiwcombobox1.ItemIndex=1then//判断下拉框的选取值是多少来定下是何科目
su:
='
综合'
else
信息技术'
ifiwcombobox2.ItemIndex=1then
ty:
考卷'
练习'
//对查询控件操作前,为保险起见,先关闭查询并清空
select编号,papernameas试卷名,bywhoas出题人,subjectas科目
usersession.qry1.SQL.Add(+‘frompapernamewheresubject='
+quotedstr(su));
usersession.qry1.SQL.Add(+'
andstatus='
+quotedstr(ty)+'
orderbyinputtimedesc'
{这是一个组合查询,简单的那种,限定记录必须是subject(科目)和status(试卷状态)}
usersession.qry1.active:
usersession.qry1.open;
lbselect.Caption:
查询到'
+inttostr(usersession.qry1.recordcount)+'
条记录,请选择您要的试卷。
//用一个IWLabel控件显示符合条件的查询到的记录数目
2007-2-4
第四天、选择界面的完善及进入相关页面
今天的内容与预想的有点不同,因为现在实现的选择界面比刚开始想的要复杂一些,因为要在这判定用户是选择练习还是测试,再决定是显示练习界面还是考试界面,如果是测试的话还要从数据库中获取该试卷是否允许重复,可以重复的话再判定用户是否超过3次的最大重复次数,
界面设计中还涉及到了从数据库中循环读取一个字段的值到列表框中,本来我是用Dbgrid来显示的,但一直有一个问题困扰我的就是,一旦查询语句发生改变,Dbgrid的存在就会导致程序的查询操作出现种种错误,我试了许多方法都解决不了,最后只能把试卷列表读入到列表框中。
再通过一个较为复杂的字符获取语句把试卷的编号读取出来,一些要注意的代码作下说明:
按钮:
选择并进入(代码解析)
procedureTfrmselect.btokClick(Sender:
//根据用户选中列表框中的行,通过分隔符#来获取试卷的编号来初始化后面要抽取的试卷。
usersession.paperid:
=copy(iwlistbox1.Items[iwlistbox1.itemindex],1,(pos('
#'
iwlistbox1.Items[iwlistbox1.itemindex]))-1);
{在读取到列表框的操作中,我是把试卷编号和试卷名同时读到一行去的,用“#”作分隔符,而后面要用到的试卷编号只是前面的部分:
如33#信息技术测试一,我们只要33就行了,所以要把33截取出来,那就用POS函数确定#在文本行中的位置,然后用COPY函数把#前的字符串拿过来并把它存进session中的paperid变量中去。
}
//用qry2来查询登陆用户对选中试卷的测试记录
usersession.qry2.close;
usersession.qry2.sql.clear;
usersession.qry2.sql.add('
select*fromresultwherepaperstatus='
+quotedstr(usersession.paperstatus));
andusername='
+quotedstr(usersession.userid)+'
and编号='
+usersession.paperid);
{此句查询的意思是:
选择所有试卷状态等于用户查询的试卷状态值并且用户名是用户ID,并且试卷编号是选中编号的记录}
usersession.qry2.open;
usersession.testtimes:
=usersession.qry2.recordcount;
usersession.qry2.Close;
//用QRY1定位用户选择的试卷,
select*frompapernamewhere编号='
+(usersession.paperid));
//并获取试卷的各项参数
usersession.papername:
=usersession.qry1.FieldByName('
).AsString;
usersession.isrepeat:
=usersession.qry1.fieldby