目录
作业班级
作业要求
项目源码地址
作业目标
通过GUI实现一个具有可视化界面的科学计算器
作业过程
界面以及功能展示
P2P表格
P2P | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 30 |
• Estimate: | • 估计这个任务需要多少时间 | 5 | 5 |
Development | 开发 | 580 | 480 |
• Analysis | • 需求分析 (包括学习新技术) | 60 | 30 |
• Design Spec | • 生成设计文档 | 15 | 15 |
• Design Review | • 设计复审 | 15 | 15 |
• Coding Standard: | • 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
• Design | • 具体设计 | 30 | 30 |
• Coding | • 具体编码 | 100 | 100 |
• Code Review: | • 代码复审 | 25 | 20 |
• Test | • 测试(自我测试,修改代码,提交修改) | 30 | 20 |
Reporting | 报告 | 60 | 40 |
• Test Repor: | • 测试报告: | 5 | 5 |
:• Size Measurement | • 计算工作量 | 10 | 10 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 10 | 10 |
合计 | 985 | 820 |
解题思路描述
个人对python语言比较熟悉,所以这次作业采用python语言来进行编写科学计算器,在作业过程中主要问题如下,本次使用了第三方库PyQt5,运行前请先在终端运行
pip install PyQt5 -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
pip install sympy -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
问题1
本次计算功能主要委托给python自带的eval函数来处理,但是eval函数无法处理想sin这样的三角函数或是 ^幂次运算符号,在
问题2
一次性清除显示界面内的内容很容易,但是如果只是清除用户上一步的操作,要避免在清除三角函数时只清除部分字母
问题3
在括号处理方面,要判断每一个正括号有没有对应的反括号,并且正确地处理括号匹配
核心代码展示
def calt_final(self, the_cal):
final_cal = ''
flag = 0
i = 0
flag1 = 0
flag2 = 0
while True:
if the_cal[i] not in num_and_op:
if the_cal[i] == '^':
p = 0
k = i
the_new = ''
while True:
k -= 1
if k == -1 or the_cal[k].isdigit() == False:
break
else:
the_new = the_cal[k] + the_new
p = k + 1
k = i
the_new1 = ''
while True:
k += 1
if k == len(the_cal) or the_cal[k].isdigit() == False:
break
else:
the_new1 = the_cal[k] + the_new1
t = pow(float(the_new), float(the_new1))
t = str(t)
final_cal += the_cal[flag:p] + t
i = k - 1
flag = k
for j in range(i + 3, len(the_cal)):
if the_cal[j] == '(' and flag2 == 0:
flag1 = j
flag2 = 1
stack.append('(')
elif the_cal[j] == '(':
stack.append('(')
elif the_cal[j] == ')':
stack.pop()
if len(stack) == 0:
flag2 = 0
the_new_cal = the_cal[flag1 + 1:j]
t = self.calt_final(the_new_cal)
if the_cal[i] != 'a':
if the_cal[i + 1] != 'q': # tri and sqrt\
t = math.radians(t)
if the_cal[i:i + 3] == tri_op[0]:
t = sy.sin(t)
elif the_cal[i:i + 3] == tri_op[1]:
t = sy.cos(t)
elif the_cal[i:i + 3] == tri_op[2]:
t = sy.tan(t)
elif the_cal[i:i + 4] == tri_op[6]:
print(1)
t = math.sqrt(t)
else:
if the_cal[i:i + 4] == tri_op[3]:
t = sy.asin(t)
elif the_cal[i:i + 4] == tri_op[4]:
t = sy.acos(t)
elif the_cal[i:i + 4] == tri_op[5]:
t = sy.atan(t)
t = math.degrees(t)
t = str(t)
final_cal += the_cal[flag:i] + t
i = j
flag = j + 1
break
i += 1
if i >= len(the_cal):
break
final_cal += the_cal[flag:len(the_cal)]
return eval(final_cal)
接口设计与实现过程
1. 用户界面设计:
这次作业中,用户界面使用PyQt5库创建。首先,创建了一个主窗口,设置了窗口的标题、图标和大小,并在窗口中创建了两个文本框(QLineEdit),一个用于显示输入表达式,另一个用于显示计算结果。并且创建了数字按钮、运算符按钮以及其他功能按钮,并使用网格布局将它们排列在界面上。
2. 按钮事件处理:
每个按钮都关联了一个点击事件处理函数。例如,数字按钮、运算符按钮和清除按钮都有相应的事件处理函数。在这些函数中,通过获取按钮的文本内容来处理用户的输入,然后将输入显示在表达式文本框中。
3. 数学表达式计算:
计算器的核心功能是解析和计算数学表达式。代码中使用了一个自定义的方法calt_final
来计算表达式。这个方法使用了一个栈来处理括号,并递归地计算表达式的值。在计算过程中处理了一些特殊的运算符,如三角函数和平方根。
4. 错误处理:
代码中也包含了错误处理部分,当用户输入无效的表达式或出现其他错误时,会在结果文本框中显示"错误"。
5. 用户体验和界面优化:
界面设计中考虑了用户体验,包括按钮的样式和布局。这有助于使应用看起来更吸引人,易于使用。
程序性能改进
- 在最初,处理计算所使用的
calt_final
里采用的是循环嵌套的方法,时间复杂度较高,之后改成采用递归的算法,降低了时间复杂度 - 一开始设计的时候,结果的输入和输出都显示在一个显示栏中,用户不方便对自己之前的操作进行观看,之后设计成输入和输出分开显示
- 程序一开始使用python的math库中的函数对三角函数进行计算,但测试过程中发现精度不够,例如sin30会等于0.4999999,之后换成了sys
单元测试
单元测试
单元测试部分代码
class TestCalculator(unittest.TestCase):
def test_cal(self):
result = cal.calt_final('2+2-3*4/4')
self.assertEqual(result, 1)
result = cal.calt_final('(2+(3*4))/8*2^2')
self.assertEqual(result,7)
result = cal.calt_final( 'sin(45)+sqrt(16)+cos(40)+tan(45)')
self.assertAlmostEqual(result , 6.473151224305525 )
result = cal.calt_final('1+5^2')
self.assertEqual(result, 26)
result = cal.calt_final('sqrt(sin(45))')
self.assertAlmostEqual(result, 0.8408964152537143)
result = cal.calt_final( 'asin(0.5)+acos(0.5)+atan(1)')
self.assertAlmostEqual(result, 2.356194490192345)
异常处理
try:
cal = self.result_display.text()
result = self.calt_final(cal)
self.result_display1.setText(str(result))
except:
self.result_display1.setText('错误')
若是遇到eval函数无法处理的式子,则抛出错误
心路历程和收获
这次作业过程中遇到了很多困难,尤其是算法的设计,在编写主要算法过程中我遇到了很多困难和bug,好在不断地优化下慢慢解决了一个个困难。
通过这次作业,我学会了P也Qt库函数地使用,对单元测试和覆盖率有了初步的认知,对GUI程序有了更深一步的学习。