使用Python和tkinter库来实现具有可视化界面的计算器--软工第一次作业

作业基本信息

这个作业属于哪个课程<2301-计算机学院-软件工程>
这个作业要求在哪里软件工程实践第一次作业
这个作业的目标<完成一个具有可视化界面的计算器>
其他参考文献tkinter教程 Math库

Gitcode项目地址

Python实现简易图形化计算器

PSP表格

PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划1015
• Estimate• 估计这个任务需要多少时间1015
Development开发410570
• Analysis• 需求分析 (包括学习新技术)12080
• Design Spec• 生成设计文档2010
• Design Review• 设计复审3020
• Coding Standard• 代码规范 (为目前的开发制定合适的规范)1520
• Design• 具体设计2030
• Coding• 具体编码180200
• Code Review• 代码复审1520
• Test• 测试(自我测试,修改代码,提交修改)4030
Reporting报告160180
• Test Repor• 测试报告7080
• Size Measurement• 计算工作量4030
• Postmortem & Process Improvement Plan• 事后总结, 并提出过程改进计划5070
合计610785

解题思路描述

  • 本次作业是使用Pythontkinter库来实现具有可视化界面的计算器。
  • 创建图形化界面: 通过tkinter库创建了一个GUI窗口,同时创建显示数学表达式的标签、输入数学表达式和显示算式结果的文本框和一个包含数字、运算符的按钮网格,每个按钮都与一个函数相关,单击按钮时调用改命令。
  • 数学表达式计算: 本次作业中花费时间最多是在实现复杂数学表达式的计算,通过查阅相关资料,我将输入的数学表达式进行拆分,然后使用两个堆栈(一个用于运算符,一个用于数字)来解析和计算表达式。

接口设计和实现过程

类 class CalculatorApp

表示主应用程序窗口。它初始化图形化窗口,并创建了计算器所需的部件和实现一些基本功能

  • 窗口布局

layout 方法用于初始化计算器的用户界面,经创建了一个标签(Label)来显示计算结果和一个文本框(Entry)来接收输入,然后通过循环创建了一个按钮矩阵,并为每个按钮关联了相应的功能。

    def layout(self):
    	#初始化界面
        self.screen = tk.Label(self.root, width=31, height=2,  bg='#BDBDBD', anchor='center',
                              textvariable=self.calculatation_formula,relief = 'sunken', font=('微软雅黑', 17))
        self.screen.grid(row=0, columnspan=6,pady=(5,5))
        self.entry = tk.Entry(self.root, width=33, bd=3,bg='#F5F5F5', justify="right", font=('微软雅黑', 17),insertbackground="#757575")
        self.entry.grid(row=1, columnspan=6, padx=5, pady=5)
        myButton = partial(tk.Button, self.root, width=7,height=3,bd=3,pady=10,padx=10, cursor='hand2', activebackground='#B3E5FC',relief='sunken')
		#按钮矩阵
        buttons = [
            ('(', '('), (')', ')'), ('^', '^'), ('/', '/'), ('*', '*'),('删除', 'backspace'),
            ('log', 'log('), (' cos ', 'cos('), ('7', '7'), ('8', '8'), ('9', '9'),('清空', 'clear'),
            (' ln ', 'ln('), (' sin ', 'sin('), (' 6 ', '6'), (' 5 ', '5'), ('4', '4'),('+', '+'),
            (' e ', 'e'), (' tan ', 'tan('), (' 3 ', '3'), (' 2 ', '2'), ('1', '1'),('-', '-'),
            (' √ ', '√('),(' π ', 'π'), (' % ', '%'), (' 0 ', '0'), (' . ', '.'), (' \n = \n ', 'calculate')
        ]
        row = 2
        col = 0
        #按钮关联对应功能
        for (text, command) in buttons:
            if text == ' \n = \n ':
                button = myButton(text=text, command=lambda cmd=command: self. get_result(),bg='#BDBDBD')
                button.grid(row=row, column=col)
            else:
                button = myButton(text=text, command=lambda cmd=command: self.input(cmd),bg='#F5F5F5',font=('微软雅黑',9))
                button.grid(row=row, column=col)
            col += 1
            if col > 5:
                col = 0
                row += 1
  • 处理输入的字符

    根据用户点击的按钮,执行不同的操作。如果 argu 是 “backspace”,则删除最后一个字符;如果是 “clear”,则清空文本框;否则,在文本框中插入 argu。

    def input(self, argu):
        formula = self.entry.get()
        if argu == 'backspace':
            self.backspace()
        elif argu == 'clear':
            self.clear()
        else:
            self.entry.insert(tk.INSERT, argu)
  • 点击’='式计算结果

    计算结果:获取文本框中的输入表达式。将表达式传递给calculate 函数进行计算。如果计算成功,清空文本框并将计算结果插入到文本框中,并将计算结果显示在标签上。如果计算失败,清空文本框并显示"出错"。

    def get_result(self):
        try:
            formula = self.entry.get()
            result = calculate(expression_format(formula))
            self.clear()
            self.entry.insert(tk.END, result)
            self.calculatation_formula.set(''.join(formula + '='))
        except Exception as e:# 捕获更具体的异常,以便排除错误
            self.clear()
            self.entry.insert(tk.END, '出错')
  • 将公式字符串拆分为标记方便后续计算

    将字符串分割成多个部分,创建一个空列表 final_expression 用于存储最终的表达式列表,迭代处理分割后的部分。

def expression_format(expression):
    expression = re.sub(' ', '', expression)
    expression_parts = re.split('(-[\d+,π,e]\.?\d*)', expression)
    expression_list = [part for part in expression_parts if part]
    final_expression = []
    for item in expression_list:
        if len(final_expression) == 0 and re.match('-[\d+,π,e]\.?\d*$', item):
            final_expression.append(item)
            continue
        if len(final_expression) > 0 and re.match('[\+\-\*\/\(\)\%\^\√]$', final_expression[-1]):
            final_expression.append(item)
            continue
        item_parts = re.split('([\+\-\*\/\(\)\%\^\√])', item)
        final_expression += [part for part in item_parts if part]
    return final_expression

  • 结果计算

初始化一个空的数字栈 num_stack 和一个空的运算符栈 op_stack,用于在计算过程中存储数字和运算符。遍历输入的表达式列表 list 中的每个元素 item。
对于每个元素 item,首先检查它是否是运算符(使用 is_operator(item) 函数)。
1、如果不是运算符,则将其视为数字或特殊常数(如π和e)
2、如果 item 是运算符,进入循环,
  (1)如果运算符栈 为空,或者运算符栈顶的运算符的优先级低于当前运算符,则将 item 压入运算符栈
  (2)如果运算符栈顶的运算符的优先级高于等于当前运算符,或者是’√’、‘sin’ 等,则从运算符栈 弹出一个运算符,并从数字栈 弹出两个数值进行计算,然后将计算结果压入数字栈 num_stack。
  (3)如果当前运算符是一元运算符(如 ‘√’、‘sin’ 等),则将其压入运算符栈。
循环结束后,如果运算符栈 中还有剩余的运算符,依次从运算符栈中弹出运算符,并从数字栈 弹出两个数值进行计算,然后将计算结果压入数字栈 。
最后,将数字栈 中的最终结果转换为字符串,然后将结果返回。

def calculate(list):
    num_stack = []
    op_stack = []
    for item in list:
        operator = is_operator(item)
        if not operator:
            if item == 'π':
                num_stack.append(pi)
            elif item == '-π':
                num_stack.append(-pi)
            elif item == 'e':
                num_stack.append(e)
            elif item == '-e':
                num_stack.append(-e)
            else:
                num_stack.append(float(item))
        else:
            while True:
                if len(op_stack) == 0:
                    op_stack.append(item)
                    break
                tag = rate(op_stack[-1], item)
                if tag == -1:
                    op_stack.append(item)
                    break
                elif tag == 0:
                    op_stack.pop()
                    calculate2(op_stack, num_stack)
                    break
                elif tag == 1:
                    if item in ['√', 'sin', 'cos','tan','log','ln']:
                        op_stack.append(item)
                        break
                    op = op_stack.pop()
                    num2 = num_stack.pop()
                    num1 = num_stack.pop()
                    num_stack.append(calculate1(float(num1), float(num2), op))
    while len(op_stack) != 0:
        op = op_stack.pop()
        num2 = num_stack.pop()
        num1 = num_stack.pop()
        num_stack.append(calculate1(float(num1), float(num2), op))
    result = str(num_stack[0])
    if result.endswith('.0'):
        result = result[:-2]
    return result

关键功能展示

  • 加减乘除运算
  • 三角函数、对数等运算
    在这里插入图片描述
  • 删除、清空功能

在这里插入图片描述

单元测试与性能分析

1. 单元测试

本次使用unittest框架进行测试

- 部分代码

import unittest
import tkinter as tk
from main import *
from calculate import  *

class TestCalculator(unittest.TestCase):
    def test_get_result(self):
        self.app.entry.insert(tk.END, '6*9+sin(30)+ln(e)+6^2+6/2-3*2')
        self.app.get_result()
        self.assertEqual(self.app.entry.get(), '88.5')
        self.assertEqual(self.app.calculatation_formula.get(), '6*9+sin(30)+ln(e)+6^2+6/2-3*2=')

    def test_calculate(self):
        # 测试 final_calc 函数
        self.assertEqual(calculate(['-2', '+', '2','-','7','*','5']), '-35')
        self.assertEqual(calculate(['ln', '(', 'e', ')', '+', '5']), '6')
        self.assertEqual(calculate(['π', '*', '2']), '6.283185307179586')

if __name__ == '__main__':
    unittest.main()

- 测试结果
在这里插入图片描述

2. 性能分析

使用 time.time() 函数来获取当前时间戳,并在执行代码前后记录时间戳,然后计算二者之差,从而得到代码执行所花费的时间

  • 分析框架
import time
def analysis():
    start_time = time.time()  
	def expression_format(formula)...
	def calculate(list)..
    result = calculate(formula_format("2 * 2+sin(30)"))
    end_time = time.time()  # 记录结束时间
    elapsed_time = end_time - start_time  # 计算执行时间
    print(f"执行时间: 0.0613 秒")
    return result
if __name__ == '__main__':
    result = performance_analysis()

在这里插入图片描述

异常处理

当用户输入错误的表达式时会通过输出错误及时提醒用户

  • 案例1:除数为0
    在这里插入图片描述
  • 案例2:三角函数等运算时为加右括号
    -在这里插入图片描述
  • 处理代码
     try:
            formula = self.entry.get()
            result = calculate(expression_format(formula))
            self.clear()
            self.entry.insert(tk.END, result)
            self.calculatation_formula.set(''.join(formula + '='))
        except Exception as e:# 捕获更具体的异常,以便排除错误
            self.clear()
            self.entry.insert(tk.END, '出错')

心得体会

  • 本次自己动手编程实现了一个科学计算器,帮助自己熟悉Python编程和Tkinter库的基础知识。
  • 一个好的计算器应该具有用户友好的界面,易于使用。技术有限,本次实现的界面较简略,后续还需要继续改进。
  • 编程中,需要学会查找官方文档和在线资源以获取帮助和解决问题。
  • 在编程过程中,难免会遇到错误,要学习使用调试工具来识别和修复错误。
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
当然可以,以下是一个简单的Python可视化计算器程序: ```python from tkinter import * class Calculator: def __init__(self, master): self.master = master master.title("Python Calculator") # 创建显示屏 self.display = Entry(master, width=30, borderwidth=5) self.display.grid(row=0, column=0, columnspan=4, padx=10, pady=10) # 创建按钮 self.button_1 = Button(master, text="1", padx=40, pady=20, command=lambda: self.button_click(1)) self.button_2 = Button(master, text="2", padx=40, pady=20, command=lambda: self.button_click(2)) self.button_3 = Button(master, text="3", padx=40, pady=20, command=lambda: self.button_click(3)) self.button_4 = Button(master, text="4", padx=40, pady=20, command=lambda: self.button_click(4)) self.button_5 = Button(master, text="5", padx=40, pady=20, command=lambda: self.button_click(5)) self.button_6 = Button(master, text="6", padx=40, pady=20, command=lambda: self.button_click(6)) self.button_7 = Button(master, text="7", padx=40, pady=20, command=lambda: self.button_click(7)) self.button_8 = Button(master, text="8", padx=40, pady=20, command=lambda: self.button_click(8)) self.button_9 = Button(master, text="9", padx=40, pady=20, command=lambda: self.button_click(9)) self.button_0 = Button(master, text="0", padx=40, pady=20, command=lambda: self.button_click(0)) self.button_add = Button(master, text="+", padx=39, pady=20, command=self.button_add) self.button_equal = Button(master, text="=", padx=91, pady=20, command=self.button_equal) self.button_clear = Button(master, text="Clear", padx=79, pady=20, command=self.button_clear) # 将按钮放置在屏幕上 self.button_1.grid(row=3, column=0) self.button_2.grid(row=3, column=1) self.button_3.grid(row=3, column=2) self.button_4.grid(row=2, column=0) self.button_5.grid(row=2, column=1) self.button_6.grid(row=2, column=2) self.button_7.grid(row=1, column=0) self.button_8.grid(row=1, column=1) self.button_9.grid(row=1, column=2) self.button_0.grid(row=4, column=0) self.button_clear.grid(row=4, column=1, columnspan=2) self.button_add.grid(row=5, column=0) self.button_equal.grid(row=5, column=1, columnspan=2) self.first_number = None self.operation = None def button_click(self, number): current = self.display.get() self.display.delete(0, END) self.display.insert(0, str(current) + str(number)) def button_clear(self): self.display.delete(0, END) self.first_number = None self.operation = None def button_add(self): self.first_number = float(self.display.get()) self.operation = "addition" self.display.delete(0, END) def button_equal(self): second_number = float(self.display.get()) self.display.delete(0, END) if self.operation == "addition": result = self.first_number + second_number elif self.operation == "subtraction": result = self.first_number - second_number elif self.operation == "multiplication": result = self.first_number * second_number elif self.operation == "division": result = self.first_number / second_number self.display.insert(0, result) self.first_number = None self.operation = None root = Tk() my_calculator = Calculator(root) root.mainloop() ``` 希望这个程序能够帮到你!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值