雅望的四则运算

1.项目地址

GitHub项目地址
结队大哥博客地址

2.PSP

PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划60120
Estimate估计这个任务需要多长时间1010
Analysis需求分析(包括学习新技术)300300
Design Spec生成设计文档60120
Design Review设计复审//
Coding Standard代码规范(为目前的开发制定合适的规范)6060
Design具体设计180180
Coding具体编码300420
Code Review代码复审120120
Test测试(自我测试,修改代码,提交修改)120240
Test Report测试报告6060
Postmortem&Process Improvement Plan事后总结,并提出改进计划180120
总计14501750

3.解题思路

此阶段主要实现程序的扩展要求,要求如下:

将程序变成一个Windows图形界面程序,增加倒计时功能,每个题目必须在20s内完成,如果完不成,则得0分并进入下一题。增加“历史记录”功能,把用户做题成绩记录下来,并可以展现历史记录。

进行需求分析可知,此阶段的任务有一下三点:

  1. 设计并编写图形界面,实现基本做题功能,包括出题、判断正误。
  2. 增加倒计时功能,限制每题时间为20s。
  3. 增加做题历史记录功能。

基于前后端分离的思想,后端应该实现可以生成不限数量题目的工具类,并提供接口,每次调用返回一道题目与答案。并且应该保证每次运行程序后生成的所有题目均不相同。前端实现上述的三个需求。

前端部分采用Qt5.12 LTS实现,布局采用QGridLayout以便可以在调整大小的时候依然保证正确的相对位置。但是相应的,这种做法无法采用Qt设计器拖拽实现,所以增加了工作量。

4.设计实现过程

4.1 前后端接口

  1. 题目的数据结构
    题目采用formula类进行封装,其中包括题目编号,题目与答案,并提供检查输入答案的方法。该类也是前端调用所提供的接口后返回的数据格式。

其声明如下:

class formula
{
public:
	int id;//题目编号
	string problem;//题目
	string answer;//答案

	formula();
	formula(string P, string A);
	formula(const formula& F);

	void init();
	void init(string P, string A);

	formula& operator =(const formula& source_formula);//重载赋值运算符

	int check(string u_answer);
};
  1. 题目生成类
    题目生成类名为generator,其中包含方法formula get_formula(int)。每次调用该方法都会返回一个上述类的实例,算式的答案也同时包含在其中,这样可以由前端直接判断用户输入的对错。

其声明如下:

class generator
{
private:
	vector<formula> formula_vector;//存储题目和答案的容器
	int sum;//存储的数量
	int index;//没有使用过的第一个题目和答案的编号(编号数值等于下标+1)
public:
	generator();

	//输出式子到文件path中,默认为problem_file.txt
	void output_into_file(string exp, string path = "problem_file.txt");

	//获取一个式子及答案,返回值为formula对象,当容器中没有未使用过的题目时,会自动生成新的题目
	//默认显示乘方为“^”,如果需要切换为“**”,传入整型参数1
	formula get_formula(int show_way = 0);

	//检查答案是否正确。id为题号,u_answer为用户的答案,正确返回1,错误返回0
	int check_answer(int id, string u_answer);
};

4.2 界面布局

界面设计如下所示:
用户界面上半部分是程序的主要功能:显示题目与输入答案。每次点击Start即为开始一次答题,左下方记录了此次答题的历史记录,包括题目、答案与正确答案。右下方记录每次答题的具体时间与得分情况。

同时,在题目上方提供了切换显示模式与倒计时的功能。模式切换提供了乘方的不同表示方法(^或**),倒计时限制为20s,若在限制时间内没有提交答案,程序会强制提交当前输入框中的内容。多次答题后,界面如下所示:
用户界面在左下方的题目历史记录中,错误的题目会被标记为红色,未答的题目空余并记0分。

4.3 倒计时功能

利用QTimer可以很方便的实现计时功能。最初的方案将Timer的时间设为20s,计时结束后执行响应的槽函数,模拟按钮的按下强制进入下一题。但是这种方案无法实时的显示倒计时时间,因为没有信号可以在每一秒结束后修改倒计时的值。

为了可以以秒为单位修改倒计时的值,另一方案是将Timer的时间设为1s,并增加计时变量int time。其初始值为20,每次Timer发出信号则对time进行减1,直至为0再赋值为20。同时,在每次信号发出时都可以修改显示的剩余时间。

代码实现如下

void mainwindow::onTimeOut()
{
//Timeout
	if (0 == time--)
		onEnterClicked();
		
//修改显示的倒计时时间
	string str = "RestTime:" + std::to_string(time) + "s";
	resttime->setText(QString::fromStdString(str));
}

4.4 题目记录与分数记录

题目记录在Enter被按下后调用,分数记录在每次End按下后调用。两者都采用QTableWidget实现,并动态的增加。在mainwindow的对象中记录了做题总数与做对题目的数量,在提交时也会一并修改。

此次答题结束后,会刷新题目历史记录,并在分数历史记录中增加此次答题的分数与时间。

代码实现如下:

void mainwindow::onEnterClicked() {
	all_count++;	//增加做题总数

	//重置剩余时间
	time = 20;
	timer->setInterval(1000);

	//动态插入列表
	int count = hislist->rowCount();
	hislist->insertRow(count);
	hislist->setItem(count, 0, new QTableWidgetItem(QString::fromStdString(f.problem)));
	hislist->setItem(count, 1, new QTableWidgetItem(QString::fromStdString(f.answer)));
	QTableWidgetItem *ansitem = new QTableWidgetItem(ansedit->text());
	//判断正误
	if (ansedit->text().toStdString() != f.answer)
		ansitem->setTextColor(Qt::red);
	else
		right_count++;
	hislist->setItem(count, 2, ansitem);
	ansitem->textColor();
	ansedit->clear();

	//生成新题目
	f = gen->get_formula(display_mode);
	quesedit->setText(QString::fromStdString(f.problem));

	//更新正确率
	string rate_str = "Correct Rate:" + std::to_string(right_count) + "/" + std::to_string(all_count);
	ratelabel->setText(QString::fromStdString(rate_str));
}

5. 总结与收获

  1. 对Qt布局有了更深入的了解。上一个项目的GUI大部分都是通过拖控件的方式制作,但是这种方式设计的界面采用了绝对定位的方式,也就意味着界面中的控件不能随着窗口大小的改变而改变。而这次的界面设计是通过编程完成的,并且采用了GridLayout的方式设计相对位置固定的布局,保证了用户界面的适应性。
  2. 因为需要组队完成项目,公用仓库,所以也提高了使用git的技巧。在开发过程中,以新分支的方式添加功能,当功能开发完成后合并分支,消除矛盾,使得结队编程可以同步进行。
  3. 事件与信号是Qt中相似却又不同的两个概念。相似的地方在于都能够处理消息;不同之处在于,信号的连接需要显式的制定,并且是对象与对象的绑定。事件的处理则是一个类的概念:在类中编写事件处理函数,所有实例化的对象都有处理该事件的能力。同时,事件可以通过过滤器过滤以实现对于特殊对象事件。在对象中没有事件处理函数的情况下,事件会向该控件的父节点进行传递,知道该事件被处理。
  4. 了解了开发前进行完整沟通与设计的重要性。在开始编程前对于接口的良好设计可以减少对接过程中的许多错误,从另一个角度加快了编码的速度。在设计中实现封装与数据隐藏可以使得接口调用者不必关心内部细节,只需要对于接口进行妥善操作,使得调用者可以专注自己本身的工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值