📞个人信息 学号:102101433 姓名:林堂钦
💡 作业基本信息
【课程】福州大学2021级软件工程A | https://bbs.csdn.net/forums/ssynkqtd-05 |
---|---|
作业要求链接 | https://bbs.csdn.net/topics/617294583 |
作业目标 | 实现一个简易计算器,要求具有图形化界面。 功能:具有基本功能的计算器 实现加、减、乘、除、归零基本操作。 附加功能:具有科学计算的计算器 实现次方、幂、三角函数等操作。 |
参考文献 | 1. https://www.cnblogs.com/xinz/archive/2011/11/20/2255830.html 2. https://www.cnblogs.com/xinz/archive/2011/10/22/2220872.html |
目录
💡 作业基本信息
📢 Gitcode项目地址
🔨 PSP表格
💬 解题思路描述
📌 接口设计和实现过程
🔑 主要的类和函数接口展示
🔑 MainWindow类
🔑 CalObj类
🔑 CalTest类
🔑 bepexpression.h
🔑 function.h
✅ 关键功能展示
👓 四则运算基础功能实现
👓 科学运算功能展现
👓 复合运算功能展现
⏰性能改进与优化
🏳🌈 时间显示功能
🏳🌈 历史记录及清空操作
🏳🌈 帮助文档以及ui界面美化
💊 单元测试
🚩 异常处理措施与展示
🎵 心得体会
📢 Gitcode项目地址
玄澈_ / 阿钦的科学计算器 · GitCode
🔨 PSP表格
PSP是卡耐基梅隆大学(CMU)的专家们针对软件工程师所提出的一套模型:Personal Software Process (PSP, 个人开发流程,或称个体软件过程)。
PSP的目的是:记录工程师如何实现需求的效率,和我们使用项目管理工具(例如微软的Project Professional,或者禅道等)进行项目进度规划类似。
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 15 | 15 |
· Estimate | · 估计这个任务需要多少时间 | 15 | 15 |
Development | 开发 | 1370 | 1570 |
· Analysis | · 需求分析 (包括学习新技术) | 540 | 540 |
· Design Spec | · 生成设计文档 | 70 | 70 |
· Design Review | · 设计复审 | 60 | 40 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 30 |
· Design | · 具体设计 | 80 | 180 |
· Coding | · 具体编码 | 300 | 360 |
· Code Review | · 代码复审 | 60 | 50 |
· Test | · 测试(自我测试,修改代码,提交修改) | 240 | 300 |
Reporting | 报告 | 110 | 115 |
· Test Repor | · 测试报告 | 60 | 70 |
· Size Measurement | · 计算工作量 | 20 | 15 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 30 |
· 合计 | 1495 | 1700 |
💬 解题思路描述
题目的要求是实现一个具有图形化界面的计数器,在实现普通的加减乘除四则运算的基础上,进一步实现科学计算器的相关内容,包括次方、幂、三角函数等相关操作。
出于作者的技术栈,本文选择了使用Qt跨平台C++开发工具在进行编写程序。
基于对题目的理解,以及查阅的相关知识,以下是本人实现该程序的一个解题思路:
- 首先设计GUI界面。使用Qt Designer等来创建GUI界面,包括数字按钮、运算符按钮、科学函数按钮等,然后将其与代码和函数进行绑定。
实现数学运算等相关功能。使用Qt的信号和槽机制来实现这些功能。例如,当用户点击加号按钮时,我们需要通过实现相关的响应函数将两个数字相加将其回显到屏幕。
除了数学运算功能外,还需要实现科学计算功能。可以使用Qt Math库中的函数来实现这些功能。例如,当用户选择平方根函数时,我们需要计算输入数字的平方根并显示结果至屏幕。
在实现科学计算功能时,我们还需要考虑一些特殊情况。例如,当用户输入的数字为负数时,平方根函数将返回一个复数。我们需要处理这种情况并给出相应的提示信息。
需要编写单元测试用例,确保每个功能都正常工作。我们可以使用Qt自带的测试框架QTest来编写单元测试用例。例如,我们可以测试加法、减法、乘法、除法以及相关的科学运算等功能的正确性。
通过编写的相关的函数来实现历史记录的功能,并在图形界面上附加当前时间的显示功能以及开发日志及提示等功能,使得该计算器的整体功能更加完善。
📌 接口设计和实现过程
🔑 主要的类和函数接口展示
在实现科学计算器的编程过程中,主要由以下几个类和函数接口类实现的。
- MainWindow类 :MainWindow类是科学计算器程序中的一个主窗口类,它提供了一个应用程序的主窗口,其中包含了计算器的主要界面和相关按钮。MainWindow类同时会处理具体的用户操作并通过对应的反应函数做出相应的响应。
- Dailog类:实现了开发日志和帮助文档的ui界面等。
- CalObj类:实现了计算器的实例类,通过结合MainWindow类中的相关内容,实例化出具体的计算器类,实现了具体的计算函数和实现方法,同时也方便后续进一步的单元测试相关的内容。
- CalTest类:通过编写该类,通过实例化CalObj类,能够调用QT自带的测试框架QTest来对该科学计算器进行单元测试,从而进一步提高程序的可靠性。
- bepexpression.h:该头文件主要包含了检验表达式正确性,格式化表达式、通过逆波兰表达式等原理来进行计算器的计算等函数实现。
- function.h:该头文件主要通过将内置的数学科学计算函数进行整合以及优化,进一步方便其他模块函数的调用和处理。
🔑 MainWindow类
上图是对MainWindow类的一个总体框架的分析,通过对整体的分析,可以将MainWindow类中的函数分为三类:
- 处理相关数字按钮信号(例如:输入1~9,π,e等)
- 处理相关操作按钮信号(例如:输入+、-、*、÷等操作按钮)
- 其他操作函数:例如更新时间、清除历史记录等
① 以on_button0_clicked()为例,展示处理相关数字按钮信号的关键代码:
void MainWindow::on_button0_clicked() { ui->textDisplay->insert("0"); }
② 以on_buttonAdd_clicked()为例,展示简单四则运算操作的关键代码,该模块直接将相关的操作符载入缓存区中,为后续的计算做好准备:
void MainWindow::on_buttonAdd_clicked() { ui->textDisplay->insert("+"); }
③ 以on_buttonLn_clicked()为例,展示科学运算过程中所需要的关键代码,参考Windows自带的科学计算器以及本项目的具体编写,本文进行这样的设定:计算器分为两种模式,分别为普通模式和函数模式。且两种模式不能混用,即普通算式中不能出现函数,函数式中不能出现运算符。这样的设定也符合系统自带计算器的相关操作。
在下面的函数模块中,我们通过一个flag标志来识别此时计算器处于哪一种计算模式,如果出现了计算模式的混用,则会对系统进行报错,并对用户进行提醒"此函数暂不支持混合运算!"。
void MainWindow::on_buttonLn_clicked() { if (flag != EMPTY) { ui->statusbar->showMessage("此函数暂不支持混合运算!", 3000); return; } ui->textDisplay->insert("ln()"); flag = LN; moveCursor(); ui->textDisplay->setFocusPolicy(Qt::NoFocus); }
④ on_buttonEqual_clicked(),这个类的重点函数实现,在这个函数中实现了对于表达式的计算操作,首先先判断计算模式是函数模式还是普通模式:如果是函数模式的话通过对于不同的科学操作函数进行不同的操作来得到最后返回的结果;如果是普通模式的话,通过bepexpression.h中的相关函数来对算术表达式进行检验,判断其是否合法,然后通过slove函数对其进行计算,最后得到返回结果并清空相关内容,以便下一次进行计算。
特点:在这个函数中,我们通过在类中定义一个 enum functionSituation 来对不同的计算操作进行标识,从而方便了后续代码的编写。同时,通过捕捉 errorCode 来针对不同的错误情况对用户进行提示,提高了软件的功能性。
void MainWindow::on_buttonEqual_clicked() { double answerNumber = 0; QString qExpression = ui->textDisplay->text(); string stdExpression = qExpression.toStdString(); //转化为c++的string if (flag != EMPTY) //函数模式 { // enum functionSituation{EMPTY = 0,SQUARE,POWER,SQRT,LG,LN,FAC,SIN,COS,TAN,NUM}; switch (flag) { case SQUARE: { //将形似于x^2的表达式中的x提取出来 QString qNumber = qExpression; qDebug() << qNumber << endl; qNumber.chop(2); // 去除尾部两个数字,留下所需数字 double number = qNumber.toDouble(); answerNumber = getPow(number, 2); break; } case POWER: { //将形似于x^(y)的表达式中的x提取出来 int Length = qExpression.length(); int position = qExpression.indexOf("^"); //分离出底数和指数 QString qBaseNumber = qExpression; qBaseNumber.chop(Length - position); double baseNumber = qBaseNumber.toDouble(); QString qPowNumber = qExpression.mid(position + 2); qPowNumber.chop(1); double powNumber = qPowNumber.toDouble(); answerNumber = getPow(baseNumber, powNumber); break; } case SQRT: { //将形似于√(x)的表达式中的x提取出来 QString qNumber = qExpression.mid(2); qNumber.chop(1); double number = solve(qNumber.toStdString()); answerNumber = getSqrt(number); break; } case LG: { //将形似于lg(x)的表达式中的x提取出来 QString qNumber = qExpression.mid(3); qNumber.chop(1); double number = solve(qNumber.toStdString()); answerNumber = getLg(number); break; } case LN: { //将形似于ln(x)的表达式中的x提取出来 QString qNumber = qExpression.mid(3); qNumber.chop(1); double number = solve(qNumber.toStdString()); answerNumber = getLn(number); break; } case FAC: { //将形似于x!的表达式中的x提取出来 QString qNumber = qExpression; qNumber.chop(1); double number = solve(qNumber.toStdString()); answerNumber = getFac(number); break; } case SIN: { //将形似于sin(x)的表达式中的x提取出来 QString qNumber = qExpression.mid(4); qNumber.chop(1); double number = solve(qNumber.toStdString()); answerNumber = getSin(number); break; } case COS: { //将形似于cos(x)的表达式中的x提取出来 QString qNumber = qExpression.mid(4); qNumber.chop(1); double number = solve(qNumber.toStdString()); answerNumber = getCos(number); break; } case TAN: { //将形似于tan(x)的表达式中的x提取出来 QString qNumber = qExpression.mid(4); qNumber.chop(1); double number = solve(qNumber.toStdString()); answerNumber = getTan(number); break; } } QString answerString = QString::number(answerNumber, 'g', 6); //重置光标位置和标志,以进行下一次运算 ui->textDisplay->setCursorPosition(ui->textDisplay->text().size()); ui->textDisplay->insert("=" + answerString); flag = EMPTY; //记录历史信息 ui->historyDisplay->append(ui->textDisplay->text()); ui->statusbar->showMessage("记得Clear再进行下一次运算哦~", 3000); } else //普通算式模式 { try { if (!analysis(stdExpression.c_str())) //尝试判断表达式是否合法 { answerNumber = solve(stdExpression); // 用这个参数接返回的计算值 QString answerString = QString::number(answerNumber, 'g', 6); //重置光标位置和标志,以进行下一次运算 ui->textDisplay->setCursorPosition(ui->textDisplay->text().size()); ui->textDisplay->insert("=" + answerString); flag = EMPTY; //记录历史信息 ui->historyDisplay->append(ui->textDisplay->text()); ui->statusbar->showMessage("记得Clear再进行下一次运算哦~", 3000); } } catch (int errorCode) { switch (errorCode) { case 1: ui->statusbar->showMessage("运算符位置不正确,请检查后重试",3000); break; case 2: ui->statusbar->showMessage("左右括号不匹配,请检查后重试", 3000); break; case 3: ui->statusbar->showMessage("存在不合法字符,请检查后重试", 3000); break; } } } }
⑤ 相关的QT连接函数以及时间功能的实现
void MainWindow::timerUpdate() { QDateTime time = QDateTime::currentDateTime(); showtime->setText(time.toString("yyyy-MM-dd hh:mm:ss dddd")); } void MainWindow::clearHistory() { ui->historyDisplay->clear(); } void MainWindow::dealMenuBar() { //连接按键退出槽 connect(ui->actionExit, &QAction::triggered, this, &MainWindow::close); //连接历史记录清空槽 connect(ui->actionClearHistory, &QAction::triggered, this, &MainWindow::clearHistory); //连接对话框打开槽 connect(ui->actionAbout, &QAction::triggered, &myDialog, &QDialog::open); }
🔑 CalObj类
enum functionSituation
{
EMPTY = 0, //空
SQUARE = 1, //平方
POWER = 2, //幂
SQRT = 3, //平方根
LG = 4, // 10为底的对数
LN, // e为底的对数
FAC, //阶乘
SIN, //正弦
COS, //余弦
TAN, //正切
NUM = 10 //普通数字计算
};
CalObj类主要是用来对一个计算器进行实例化操作,方便后面进行单元测试。
其中的Func()函数也是通过实话化不同的计算器对象来得到最后的返回值,以便和测试用例的最终结果进行比较。
🔑 CalTest类
该类是在实例化CalObj类对象的基础上,利用Qt自带的QTest测试框架来对该项目进行单元测试。共编写了11个测试样例,分别针对平方、幂方、log、ln、阶乘等操作进行测试,具体的测试结果见下文所示。
部分测试函数的关键代码如下:
void CalTest::case1_squere()
{
CalObj c("2^2", 1);
//std::cout << c.Func() << std::endl;
QVERIFY(c.Func() == 4);
}
🔑 bepexpression.h
这个模块主要实现了后台处理表达式的相关操作,包括以下几个函数:
- int analysis(const char *expression) :检验算术表达式的合法性
- string format(string str):处理负号开头的表达式,处理方法是在前面加个0
- int comparePriorities(char c):比较运算符优先级
- vector<string> Infix2Suffix(string str):中缀表达式转后缀
- double result(vector<string> suffixExpression):计算后缀表达式结果
- double solve(string str):调用前几个函数,相当于main函数,一步到位求解表达式
接下来介绍一下几个重要的函数,首先是int analysis(const char *expression),这个函数是从来检查算术表达式的合法性的,如果所检验的算术表达式是不合法的话,会抛出对应的异常,有如下几种异常异常代码: 1,运算符位置不正确;2,左右括号不匹配 ;3,存在不合法字符。
如果最后验证是一个正确的表达式的话,则会返回整数0。
具体的关键函数实现如下:
int analysis(const char *expression) { int opt = 0; int length = strlen(expression); bool isNumberEnd = false; int numberBracket = 0; //括号总数,检测到左括号就+1,检测到右括号就-1 for (int i = 0; i < length;) { if (expression[i] >= '0' && expression[i] <= '9') { if (isNumberEnd) throw 1; isNumberEnd = true; //此时检测到数字,所以置为真,因为下面要把连续的数字吃掉 while ((expression[i] >= '0' && expression[i] <= '9') || expression[i] == '.') ++i; } //数字吃掉了,轮到运算符 char const *sop = "(+-*/)"; opt = strchr(sop, expression[i++]) - sop; //计算运算符优先级,从左到右优先级依次增高 if (opt < 0 || opt > 6) throw 3; if (opt == 0) //匹配到左括号 { if (isNumberEnd) //此时括号数应为0,否则为错 throw 1; numberBracket++; } if (opt == 5) //匹配到右括号 { if (numberBracket <= 0) //如果是右括号,那么nbracket必大于0 throw 2; numberBracket--; } if (opt > 0 && opt < 6 && !isNumberEnd) //运算符正确但后面没有数字 throw 1; if (opt > 0 && opt < 5) //匹配到+-*和/,数字末端置false isNumberEnd = false; } if (numberBracket) //括号不匹配 throw 2; if (!isNumberEnd) //运算符缺少运算数 throw 1; return 0; }
接下来是vector<string> Infix2Suffix(string str),这个函数主要实现了中缀表达式转后缀的相关操作,大致流程如下:
- 初始化一个空栈(用于存储操作符)。
- 从左到右遍历中缀表达式的每个字符。
- 如果遇到操作数,直接输出。
- 如果遇到操作符(如+、-、*、/),则将其与栈顶的操作符进行比较。如果当前操作符的优先级高于栈顶操作符,将当前操作符压入栈;否则,将栈顶操作符弹出并输出,直到遇到优先级低于或等于当前操作符的操作符或栈为空,然后将当前操作符压入栈。
- 如果遇到左括号(,将其压入栈。
- 如果遇到右括号(,则将栈顶的操作符弹出并输出,直到遇到左括号,然后将左括号弹出。
- 遍历完中缀表达式后,将栈中剩余的操作符依次弹出并输出。
最后实现的关键代码如下:
vector<string> Infix2Suffix(string str) { vector<string> suffixExpression; //存储后缀表达式 stack<char> operatorStack; for (int i = 0; i < str.length(); i++) { string temp = ""; switch (str[i]) { // case '+': case '-': case '*': case '/': { if (operatorStack.empty() || operatorStack.top() == '(') { operatorStack.push(str[i]); } else { //栈不为空且栈顶元素优先级大于这个位置的运算符,优先级大的先入栈 while (!operatorStack.empty() && comparePriorities(operatorStack.top()) >= comparePriorities(str[i])) { temp += operatorStack.top(); suffixExpression.push_back(temp); operatorStack.pop(); temp = ""; } operatorStack.push(str[i]); } break; } case '(': { operatorStack.push(str[i]); break; } case ')': { while (operatorStack.top() != '(') { temp += operatorStack.top(); suffixExpression.push_back(temp); operatorStack.pop(); temp = ""; } operatorStack.pop(); break; } //除去+-*、/和括号,剩下的就是数字 default: { if ((str[i] >= '0' && str[i] <= '9')) { temp += str[i]; //若是连续数字,若不是则跳过而直接放入逆波兰表达式 while (i + 1 < str.size() && str[i + 1] >= '0' && str[i + 1] <= '9' || str[i + 1] == '.') { temp += str[i + 1]; ++i; } suffixExpression.push_back(temp); } } } } while (!operatorStack.empty()) { string temp = ""; temp += operatorStack.top(); suffixExpression.push_back(temp); operatorStack.pop(); } return suffixExpression; }
最后是double result(vector<string> suffixExpression),这个函数将通过上述方法得到的后缀表达式进行计算从而得到最后的结果,大致流程如下:
- 从左到右遍历后缀表达式的每个字符。
- 如果遇到操作数,将其压入栈中。
- 如果遇到操作符,从栈中弹出两个操作数,进行相应的运算,然后将结果压回栈中。
- 遍历完后缀表达式后,栈中剩余的元素即为计算结果。
具体实现的关键代码如下:
double result(vector<string> suffixExpression) { stack<double> suffixStack; //后缀表达式计算栈 double num, op1, op2; //定义数字、操作数1和2 for (int i = 0; i < suffixExpression.size(); i++) { string temp = suffixExpression[i]; if (temp[0] >= '0' && temp[0] <= '9') { // c_str()函数是将string转化为标准char*,atof()是将字符串转化为浮点数 num = atof(temp.c_str()); suffixStack.push(num); //数字压入栈 } else if (suffixExpression[i] == "+") { //因为是后缀表达式,所以先出栈的是操作数2,后出来的是操作数1 op2 = suffixStack.top(); //取栈顶 suffixStack.pop(); //栈顶出栈 op1 = suffixStack.top(); suffixStack.pop(); suffixStack.push(op1 + op2); } else if (suffixExpression[i] == "-") { op2 = suffixStack.top(); suffixStack.pop(); op1 = suffixStack.top(); suffixStack.pop(); suffixStack.push(op1 - op2); } else if (suffixExpression[i] == "*") { op2 = suffixStack.top(); suffixStack.pop(); op1 = suffixStack.top(); suffixStack.pop(); suffixStack.push(op1 * op2); } else if (suffixExpression[i] == "/") { op2 = suffixStack.top(); suffixStack.pop(); op1 = suffixStack.top(); suffixStack.pop(); suffixStack.push(op1 / op2); } } return suffixStack.top(); }
最后通过double solve(string str)函数来将上述的操作进行一个集成,提高整体代码的封装性。
double solve(string str) { str = format(str); vector<string> suffixExpression = Infix2Suffix(str); double k = result(suffixExpression); return k; }
🔑 function.h
这个模块主要是将一些常用的科学计算函数进行封装,从而提高代码的简洁程度以及减少不同模块代码之间的耦合性。
以其中一个函数为例来展示关键代码:
double getSin( double number )
{
double answer = 0;
// 计算弧度
double radian = ( number * 3.1415926 ) / 180.0;
answer = sin( radian );
return answer;
}
✅ 关键功能展示
👓 四则运算基础功能实现
👓 科学运算功能展现
👓 复合运算功能展现
⏰性能改进与优化
🏳🌈 时间显示功能
为了能够更好的提高软件的界面美观程度,从实用性的角度出发,我考虑在程序的右下角添加了一个时间显示的框框,在一定程度上能够是程序变得更加具有完备性。
以下关键代码用以获取实时时间来展示:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
flag = EMPTY;
showtime = new QLabel(this);
//更新时间,在statusbar右边显示,1000ms刷新一次
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &MainWindow::timerUpdate);
timer->start(1000);
ui->statusbar->addPermanentWidget(showtime);
dealMenuBar();
}
void MainWindow::timerUpdate()
{
QDateTime time = QDateTime::currentDateTime();
showtime->setText(time.toString("yyyy-MM-dd hh:mm:ss dddd"));
}
实现效果如下:
🏳🌈 历史记录及清空操作
在一个功能较为完善的计算器中,需要有历史记录功能和清空操作,通过以下关键代码来实现这些功能:
//记录历史信息
ui->historyDisplay->append(ui->textDisplay->text());
ui->statusbar->showMessage("记得Clear再进行下一次运算哦~", 3000);
void MainWindow::dealMenuBar()
{
//连接按键退出槽
connect(ui->actionExit, &QAction::triggered, this, &MainWindow::close);
//连接历史记录清空槽
connect(ui->actionClearHistory, &QAction::triggered, this,
&MainWindow::clearHistory);
//连接对话框打开槽
connect(ui->actionAbout, &QAction::triggered, &myDialog, &QDialog::open);
}
具体的实现可参考上述关键内容展示中的GIF。
🏳🌈 帮助文档以及ui界面美化
在实现了基础的功能的基础上,作者希望能够通过进一步美好用户的操作界面已经相关的帮助操作来带给用户更加优越的使用体验,在这种想法的基础上,作者增加了帮助文档这一操作选项以及对原有的ui界面进行进一步的美化。
💊 单元测试
在上述的内容中,本文已经提到了通过构建CalTest类和CalObj类来实例化计算器对象,从而能够正确调用QT自带的QTest库中的测试相关函数,作者共设置了11个测试样例,涵盖了所有可能发生的情况和操作,具体的结果展示如下:
可以看到,通过了所有的测试,证明该程序在功能上具有一定的科学性和完备性,能够满足正常的使用需求。
🚩 异常处理措施与展示
对于用户在实际应用的过程中出现的相关非法操作,程序会通过相关提示来告知用户进行清空或者其他操作来重新进行运算:
出现左右括号不匹配的情况:
出现错误的使用相关运算符的情况:
出现÷0的情况,并不会直接报错,而是通过输出inf来提示用户:
出现次方或幂方的运算无底数的情况:
出现不完整的算式:
还有其他的异常情况,本文列举了上述几种典型的情况来作为说明,同时,也还有作者所遗漏的一些情况需要改进,将在未来的后续版本中进行进一步的更新。
🎵 心得体会
在个人成长方面:这次的编程任务对我来说是一次非凡的体验。从最开的选择语言,对qt相关语言的学习,到后面自己慢慢去配置环境,去查阅相关的资料,看别人的博客和代码,学习优秀的经验,接着是对项目的不断打磨和优化,出现了bug的时候需要针对不同的问题一步步的去解决相关的问题,到最后的单元测试和性能测试,让我对于一个软件整体的生命流程有了进一步的认识,知道了一款好的产品是应该不断出来的,不断的测试,在测试中去优化相关的功能和用户体验;最后是对于程序的打包,通过windeployqt.exe,一款Qt自带的工具,用于创建应用程序发布包,将某程序依赖的库、资源拷贝到其所在目录,防止程序在其他电脑上运行报找不到库的错误,在这个过程中我对于一个项目的发布又有了进一步的体验和收获。
在技术方面:通过使用Qt编程实现了一个科学计算器,在这个过程中,我学到了很多关于GUI设计和事件处理的知识,同时也深刻理解了面向对象编程的思想。例如:我学会了如何使用Qt Designer来设计GUI界面。通过拖拽和调整控件的位置和大小,我可以轻松地创建出符合需求的界面。同时,我也学会了如何使用信号和槽机制来实现各个控件之间的交互,这大大提高了我的编程效率。同时,在实现科学计算器的过程中,我也深刻理解了面向对象编程的思想。我将整个计算器看作是一个对象,将各种功能封装成不同的方法,然后通过调用这些方法来实现计算器的完整功能。这种思想不仅可以应用到科学计算器的开发中,也可以应用到其他程序的开发中。
总而已知,实践出真知,通过这次的实践任务,让我完整的体验了一次项目的开发流程,这也符合软件工程这门课的目标所在,希望在接下来的课程中能够不断提高自己的能力,开发出更好的项目产品。