当前位置:   article > 正文

C++ QT 课程设计:科学计算器_qt科学计算器

qt科学计算器

cug chx

目录

一 课程设计题目与要求

二 需求分析

2.1 用户需求

2.2 功能性需求

2.3 非功能性需求

2.4 运行环境

三 概要设计

3.1 流程设计

3.2 交互设计

3.3 底层设计

四 详细设计

4.1 界面设计

4.2 计算功能

4.3 历史记录

4.4 自动纠正

4.5 安全检查

4.6 错误检查

4.7 键盘输入

五 测试

5.1 基础功能测试

5.2 自动纠正测试

5.3 安全检查测试

4.4 错误检查测试

5.5 键盘输入测试


课程设计题目与要求

        仿照Windows系统的计算器软件,如图1.1所示,为教材第12.4节通用计算器设计界面,开发一款实用的计算器软件,并按照实验指导书附录中课程设计报告模板要求撰写结课报告。

0a8d8b27c8e84d8d9314edcb21cf0b0e.png

图1.1 Windows系统的计算器界面

需求分析

2.1 用户需求

        (1)设计一套算法,实现常数数学表达式的混合计算,遵循运算优先级顺序,支持小括号。

        (2)提供一套UI界面,使用户能够方便地输入表达式,直观地获得计算结果。

2.2 功能性需求

        (1)数学运算功能。包括但不限于四则运算、三角函数、幂、对数的数学运算;支持整形和浮点型运算;支持输入小括号改变运算顺序。

        (2)输入输出功能。支持屏幕按钮和键盘两种输入方式,支持删除、清除功能;提供计算历史记录,和历史记录清除功能。

        (3)结果调用功能。在进行sin、cos等函数运算时,允许用户直接调用上一次的运算结果,进行函数运算。

2.3 非功能性需求

        (1)自动纠正功能。用户在输入算式时若出现低级错误,如连续输入两个运算符;或出现符合书写习惯但不符合程序计算规则的错误,如数字和函数运算符之间省略乘号,程序应当自动进行纠正,以避免发生潜在的输入错误。

        (2)错误检查功能。对于分母为零、对负数求算数平方根、arcsin范围超出[-1, 1]等一些不符合约定的数学错误,程序应当检测出错误类型,并向用户发出错误警告。

        (3)安全检查功能。用户的输入是多种多样的、难以枚举的,当意料之外的非法输入时发生时,应当避免程序自身崩溃,同时向用户报告错误。

2.4 运行环境

        操作系统Windows 10;Qt Creator版本4.11.0;Qt版本5.14.1

三 概要设计

3.1 流程设计

        Qt基于信号与槽机制,因此程序总体上并非顺序执行的流程。在主函数中,完成应用程序QApplication和主要窗口MainWindow类的对象创建后,程序就进入事件循环,等待用户触发事件。程序框图如图3.1所示。

7ae1f051217b4a3c9a80c9ef053e6330.png

图3.1 主函数程序框图

3.2 交互设计

        用户在操作应用程序时,实际是与程序外层的mainwindow类进行信息交互,如图3.2所示。mainwindow是由QMainWindow、QPushButton、QTextBrowser、QLabel等一系列Qt提供UI设计类构成。借助Qt提供的类,程序员可以通过代码或Qt Designer很方便地创建和布局窗口,并通过信号和槽机制处理事件。

417082c94dcf4c83b87b7799ccd66031.png

  图3.2 程序交互设计示意图

        位于外层的mainwindow对用户输入的信息进行处理后,将其转化为底层operator、stack、factory类能够理解的形式,然后进行计算。因此,可以认为mainwindow起到了“桥梁”的作用,使得用户能够借助mainwindow类,以一种友好、可视化的方式与底层负责运算的类进行交流,用户只能看到图形化的界面,而内核部分对用户是屏蔽的。实际上,用户也无需知道底层的算法,这样面向对象的设计思想对于用户是友好的,对于开发者也是友好的。

3.3 底层设计

        除mainwindow类外,本应用程序底层中主要有operator、stack、factory三个类,其UML图如图3.3所示。

2711d4b226a14eeb976b0ac0a406b1c9.png

 图3.3 程序UML图

        为了提高计算程序的可读性、可维护性和可拓展性,本程序参考教材12.4节通用计算器,设计了Operator类对程序的逻辑关系进行了优化。Operator类存储了每一种运算的标签、目数和优先级,同时提供了计算函数getResult(),该函数是一个纯虚函数,需要在具体运算符的子类中实现。

        在程序中,所有运算符类都继承自Operator,重写getResult()函数以便实现不同的运算功能。当开发者增添、删改某种运算时,只需要对相应的运算符类进行修改,而不需要像实验8.2简易计算器一样修改if-else语句。对象工厂Factory类则是为了更加方便地创建运算符类。

四 详细设计

4.1 界面设计

        Qt允许程序员不通过任何设计工具,以纯粹的C++代码来设计界面,同时也提供了一个可视化的界面设计工具:Qt Designer。通过Qt Designer可以很方便地创建部件,修改部件属性,调整窗口布局。

        窗体的布局是十分重要的,只有灵活运用水平布局、垂直布局、栅格布局等方式将所有部件组合起来,并设置合理的尺寸策略(sizePolicy),才能在用户任意拖动窗口大小时,窗口内的部件随窗口尺寸一起缩放,且布局比例不变,保持美观。本程序的界面布局如图4.1所示。

38f0196a420946dba1cba70eed0bd2c8.png

 图4.1 窗口布局

        程序套用了CSS样式表来对界面外观进行风格化设置,并将图标设置为自己设计的logo,上述操作的代码如下:

  1. a.setWindowIcon(QIcon("./logo.ico"));
  2. QFile file(":/qss/psblack.css");
  3. file.open(QFile::ReadOnly);
  4. if(file.isOpen())
  5. {
  6. QString styleSheet = this->styleSheet();
  7. styleSheet += QLatin1String(file.readAll());
  8. this->setStyleSheet(styleSheet);
  9. }

        最终呈现出的界面如图4.2所示。

d59862311b03488aa857e80a4695e168.png

 图4.2 计算器界面效果图

4.2 计算功能

        每一个按键的clicked信号与对应的槽函数绑定,程序在槽函数中执行相关操作,或将运算符、数字追加到计算式qstr中。当用户按下等号键时,表示一串计算式输入完毕,程序将调用doIt( )函数一次性计算完整个算式。

        在doIt( )函数中,通过for循环遍历计算式exp,依次判断并读取计算式中的数字和运算符,分别压入数字栈和符号栈。程序代码如下:

  1. for(auto it=exp.begin(); it!=exp.end();)
  2. {
  3. //如果是数字
  4. if (isNum(it))
  5. m_num.push(std::move(readNum(it)));
  6. //如果不是数字
  7. else
  8. {
  9. //处理含字母的运算符(sin,cos,ect.)
  10. if(isSig(it))
  11. oo = Factory::create(readSig(it));
  12. //处理单字符运算符(+,-,*,ect.)
  13. else
  14. {
  15. string temp;
  16. temp += *it++;
  17. oo = Factory::create(temp);
  18. }
  19. //如果当前运算符比栈顶优先级低, 执行计算
  20. if(oo->symbol()!="(")
  21. {
  22. while (oo->precedence() <= m_opr.top()->precedence())
  23. {
  24. if (m_opr.top()->symbol() == "#")
  25. break;
  26. calculate();
  27. }
  28. }
  29. //运算符入栈,等号除外
  30. if (oo->symbol() != "=")
  31. m_opr.push(std::move(oo));
  32. }
  33. }

        当遍历到运算符时,比较当前运算符与符号栈栈顶运算符的优先级,若当前运算符优先级更低,则调用calculate( )函数对栈顶运算符执行一步计算,然后将当前运算符入栈。

        在calculate中,若数字栈非空,则根据运算符的目数从数字栈中弹出相应数量的数字,调用该运算符的getResult( )方法进行计算,并将计算结果再次压入数字栈。该运算符的所有计算操作至此结束,将其弹出符号栈丢弃。

        特别地,当符号栈栈顶为运算符"#"时,证明符号栈中已经没有运算符,直接将当前遍历到的运算符入栈即可,不执行计算。上述操作的代码实现如下:

  1. void MainWindow::calculate()
  2. {
  3. double a[2] = {0,0};
  4. for(auto i=0; i<m_opr.top()->numOprand(); ++i)
  5. {
  6. if(!m_num.empty())
  7. {
  8. a[i] = m_num.top();
  9. m_num.pop();
  10. }
  11. }
  12. m_num.push(std::move(m_opr.top()->getResult(a[1], a[0])));
  13. m_opr.pop();
  14. }

        在整个计算式遍历结束后,所有计算也已经结束,结果置于数字栈栈顶,可以将其取出并通过UI展示给用户。

4.3 历史记录

        历史记录栏保存了用户输入的所有有效计算式与其计算结果。每次计算结束后,调用showText的setText( )方法刷新显示计算结果栏的同时,调用historyText的append( )方法,将相同的内容追加到历史记录栏,这样就用简单的方法实现了历史记录功能。

  1. ui->showText->setText(showstr);
  2. ui->historyText->append(showstr);

        按下C History按钮,触发槽函数,以清空历史记录栏historyText的内容。

  1. void MainWindow::clickedclcH()
  2. {
  3. ui->historyText->clear();
  4. }

4.4 自动纠正

        自动纠正旨在帮助用户在输入时实时纠正一部分简单的公式错误。这里主要考虑了两种情况。

        第一种情况,符合书写习惯但违背计算机规则的错误。在书写公式时,我们约定,常数与sin、log等函数运算符相乘,之间的乘号可以省略,但这会给程序带来错误。自动纠正功能将在用户输入函数运算符时,检测前一个输入的字符是否为数字,若为数字,自动补全乘号。以运算符"sin"为例,实现代码如下:

  1. auto lastInput = showstr.toStdString().end()-1;
  2. if(isNum(lastInput))
  3. {
  4. qstr += "*sin";
  5. showstr += "*sin";
  6. lastQstrLength = 4;
  7. lastShowstrLength = 4;
  8. }
  9. else
  10. {
  11. qstr += "sin";
  12. showstr += "sin";
  13. lastQstrLength = 3;
  14. lastShowstrLength = 3;
  15. }

        第二种情况,不符合规则的低级错误。计算式中不应连续出现两个双目运算符,例如"+-"。自动纠正功能将在每次输入双目运算符后,将lastQstrLength、lastShowstrLength置为输入运算符在qstr和showstr中所占的字符长度。

        而在每次输入输入双目运算符之前,判断lastQstrLength、lastShowstrLength是否非零,若非零,则证明上一个输入已经是双目运算符,那么将在qstr、showstr中分别删除lastQstrLength、lastShowstrLength长度的字符,以将上一次输入的双目运算符删除,替换为新输入的运算符。

        以"+"为例,代码实现如下:

  1. void MainWindow::clickedadd()
  2. {
  3. if(lastQstrLength!=0)
  4. {
  5. qstr.chop(lastQstrLength);
  6. showstr.chop(lastShowstrLength);
  7. }
  8. qstr += "+";
  9. showstr += "+";
  10. lastQstrLength = 1;
  11. lastShowstrLength = 1;
  12. ui->showText->setText(showstr);
  13. }

4.5 安全检查

        尽管程序已经具有了基本的自动纠正功能,但仍然无法避免一些意料之外的非法输入产生,在遇到非法输入时,安全检查机制可以避免程序崩溃,并及时向用户报告错误。

        本程序中,导致程序崩溃的原因是用户连续输入了两个及以上运算符,形成了不符合数学约定的非法运算符组合,例如sincos,导致在使用Factory创建运算符时出错。

        C++提供了map::find( )方法,若在ms_operator中找不到所给定的key,则返回一个指向ms_operator末尾的迭代器。利用这个特性,若ms_operator.find(opr)返回值不等于ms_operator.end( ),则证明opr是已注册的运算符,返回正确的指针;否则证明用户输入的opr是非法字符,返回空指针nullptr。

  1. static unique_ptr<Operator> create(string opr)
  2. {
  3. auto it = ms_operator.find(opr);
  4. if (it != ms_operator.end())
  5. return it->second();
  6. else
  7. return nullptr;
  8. }

        当程序检测到create( )函数返回了nullptr,即输入不合法,通过QMessageBox创建警告窗口上报用户,调用一次清除键clc的槽函数,将显示缓存和数据栈复位,然后使用return结束本次计算操作。至此,安全检查成功避免了程序崩溃,同时对数据和程序执行了复位操作。

  1. if(isSig(it))
  2. {
  3. oo = Factory::create(readSig(it));
  4. //若创建运算符失败,则返回空指针
  5. if(oo==nullptr)
  6. {
  7. QMessageBox::warning(NULL,"输入错误","输入不合法,请检查算式");
  8. clickedclc();
  9. return 0;
  10. }
  11. }

4.6 错误检查

        错误是指某些违背数学约定,可能会导致结果未被定义,或出现歧义,但并不会导致程序崩溃的数学性错误。例如分母为0、对负数取平方根、arctan超出范围、数字无穷大等情况。

        C++标准库提供了十分完善的方式处理这些数学错误。若计算返回值为nan,说明出现了未定义的数学运算,或未定义的结果;若返回值为inf,说明计算结果为无穷大。通过isnan( )和isinf( )函数,即可对以上两种情况作出判断,然后通过QMessageBox创建警告窗口上报用户。

  1. if(isnan(result))
  2. QMessageBox::warning(NULL, "运算错误","存在数学错误,运算结果未定义");
  3. else if(isinf(result))
  4. QMessageBox::warning(NULL, "运算错误","存在数学错误,运算结果无穷大");

4.7 键盘输入

        对于数字、加减乘除、等于、删除、清除等使用频率非常高的按键,使用键盘直接输入可以大大提高效率。

        重写keyPressEvent事件的槽函数,在槽函数内使用switch语句将按键与对应UI界面上按钮的槽函数绑定,等效于每次按下键盘时,触发了一次对应按钮的槽函数,即可实现键盘输入。实现代码如下:

  1. void MainWindow::keyPressEvent(QKeyEvent *ev)
  2. {
  3. switch(ev->key())
  4. {
  5. case(Qt::Key_Backspace):
  6. clickedback();
  7. break;
  8. case(Qt::Key_Delete):
  9. clickedclc();
  10. break;
  11. case(Qt::Key_Return):
  12. clickedEqu();
  13. break;
  14. case(Qt::Key_0):
  15. clicked0();
  16. break;
  17. case(Qt::Key_1):
  18. clicked1();
  19. break;
  20. ······
  21. }
  22. }

五 测试

5.1 基础功能测试

        点击屏幕上的按钮输入算式,按"←"键删除一个数字或运算符,按"C"键清除输入,按"="键得出计算结果,本次计算自动添加至历史记录栏,按"C history"键清除历史记录栏。经检验,计算结果正确,如图5.1所示。

d6dc82e9b3994fb29d9b4753f28aeaeb.png

 图5.1 基础功能测试

5.2 自动纠正测试

        依次点击"1"、"-"、"+"、"2"、"√"、"4"、"=",得到如图5.2所示结果。可以看到,在依次按下"-"、"+"时,输入被自动修正为"+";在依次按下"2"、"√"时,输入被自动修正为"2*√"。最终计算结果正确。

a39d26f964f84c179f292e9f64a5af5c.png

 图5.2 自动纠正测试

5.3 安全检查测试

        输入一个不合法的算式,点击"=",程序报告“输入不合法,请检查算式”,如图5.3所示。点击“OK”关闭警告窗口后,输入算式被自动清空。

32b5cb82ef5e4e0ca4137f8feb0ce2e6.png

图5.3 安全检查测试

4.4 错误检查测试

        输入一个数除以零,程序报告“存在数学错误,运算结果无穷大”,如图5.4所示。点击“OK”关闭警告窗口后,输入算式被自动清空。

3b4d4b5ae8ef4f95ade8af7378f5c42f.png

 图5.4 错误检查测试1

        对一个负数求平方根,程序报告“存在数学错误,运算结果未定义”,如图5.5所示。点击“OK”关闭警告窗口后,输入算式被自动清空。

38b1d5b7257b484aa5dbc302806091d4.png

 图5.5 错误检查测试2

5.5 键盘输入测试

        键盘输入支持数字键输入"0"~"9";退格键删除字符;delete或ESC键清空输入;enter键计算结果;"+"、"-"、"*"、"/"、"%"、"^"、"("、")"、"!"运算符的输入。测试结果如图5.6所示。

01c5f60c730749a6bb61a0d0f603c629.png

 图5.6 键盘输入测试

完整工程下载地址:https://download.csdn.net/download/yul13579/53395197

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/890992
推荐阅读
相关标签
  

闽ICP备14008679号