UI第三组总结 PB16150206朱池苇+PB16070837刘鼎乾
项目简介
这次软件工程结对项目为制作一个给小学生用的四则运算出题软件,我们分配到的是UI组,也就是负责人机交互,使用Core组封装好的模块。
GitHub地址:https://github.com/Ignoramus0817/Calculation-GUI
需求分析
- 对上述各属性参数(生成题目的数量,操作数的数量,题目及答案中的数值的范围……)进行设置
- 调用Core模块得到题目和运算结果,显示题目,接受输入,并能判断答案是否正确
- 增加“倒计时”功能,每个题目必须在20秒内完成,否则,得0分并进入下一题
- 增加“错题记录”功能,对于答错的题,将其保存下来,当下次进行“复习”时,增大错题在练习题中的概率
- 增加”历史纪录“功能,把用户做题的成绩纪录下来并可以展现历史纪录
- 增加“成绩分享”功能,生成成绩单,想一想成绩单里要展现什么,仅仅是最后的得分吗?错题的类型及数量?帮用户分析其薄弱的环节,提出合理的学习建议?
- 对所有Core组的模块进行测试
团队分工
结对编程作业大部分的时间都是采取共同编写代码的方式,即“一个做驾驶员,一个做领航员”,驾驶员负责敲键盘,领航员在一侧提供建议、检查错误或帮忙搜索相关的资料。
就这次团队项目而言,合作比较愉快。清明节的后两天两人一起学习了Qt。由于朱池苇对Qt比较熟悉,所以大部分程序都是朱池苇同学作为驾驶员,而刘鼎乾同学则作为领航员,与朱池苇同学对问题进行讨论,查找资料,检查错误等。博客则由两人共同完成。
PSP 表格
任务内容 | 计划完成需要的时间(min) | 实际完成需要的时间(min) | |
---|---|---|---|
Estimate | 估算 | 15 | 15 |
Analysis | 需求分析(包括学习) | 135 | 200 |
Design Spec | 设计文档 | 15 | 15 |
Coding Standard | 代码规范 | 10 | 10 |
Design | 具体设计 | 60 | 120 |
Coding | 具体编码 | 300 | 480 |
Code Review | 代码复审 | --(包含在编码过程中) | |
Test | 测试 | 120 | 300 |
Record Time Spent | 记录用时 | 10 | 10 |
Test Report | 测试报告 | 20 | 40 |
Size Measurement | 计算工作量 | 20 | 10 |
Postmortem | 总结改进 | 180 | 180 |
Summary | 合计 | 885 | 1380 |
代码规范
1、文件名:一律小写。
2、类名:一律使用UpperCamelCase。
3、变量名、对象名、方法名(函数名):一律使用lowerCamelCase。
二、代码风格
1、4空格缩进
2、左花括号换行
3、不同模块代码之间空行
4、预处理命令:
文件包含:类和头文件分别集中,并且模块之间空格
宏定义:宏名一律大写
三、其他
1、程序所有内容全部为英文,禁止出现中文和拼音,包括注释和UI界面文字。(博客下方展示代码块中注释是另外添加,程序中不含中文注释)
代码架构与具体实现
UI界面非常简单,由于使用qStackedWidget来实现翻页和题目的刷新,整个UI仅分为三个部分:主窗口CalGUI,存档对话框SaveSuccess,以及历史记录对话框History。绝大部分的功能都在主窗口上实现,存档对话框以及历史记录对话框仅有显示功能,因此着重介绍主窗口的结构和实现。
主窗口上添加StackedWidget控件,分成三页:
第一页:初始化生成条件
本页主要由各种数字输入框(SpinBox)和选项按钮(CheckBox、RadioButton)构成,用SpinBox::value
和CheckBox::isChecked
、RadioButton::isChecked
获取用户输入的值,而通过设定默认值和输入范围的方法,可以限制非法输入。
本页包含两个按钮(PushButton),Next和Cancel,Cancel顾名思义是退出,而Next则是说明已经设定完毕,可以开始做题,因此,Next的槽中调用了Core中的Setting接口。当用户按下Next时,各个参数已经设置完毕。
第二页:显示题目和题号、输入答案、倒计时
本页有两个按钮Start和Next,分别实现“开始”和“下一题”,Next按钮初始状态为隐藏,当Start被按下时显示。Start按钮使第一个题目显示在一个文本浏览框TextBrowser中,并使计时器开始倒计时。
倒计时的实现:
//添加在Start按钮按下信号的槽函数中 void CalGUI::on_startButton_clicked() { QTimer *timer = new QTimer(this); connect(timer,&QTimer::timeout,this,&CalGUI::timerUpdate); timer->start(1000); } //刷新时间的函数 void CalGUI::timerUpdate() { int a=ui->restTime->value(); if(a>0) ui->restTime->display(a-1); else ui->qNext->clicked(); //如果时间耗尽,发出Next按钮按下的信号,自动跳转至下一题。 } //timer的信号函数设置为空 void CalGUI::timeOut() { }
将Timer的时间耗尽和Next的按下两个信号连接在一起,即可方便地实现时间耗尽自动跳转的功能,由于判断正误由Next控制,因此将两者合并不会造成误判。
用两个QLabel分别显示当前题号和总题数。在Next槽函数中比较两个QLabel对应数字的大小(Qt提供了相关转换函数,非常方便),当前者大于后者时,进入显示结果页面。
第三页:显示结果,存档和查看历史记录
结果显示即为前面做题时存储的正确题目数量、错题、正确率的显示。
存档按钮控制生成.txt存档文件(由于没有掌握对话框数据传递,偷了个懒不让用户自行输入文件名),历史记录按钮控制查看往期记录(包括时间、正确题目数、总题目数、正确率、错题、错误答案和正确答案)。比较简单。
测试结果
参数设置页面
开始答题前
开始答题后
结果(太难无法口算因此全错)
结果2(为了展示错题和正确率统计故意错一半)
历史记录对话框
BUG记录与分析
这是编写UI和对接过程中遇到最多的一个BUG。
如果是在编写UI本身过程中遇到,一般是给出了函数声明,但是没有具体实现所导致的,在.h+.cpp这种类定义和成员函数定义分离的情况下,此类错误极易出现。我认为较好的方法是,声明后立刻在cpp文件中撰写函数定义,如果暂且不需要写,可以用花括号留空。
如果是在对接过程中遇到,不可能是相关函数core组同学没有定义。则一般是lib文件没有正常加载,或者是dll文件的属性(位数x86还是x64、debug还是release)与编译环境的属性不匹配所导致的。合理地发布SDK就能解决这种问题,所谓合理,指的是发布时分类,并且指明相应SDK的属性。
2、无法打开xxxx.lib文件
这是库文件没有正常加载导致的,Qt Creator中.lib文件的加载方式和VS中有所不同,需要在.pro文件中新增格式如下的代码:
LIBS +=-L$$quote(D:\zhu\Software Engineering\2018-SE-Course\Calculator GUI\CalGUI\untitled\Core15\x64) -lCore15 或 LIBS +=-LD:\zhu\SoftwareEngineering\2018-SE-Course\CalculatorGUI\CalGUI\untitled\Core15\x64 -lCore15
值得注意的是,假如需要将源代码发给他人,此处绝对路径需要修改为相对路径,否则无法正常加载。
许多组发布SDK时仅仅注明了Qt for VS的使用方法,而忽略了Qt Creator的使用方法。
3、函数参数表无法将xxx转换为xxx
这种BUG本是函数传入的参数和参数表不一致导致的,是编程时出现的失误,为何要在此处列出呢?因为Qt虽然支持部分C++库和语法,但两者仍然有区别。
比如Qt中的字符串类为QString,而C++中类为string(std::string),需要经过QString::fromStdString转换,才可以使用。
这种差异的存在导致对接过程中此类BUG大量出现,小心一些即可避免。
在与第七组对接后的BUG和解决方案:
1、显示结果页面正确率和正确题数显示不正常(由于计时器信号未阻塞,会导致正确题目数量不断增加,又由于正确率为实时计算,因此正确率也会不断增加),跳至结果页面后,阻塞计时器信号即可解决。
2、分辨率问题,控件无法自适应窗口大小和分辨率。修改UI布局可以解决(推荐刚开始编写UI时就考虑这个问题,否则会产生巨大的工作量)。
3、运行时有命令行(见上图),修改Qt设置文件,将CONFIG += console改为CONFIG += release即可。
由编程得出的一些建议
1、发布SDK时一定要根据编译环境和平台分类。
2、勤于写README,并且在写README时要考虑各种用户的需求(比如VS用户和Qt Creator用户)。
3、接口越少越好(接口数和参数数量折中)。
工作时刻
结对编程的意义相关
- 一个人编写代码,总是有种灯下黑的现象。一些非常简单的错误,由驾驶员自己是很难检查出来的,旁观者清,领航员则能够较为轻易地发现。
- 结对编程相当于在编写代码的同时,进行复审,这可以使驾驶员能够“朝着目标方向”前进,代码规范、算法思想、以及最重要的,概念完整性,可以得到保障。
- 可以不断从别人那里学习,提高自己的水平。在他人的监督下写代码有利于形成良好的代码风格,清晰的编码思路,以及熟练的调试技巧等
- 两个人的知识结构可以互补,比如驾驶员拥有稳重的编程风格和严谨的代码规范,但不善于设计巧妙的算法;而领航员则有灵活的头脑,两者结合,可以使代码高效、优雅而不失严谨,不会成为“奇技淫巧”。
关于走上工作岗位后是否会选择结对编程
(刘鼎乾)我认为我会选择结对编程。不仅是因为队友之间能相互找到彼此的灯下黑,还因为能在工作之余和别人进行交流,切磋。这也是不断提高自己,终身学习的有效途径。
(朱池苇)在工程需要迅速完成且规模不大时,我不会选择结对编程;但在工作量大的工程中,结对编程无疑是有其意义的。工程较大时,思路容易混乱,或者因为繁琐的各种定义而犯一些低级的错误。结对编程大大减少了这方面的成本。
课程建议:
(刘鼎乾)我认为邓老师确实非常非常负责,很认真地想把这门课上好,但是我觉得还是有一些问题。
1、上课不只是讲软件工程的理论,还希望能多讲一些实际编程有关的东西。
2、安排大作业和结对编程的时候,尽量和期中考等考试分开,能不影响大家的gpa就不影响。希望后面补做个人作业和结对编程时,不要放在和期末考冲突的时候,可以考虑放在暑假。
3、我觉得为了难而难是不可取的。有些时候加大难度我感觉不是很有现实意义。而我们工科是非常讲究现实意义的。增加难度可以,但要建立在实际有用的基础上,如果为了难为我们而故意弄一些很繁琐的事情,我觉得并不可取。
(朱池苇)目前我选这门课的目的基本上都实现了:了解自己技术栈的缺陷、体验一下稍难一些的工程、磨炼编程技术和肝功能(逃)。因此意见也不多。主要如下:
希望能具体介绍一下哪种工程师需要精通哪些技能(比如:前端工程师必须精通HTML,XML,JS,CSS,json,ajax等等),使我们课外自己学习更有方向性,我在这方面比较迷茫,哪些东西是会有用的,它们是用来干嘛的。如果能推荐一些教材或者学习时可以设定的目标等等就更好了。
团队项目如何改进
与结对编程类似,一个团队中一般都有所谓“架构师”“产品负责人”的存在,这些人对技术或是需求非常了解,他们应该成为团队的领航员,由他们规定工程的架构和规范,所有人需要根据他们设定的规则完成自己的工作。
而复审工作则不是同时进行的,而是完成一个部分后将代码交由领航员审核,对其中不恰当的部分进行修正。
同时组内工作职能相近的同学也可以采用标准的结对编程方式。