软工作业01_简易计算器

作业链接

https://bbs.csdn.net/topics/617294583

 Gitcode项目地址

102101436郑择杭 / homework11 · GitCode

PSPPersonal software process stages预估耗时(分钟re)实际耗时(分钟)
Planning计划2020
Estimate估计这个任务需要多少时间1515
Development开发5060
analysis需求分析(包括学习新技术)2025
Design spec生成设计文档3030
Design review设计复审1515
Coding standard代码规范(为目前的开发指定合适的规范)1515
Design具体设计100110
Coding具体编码200240
Code review代码复审8090
Test

测试(自我测试,修改代码、提交修改)

6060
Reporting报告6060
Test report测试报告4560
Size measurement计算工作量1010
Postmortem process improvement plan事后总结,并提出过程改进计划2020
Total合计740830

界面展示

解题思路描述

本次作业要求实现一个简易计算器,了解计算器的一些基本要求,决定使用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

   

单元测试

性能改进

我在基本的加减乘除运算上,增加了取余数,取幂数等操作,同时健壮性较强,可以应对大多数表达式,并独立实现运算逻辑代码

心得体会

这次软工作品让我学到了不少东西,不断的去学习怎么开发图形化用户界面,对一个完整的软件的前后端交互,以及怎么完成一个具体的项目有了更近一步的了解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值