博客目录: 一、问题描述
二、设计思路
三、UI开发过程
四、对接过程
五、整体总结
六、对课程的两点建议
一、问题描述
本次结对编程作业分为以下两种类型Core(计算核心)和UI(用户界面),Core组负责根据设置要求生成一定数量的四则运算题目,UI组负责制作用户界面,然后两组对接完成一个完整的四则运算软件。具体要求详见:作业要求
二、设计思路
我们抽到的是UI组,负责界面的编写,我们选用的是Qt Creator这款编辑器。界面主要分为欢迎页(可在该页通过对话框设置参数)、答题页、反馈页(包括历史记录和错题汇总)。各个界面文字部分用label来写,倒计时用LCD number结合定时器来实现,用lineedit来接收用户的输入,页面之间使用信号和槽进行连接从而实现通过按钮来跳转,反馈页面中使用textedit向用户呈现结果。
三、UI开发过程
1.制作欢迎页以及设置对话框(4.5下午 驾驶员:胡宇晨, 领航员:张学忠)
工作重点在于使用label说明内容,pushbutton实现相应功能,使用对话框实现对参数的设置;
遇到的bug:设置对话框无法显示标题的所有内容,后发现对话框大小与提示说明的内容长度有关,后通过调节空格解决。
2.制作答题页面(4.5晚上 驾驶员:张学忠,领航员:胡宇晨)
工作重点在于使用lcdnumber显示器结合定时器实现倒计时功能,以及lineedit获取用户输入,通过确定按钮更新题目。
遇到的bug:
(1) 我们最初的想法是通过stackedwidget来实现切换页面从而更新题目,但是这样需要我们具体编辑一页的ui界面,当题目数量达到一定规模时这种方法就不再适用,解决方法是只设置一个页面,然后通过更新标签内容实现题目的跳转;
(2)当每更新到下一题时,上一题的答案仍然保留在文本框里,用户需要删除它之后再输入新答案,我们通过在每一次获取答案后将文本框置空的方法(代码为 ui->answer1->setText("");)解决了这个问题;
(3)上述(2)问题解决后,我们发现题目更新后光标并不在文本框中,于是我们加了一行代码 ui->answer1->setFocus(); 解决了这个问题。
3. 实现页面之间的跳转(4.6下午 驾驶员:胡宇晨,领航员:张学忠)
主要是通过信号和槽一一对应的方法来实现按钮触发页面跳转的:(下图实现从welcome页面通过按钮跳转到答题页面wodget)
之前我们试过在button的触发响应函数里使用accept(),然后通过检测accept信号实现跳转,但是这样的话代码需要编写在main()里面而且需要判断语句,后来我们通过查资料获知上述方法,用很清晰的结构实现页面跳转。之后的反馈阶段,我们在上述基础上还增加了页面回退的功能(主要是通过父子页面之间的带参传递)。
4. 数据处理及文件操作(4.6下午及晚上 驾驶员:张学忠,领航员:胡宇晨)
我们假定现已获取两个分别存放题目和答案的txt文件(produce_q和produce_a,每一行存放一道题或者一个答案并根据行号对应起来),我们的设计思路是在答题页面呈现之前从上述两个文件里分别逐行读取题目和答案,并存放到两个QString数组里(设为ques和ans),然后我们每次更新题目页面时就从ques数组里读出题目并显示在lineedit里,用户每答完一题我们就获取用户输入并与标准答案比对以统计得分(在用户答完最后一题时我们将告知用户其得分情况),同时把题目和用户输入作为一个完整的表达式写入文件history.txt,如果此题用户回答错误我们再将它写入文件error.txt并注释正确答案。整个过程我们全部使用文件的读写操作,其一是因为Qt的文件操作非常便捷,其二也是为了让用户在答题之后能获得一个存放记录的文件(不仅仅是通过我们的软件来查看)。
(这一阶段出奇地顺利,基本没有遇到什么bug)
5. 制作反馈页面、历史记录页面以及错题汇总页面(4.6晚上及4.7下午 驾驶员:胡宇晨,领航员:张学忠)
我们在上述数据处理和文件操作的基础上编写了历史记录和错题汇总界面,二者都可以通过反馈界面的相应按钮点击进入。
工作重点在于返回功能的设置,我们采用的是带参传递的方式:
(关于反馈页三个按钮功能的描述) (历史记录页面的返回功能,错题汇总页面同理)
遇到的bug:
(1)返回之后再进入历史记录页面无法打开history.txt,解决方法:在返回之前关闭文件history.txt(close函数);
(2) 如果在历史记录界面连续两次点击“查看”按钮会导致文件打开失败,解决方法:点击一次“查看”按钮后立即将其锁定(代码: ui->pushButton_2->setEnabled(false); ),从图中也可以看出。
6.重构设置界面(4.7晚上 驾驶员:张学忠,领航员:胡宇晨)
之前我们的设置界面是通过输入对话框InputDialog来实现的,这样的缺点在于每个设置的单项都会弹出一次,所以即使用户想选择默认值,仍然需要经历一遍所有的设置单项,而且不支持选择式的设置框,综合以上考虑我们准备仿照上面的历史记录和错题汇总界面。设计出来是下面这样的:(图片较大因为我们对之前的界面进行了放大处理)
设置项包括题目数量、题中操作数的最大取值、题中操作数的数量、操作数的数据类型、操作数的精度(如果是小数的话)以及支持的运算种类,设置框则采用了填空、单选和多选的形式,用户输入设置信息之后点击确定我们就获取这些信息传入Core组的设置输入接口,即可按照用户的需求进行题型的设置,如果用户放弃输入我们将使用默认值(上图的提示信息中已给出),如果用户输入不合法的值我们也会将其改为默认值。相应模块的代码如下:
(获取各设置项的值) (对不合法数据的处理)
至此整个UI界面部分就全部设计完成了,优化部分准备完成对接之后再行考虑。
四、对接过程
由于我们做的是与用户交互的UI界面,核心功能还是由core组完成的,需要对接之后才能完成四则运算器的具体功能。由于时间限制,我们目前只成功对接了五组core,先在此总结一下,之后对接工作还将继续。
1、吴雪晴组
对接采用动态链接库.dll,在debug模式下运用显式调用,成功地调用了dll中的函数,通过文件传递生成的题目和答案。界面和之前一样就不放图了,我们直接进txt看看吧!
假设我们设置题目数为10、操作数最大为100、操作数个数最大为10、操作数为整数、支持加减乘除和乘方:
(生成的十道题) (对应的答案)
对接过程中遇到的bug:
Qt调用dll:我们之前查资料得知隐式调用是最便捷的调用方法,但是调用之后程序一直报错(一直显示未定义),之后经查阅相关知识得知是因为MinGW编译器无法使用隐式调用,面对安装vs编译器和换用显式调用两种解决方法,我们选择了后者;在显示调用的过程中,我们发现函数在运行的过程中会报错而终止,后发现是可以连接上dll但连不上函数,查阅资料后得知是vs编译器在编译过程中会改变函数名,我们使用depends软件打开dll文件找到被改变的函数名,最后成功连上了函数。
2.刘梓轩组
同样使用显示调用动态链接库的方式,因有了和第一组对接的经验,此次对接较为顺利,但还是出了一些小bug:
(1)在他们提供的.h文件里,__declspec(dllexport)前为单短线,这在VS下可执行,但是在qt下会报错,手动改成了双短线;
(2)测试过程发现他们还有一小部分core的功能没有实现,经core组修改后解决了这个问题。
3.林静雯组
采用显示调用dll方式,对接过程也比较顺利,但检测出一个逻辑上的bug:
我们给调用的函数传递设置信息时,在运算类型这块出了一个bug,我们的思路是在ui界面上放置5个复选框分别对应加减乘除和乘方,如果我们检测到五个选项无一选中就将其默认为加减乘除,我们是使用一个标志flag_1完成这一步的,我们犯的错误是把赋默认值这一步放在了设置页面的按钮事件中,这样做的问题在于假如用户不进入设置页面直接开始答题的话,我们传入的这五个参数都将是0(初始值),这会导致dll崩溃(恰好对面的dll中没有这一步检测..)。这是一个低级的逻辑错误,我们把它写在这里是想给自己一个提醒。
4.耿子钢组
同样采用显示调用的方法,这一组的不同之处在于他们是使用结构体传参的,而之前几组都是用函数传的。我们遇到bug是关于参数传递的问题,我们和core组联系以后对方非常积极地帮我们调试,后来发现是因为他们对题目结果大小做了限制(1000以内),参数入口限制为:若只选择加减法,操作数允许在500以内,若选择了乘除,操作数则只允许在20以内。沟通之后我们对设置页面以及参数纠偏部分做了修改便对接成功了。
5.王亚正组
该组对接情况与第三组基本相同,在此不再赘述。
五、整体总结
1.结对编程的意义:
(1)互相监督,避免编程中的很多不必要的错误;
(2)针对每个模块,有更多的思路和方法,疏漏之处能及时发现,开发出的软件功能上也更加完善;
(3)遇到bug时,可以从更多的思维角度去解决,同时可以寻求更多的资源,使得问题的解决明显更加快捷;
(4)结对过程中,可以在遇到困难时相互鼓励,坚持沟通,保持良好的情绪,有助于项目的进展。
2.从结对编程中学到了什么?
(1)学习了Qt开发技能,了解了dll、lib等基本概念和使用方法,并初步接触到了MVC架构;
(2)对于编程过程中的一些逻辑规则、编码规范有了更深入的认识并加以实践;
(3)更加意识到了合作的重要性以及一些合作中的注意事项;
(4)从对接过程中认识到了沟通交流对于合作的重要性;
(5)解决问题的能力得到了很大的提升。
3.走上工作岗位后,是否选择结对编程用于解决部分任务?
我选择使用结对编程,因为结对编程可以大大提高项目开发的效率,尤其是工作量较大的项目,具体的原因上述两点已经详述,在此略过。
4.今后的团队作业,每周的任务,准备如何吸收个人作业、结对作业,改善开发流程?
团队作业也需要像结对作业一样,事先给出完整的架构,接口双方、前后端应当事先进行交流并制定出双方都同意的方案,避免后续的修改;团队作业中,对于部分模块也可以使用结对编程的模式以提高局部开发的效率;团队作业中,各个成员需要及时进行单元测试,排除bug,避免对团队造成影响。
六、对课程的两点建议
(1)减少课后作业量,课堂内容增加实用技能教学、减少理论知识介绍;
(2)希望作业要求更加明确,发布作业时能一锤定音;
PSP表格
步骤 | 预估耗时(小时) | 实际耗时(小时) |
计划 | 0.5 | 0.5 |
·估计开发时间 | 0.5 | 0.5 |
开发 | 83.4 | 95.6 |
·需求分析(包括学习新技术) | 8 | 10 |
·生成设计文档 | 0.5 | 0.5 |
·设计复审 | 0.5 | 0.1 |
·代码规范 | 0.5 | 0.3 |
·具体设计 | 1 | 2 |
·具体编码 | 12 | 16 |
·代码复审 | 0.5 | 0.5 |
·测试(包括对接过程) | 48 | 60 |
报告 | 6.2 | 8.1 |
·测试报告及博客 | 5 | 6 |
·计算工作量 | 0.2 | 0.1 |
·总结 | 1 | 2 |
合计 | 83.9 | 106.1 |
笔者能力有限,还请大家多多指教,感激不尽!!
参考博客: