作业链接
https://bbs.csdn.net/topics/617294583
Gitcode项目地址
102101436郑择杭 / homework11 · GitCode
PSP | Personal software process stages | 预估耗时(分钟re) | 实际耗时(分钟) | ||
Planning | 计划 | 20 | 20 | ||
Estimate | 估计这个任务需要多少时间 | 15 | 15 | ||
Development | 开发 | 50 | 60 | ||
analysis | 需求分析(包括学习新技术) | 20 | 25 | ||
Design spec | 生成设计文档 | 30 | 30 | ||
Design review | 设计复审 | 15 | 15 | ||
Coding standard | 代码规范(为目前的开发指定合适的规范) | 15 | 15 | ||
Design | 具体设计 | 100 | 110 | ||
Coding | 具体编码 | 200 | 240 | ||
Code review | 代码复审 | 80 | 90 | ||
Test |
| 60 | 60 | ||
Reporting | 报告 | 60 | 60 | ||
Test report | 测试报告 | 45 | 60 | ||
Size measurement | 计算工作量 | 10 | 10 | ||
Postmortem process improvement plan | 事后总结,并提出过程改进计划 | 20 | 20 | ||
Total | 合计 | 740 | 830 |
界面展示
解题思路描述
本次作业要求实现一个简易计算器,了解计算器的一些基本要求,决定使用Python来实现,主要有以下几个问题和步骤
1.设计用户界面:使用Python的GUI库(如Tkinter、PyQt等)创建一个计算器界面,包括数字按钮、运算符按钮和等号按钮等。
2.获取并解析输入:在界面中添加按钮事件处理函数,当用户点击数字按钮或运算符按钮时,将对应的数字或运算符添加到输入框中,再使用Python的字符串处理方法,将用户输入的表达式进行解析,提取出数字和运算符。
3.执行计算:编写·对应的代码,进行计算
实现过程以及关键代码展示
1.设计用户界面:使用Python的GUI库,Tkinter创建一个计算器界面,包括数字按钮、运算符按钮和等号按钮等。
import tkinter as tk
import tkinter.messagebox as messagebox
from abc import ABC, abstractmethod
from Exception import *
from functools import partial
from settings import *
from functions import *
if __name__ == '__main__':
calculator = tk.Tk()
width_cal = 280
height_cal = 300
width_win = calculator.winfo_screenwidth()
height_win = calculator.winfo_screenheight()
# title
calculator.title('calculator')
# icon
# calculator.iconbitmap('img/snapper.ico')
# color
calculator.background = 'blue'
calculator.resizable(0, 0)
# justify: show lines of text, define how to align,
# including LEFT, RIGHT ,CENTER
# row: entry's horizontal position in grid
# column: entry's vertical position in grid
# columnspan: merge many contiguous blocks in grid
entry = tk.Entry(calculator, justify='right', font=1)
entry.grid(row=0, column=0, columnspan=5, padx=0, pady=10)
def placeButton(text, func, func_params, bg=button_bg, **place_params):
# partial
my_button = partial(tk.Button, calculator, bg=button_bg, padx=10, pady=3,
activebackground=button_activate_bg, relief='raised')
button = my_button(text=text, bg=bg, command=lambda: func(*func_params))
button.grid(**place_params)
placeButton('7', get_input, (entry, '7'), row=1, column=0, ipadx=5, pady=5)
placeButton('8', get_input, (entry, '8'), row=1, column=1, ipadx=5, pady=5)
placeButton('9', get_input, (entry, '9'), row=1, column=2, ipadx=5, pady=5)
placeButton('4', get_input, (entry, '4'), row=2, column=0, ipadx=5, pady=5)
placeButton('5', get_input, (entry, '5'), row=2, column=1, ipadx=5, pady=5)
placeButton('6', get_input, (entry, '6'), row=2, column=2, ipadx=5, pady=5)
placeButton('1', get_input, (entry, '1'), row=3, column=0, ipadx=5, pady=5)
placeButton('2', get_input, (entry, '2'), row=3, column=1, ipadx=5, pady=5)
placeButton('3', get_input, (entry, '3'), row=3, column=2, ipadx=5, pady=5)
placeButton('0', get_input, (entry, '0'), row=4, column=0, padx=8, pady=5,
columnspan=2, sticky=tk.E + tk.W + tk.N + tk.S)
placeButton('.', get_input, (entry, '.'), row=4, column=2, ipadx=7, padx=5, pady=5)
placeButton('+', get_input, (entry, '+'), bg=math_symbol_bg, row=1, column=3, ipadx=5, pady=5)
placeButton('-', get_input, (entry, '-'), bg=math_symbol_bg, row=2, column=3, ipadx=5, pady=5)
placeButton('*', get_input, (entry, '*'), bg=math_symbol_bg, row=3, column=3, ipadx=5, pady=5)
placeButton('/', get_input, (entry, '/'), bg=math_symbol_bg, row=4, column=3, ipadx=5, pady=5)
placeButton('(', get_input, (entry, '('), bg=math_symbol_bg, row=1, column=4, ipadx=5, pady=5)
placeButton(')', get_input, (entry, ')'), bg=math_symbol_bg, row=2, column=4, ipadx=5, pady=5)
placeButton('%', get_input, (entry, '%'), bg=math_symbol_bg, row=3, column=4, ipadx=5, pady=5)
placeButton('^', get_input, (entry, '^'), bg=math_symbol_bg, row=4, column=4, ipadx=5, pady=5)
placeButton('del', backspace, (entry,), row=5, column=0, ipadx=5, padx=5, pady=5)
placeButton('C', clear, (entry,), row=5, column=1, pady=5, ipadx=5)
placeButton('=', calculate, (entry,), bg=cal_output_bg, row=5, column=2, padx=5, pady=5,
columnspan=2, sticky=tk.E + tk.W + tk.N + tk.S)
# move calculator to the center of screen
calculator.geometry(f'{width_cal}x{height_cal}+'
f'{(width_win - width_cal)//2}+'
f'{(height_win-height_cal)//2}')
calculator.mainloop()
2.创建计算器类:创建一个名为Calculator的抽象类,和很多子类,利用工厂设计模式,进行设计
from Exception import *
from abc import ABC, abstractmethod
class OpeCreator:
@staticmethod
def create(arg):
if arg == '+':
return Add()
if arg == '-':
return Sub()
if arg == '*':
return Mul()
if arg == '/':
return Div()
if arg == '^':
return Pow()
if arg == '%':
return Mod()
raise NoOpeTypeError()
class Operator(ABC):
def checkArgs(self, len_input_args):
if len_input_args != self.__expected_args:
self.__argsErr(len_input_args)
def __argsErr(self, len_input_args):
raise ArgsException(self.__expected_args, len_input_args)
def __init__(self, expected_args):
self.__expected_args = expected_args
def __call__(self, *args):
return self.calculate(*args)
def getExpectedArgs(self):
return self.__expected_args
@abstractmethod
def calculate(self, *args):
pass
class Add(Operator):
def __init__(self):
super(Add, self).__init__(2)
def calculate(self, *args):
self.checkArgs(len(args))
return args[0] + args[1]
class Sub(Operator):
def __init__(self):
super(Sub, self).__init__(2)
def calculate(self, *args):
self.checkArgs(len(args))
return args[0] - args[1]
class Mul(Operator):
def __init__(self):
super(Mul, self).__init__(2)
def calculate(self, *args):
self.checkArgs(len(args))
return args[0] * args[1]
class Div(Operator):
def __init__(self):
super(Div, self).__init__(2)
def checkArgs(self, args):
if len(args) != self.getExpectedArgs():
raise ArgsException(self.__expected_args, len(args))
if args[1] == 0:
raise ZeroDivisionException
def calculate(self, *args):
self.checkArgs(args)
return args[0] / args[1]
class Pow(Operator):
def __init__(self):
super(Pow, self).__init__(2)
def calculate(self, *args):
self.checkArgs(len(args))
return args[0] ** args[1]
class Mod(Operator):
def __init__(self):
super(Mod, self).__init__(2)
def calculate(self, *args):
self.checkArgs(len(args))
return args[0] % args[1]
def test():
try:
add = OpeCreator.create('+')
sub = OpeCreator.create('-')
mul = OpeCreator.create('*')
div = OpeCreator.create('/')
pow_ = OpeCreator.create('^')
mod = OpeCreator.create('%')
print(add.calculate(1, 2))
print(sub.calculate(1, 2))
print(mul.calculate(1, 2))
print(OpeCreator.create('/')(2, 3))
print(OpeCreator.create('+')(2, 2))
print(OpeCreator.create('^')(2, 2))
print(OpeCreator.create('%')(4, 3))
except ArgsException as ae:
print(ae)
except ZeroDivisionException as ze:
print(ze)
except NoOpeTypeError as ne:
print(ne)
print('Done')
3运算部分逻辑代码
from Operator import OpeCreator
import re
opes = ['+', '-', '*', '/', '%', '^', '(', ')']
opes_cal = ['+', '-', '*', '/', '%', '^']
ope_fs = {ope: OpeCreator.create(ope) for ope in opes_cal}
def isOperator(arg):
return True if arg in opes else False
# transfer str to list , and judge whether - means negative or sub
def formula_format(formula):
formula = re.sub(' ', '', formula) # delete space
formula_list = [i for i in re.split(r'(-[\d+]\.?\d*)', formula) if i]
final_formula = [] # final expression
for item in formula_list:
if len(final_formula) == 0 and re.match(r'-[\d+]\.?\d*$', item):
final_formula.append(item)
continue
if len(final_formula) > 0:
if re.match(r'[\+\-\*\/\(\%\^]$', final_formula[-1]):
final_formula.append(item)
continue
item_split = [i for i in re.split(r'([\+\-\*\/\(\)\%\^])', item) if i]
final_formula += item_split
return final_formula
def decision(tail_op, cur_op):
rate1 = ['+', '-']
rate2 = ['*', '/', '%']
rate3 = ['^']
rate4 = ['(']
rate5 = [')']
if tail_op in rate1:
if cur_op in rate2 or cur_op in rate3 or cur_op in rate4:
return -1
else:
return 1
elif tail_op in rate2:
if cur_op in rate3 or cur_op in rate4:
return -1
else:
return 1
elif tail_op in rate3:
if cur_op in rate4:
return -1
else:
return 1
elif tail_op in rate4:
if cur_op in rate5:
return 0
else:
return -1
def calculate(operator, *args):
return ope_fs[operator](*args)
def final_calc(formula_list):
num_stack = []
op_stack = []
for item in formula_list:
operator = isOperator(item)
if not operator:
num_stack.append(float(item))
else:
while True:
# op_stack is null , then append into it immediately
if len(op_stack) == 0:
op_stack.append(item)
break
# 决定压栈或弹栈
tag = decision(op_stack[-1], item)
# -1, then append and continue
if tag == -1:
op_stack.append(item)
break
# 0, pop '(' and drop current ')' continue
elif tag == 0:
op_stack.pop()
break
elif tag == 1:
op = op_stack.pop()
num2 = num_stack.pop()
num1 = num_stack.pop()
num_stack.append(calculate(op, num1, num2))
while len(op_stack) != 0:
op = op_stack.pop()
num2 = num_stack.pop()
num1 = num_stack.pop()
num_stack.append(calculate(op, num1, num2))
result = str(num_stack[0])
if result[len(result) - 1] == '0' and result[len(result) - 2] == '.':
result = result[0:-2]
return result
单元测试
性能改进
我在基本的加减乘除运算上,增加了取余数,取幂数等操作,同时健壮性较强,可以应对大多数表达式,并独立实现运算逻辑代码
心得体会
这次软工作品让我学到了不少东西,不断的去学习怎么开发图形化用户界面,对一个完整的软件的前后端交互,以及怎么完成一个具体的项目有了更近一步的了解