简单的计算器功能:
用到的库为tkinter和math
计算器运算功能包括+、-、*、/、sin、cos、幂运算(**)、对数运算、进制转换。
同时还能C:删除全部内容,<:删除最后一个字符,=:得出运算结果。
思路不难
原理是将输入框的内容(字符串)通过双栈法函数得出数值。
双栈法运算表达式,确定优先级,由于双栈法难以实现对数、进制转换,所以目前只用双栈法计算四则运算表达式的值,对数和进制的运算的功能用eval()来实现。
tkinter库含有输入框和按钮等控件,因为本计算器是将字符串用双栈法得出结果,所以首先要获取字符串。tkinter.Entry()为输入框,可以键入,也可以调用函数插入字符,用计算器界面按钮tkinter.Button()来添加窗口按钮,按钮响应函数是将各自代表的字符串插入到Entry的输入框中。
得到的字符串用双栈法运算结果,双栈法是一个栈存数字,另一个栈存字符。先要确定运算符号的优先级,括号最高,如果遇到右括号“)”,则运算右括号前的表达式直到上一个最近的左括号。依次计算,得出结果。
此外,除了要得出运算结果,还要将运算结果显示到输入框。调用函数即可。
最后历史记录。将运算式连同结果保存到txt文件中,利用python的文件函数调用txt文件,写入内容,浏览历史记录的话就读出内容。
重点就是将按钮内容输入到输入框。
详细设计:
import tkinter as tk
from math import *
#界面基本参数
root=tk.Tk()
root.title('计算器')
root.geometry('395x385')
#输入框,用于显示输入的字符串
e=tk.Entry(root,show='',width=19,bd=2,font=('Arial',15))
e.place(x=30,y=10)
#输出框,用于显示输出的结果
t=tk.Entry(root,show='',width=9,bd=2,font=('Arial',15))
t.place(x=261,y=10)
list=[]
iseval=0
class Solution:
def calculate(self, s: str):
# 第一处替换是去除空白字符
# 第二处替换是要在括号以负号开头添加前导0,否则数字栈数字对不上。第三处替换是将"--1"这种格式变成“+1”这种格式
# 第二第三处替换均为特殊情况
s = s.replace(" ", "").replace("(-", "(0-").replace("--", "+") + "+"
length = len(s)
# 定义两个栈分别存放运算符和数字
op_stack, num_stack = [], []
#print(s)
i = 0
# 定义运算符优先级
op_priority = {'+': 0, '-': 0, '*': 1, '/': 1, '%': 1}
while i < length:
c = s[i]
# 判断c是否是数字
if c.isdigit():
num = 0
while s[i].isdigit():
num = num * 10 + int(s[i])
i += 1
# 跳出while循环后i所指的字符已经是运算符,因为大循环每结束一次都要加1,所以这里要退一步,之后再在大循环结束一次后加1
i -= 1
num_stack.append(num)
elif c == '(':
op_stack.append(c)
elif c == ')':
# 不断将括号内没有计算完的表达式计算完,直到遇到左括号为止。
# 注意:括号内表达式的运算符优先级已经是由低到高排序了(一低一高),可以直接调用calc方法计算而无需考虑运算符优先级问题
# 因为遍历到“后”运算符时如果“前”运算符优先级大于“后”运算符会取出栈内元素进行计算,
# 计算后“前”运算符会消失,然后“后”运算符会压入栈中。也就是说只有符合优先级由低到高排序才能继续存在括号内,否则会被提前消除
while op_stack[-1] != '(':
self.calc(num_stack, op_stack)
# 将左括号弹出
op_stack.pop()
# 运算符分支
else:
# 特殊情况:当表达式出现 6/-2+5这种类型时,我们要给-2加上对括号和前导0变成 6/(0-2)+5才能继续计算
if s[i - 1] in "*/" and c == "-":
num_stack.append(0)
op_stack.append("(")
# 遍历往后字符串,直至遇到运算符或右括号为止,再其前面插入一个右括号
# 注意: 数字后面不能是左括号
f = i + 1
while s[f] not in ")+-*/":
f += 1
s = s[:f] + ")" + s[f:]
length += 1
# “(”不是运算符没有优先级,在栈顶时不能参与优先级比较。
while op_stack and op_stack[-1] != '(':
prev_op = op_stack[-1]
# 唯有当栈顶运算符的优先级小于此时运算符的优先级才不会被计算,即“前”运算符优先级小于“后”运算符优先级不会被计算(消除)。如 前面为“+”后面为“/”
if op_priority[prev_op] >= op_priority[c]:
# 将两个栈传过去后,相同的地址可以直接将两栈修改,而无需返回处理后的新栈再接收栈
self.calc(num_stack, op_stack)
else:
break
op_stack.append(c)
i += 1
#print(num_stack, op_stack)
#print(s)
return num_stack[0]
def calc(self, num_stack: list, op_stack: list):
# 每调用一次该函数,数字栈里面至少有一个元素,只有连续出现两次运算符,数字栈才有可能为空, 这种计算x,y都需要补0。而这样的表达式一般只有"++1"这类情况
# 一般情况下都不为0,或x(比y先出现的数字)才有可能是0,如-5的计算应看作0-5
op, y, x = op_stack.pop(), num_stack.pop() if num_stack else 0, num_stack.pop() if num_stack else 0
ans = 0
if op == '+':
ans = x + y
elif op == '-':
ans = x - y
elif op == '*':
ans = x * y
elif op == '/':
ans = x / y
elif op == '%':
ans = x % y
# 使用round函数保留5位小数
num_stack.append(round(ans, 5))
#要运算的数字组成的列表
def list1(x):
list.append(x)
#判断第一位是0
def is0():
global list
if len(list)==0:
return 0
if len(list)>=2 and list[0] == 0 and list[1]=='.':
return 0
if list[0]==0:
return 1
return
#连输符号判断
def isfuhao():
string=e.get()
if len(string)==0:
return True
if string[-1]=='+' or string[-1]=='-' or string[-1]=='*' or string[-1]=='/' or string[-1]=='(' :
return True
return False
#按钮控件:1、2、3、4、5、6、7、8、9、0、(、)、+、-、*、/、sin、cos、**(幂运算)等等。
def hit1():
if is0()==1:
hitd()
list1(1)
e.insert('insert','1')#文本框插入字符,下面几个按钮同理
b1=tk.Button(root,text='1',width=5,height=1,command=hit1,font=('Arial',15))
b1.place(x=30,y=45)
def hit2():
if is0()==1:
hitd()
list1(2)
e.insert('insert','2')
b2=tk.Button(root,text='2',width=5,height=1,command=hit2,font=('Arial',15))
b2.place(x=120,y=45)
def hit3():
if is0()==1:
hitd()
list1(3)
e.insert('insert','3')
b3=tk.Button(root,text='3',width=5,height=1,command=hit3,font=('Arial',15))
b3.place(x=210,y=45)
def hitjia():
global list
if isfuhao()==True:#是否连输符号
return
e.insert('insert','+')
list=[]
bjia=tk.Button(root,text='+',width=5,height=1,command=hitjia,font=('Arial',15))
bjia.place(x=300,y=45)
def hit4():
if is0()==1:
hitd()
list1(3)
e.insert('insert','4')
b4=tk.Button(root,text='4',width=5,height=1,command=hit4,font=('Arial',15))
b4.place(x=30,y=90)
def hit5():
if is0()==1:
hitd()
list1(5)
e.insert('insert','5')
b5=tk.Button(root,text='5',width=5,height=1,command=hit5,font=('Arial',15))
b5.place(x=120,y=90)
def hit6():
if is0()==1:
hitd()
list1(6)
e.insert('insert','6')
b6=tk.Button(root,text='6',width=5,height=1,command=hit6,font=('Arial',15))
b6.place(x=210,y=90)
def hitjian():
global list
if isfuhao()==True:
return
e.insert('insert','-')
list=[]
bjian=tk.Button(root,text='-',width=5,height=1,command=hitjian,font=('Arial',15))
bjian.place(x=300,y=90)
def hit7():
if is0()==1:
hitd()
list1(7)
e.insert('insert','7')
b7=tk.Button(root,text='7',width=5,height=1,command=hit7,font=('Arial',15))
b7.place(x=30,y=135)
def hit8():
if is0()==1:
hitd()
list1(8)
e.insert('insert','8')
b8=tk.Button(root,text='8',width=5,height=1,command=hit8,font=('Arial',15))
b8.place(x=120,y=135)
def hit9():
if is0()==1:
hitd()
list1(9)
e.insert('insert','9')
b9=tk.Button(root,text='9',width=5,height=1,command=hit9,font=('Arial',15))
b9.place(x=210,y=135)
def hitcheng():
global list
if isfuhao()==True:
return
e.insert('insert','*')
list=[]
bcheng=tk.Button(root,text='*',width=5,height=1,command=hitcheng,font=('Arial',15))
bcheng.place(x=300,y=135)
def hitzuo():
string1=e.get()
global list
if len(list)!=0:
return
if len(string1)!=0:
if string1[-1]==')':
return
e.insert('insert', '(')
list=[]
bzuo=tk.Button(root,text='(',width=5,height=1,command=hitzuo,font=('Arial',15))
bzuo.place(x=30,y=180)
def hit0():
if is0()==1:
hitd()
list1(0)
e.insert('insert','0')
b0=tk.Button(root,text='0',width=5,height=1,command=hit0,font=('Arial',15))
b0.place(x=120,y=180)
def hityou():
global list
string = e.get()
if len(string) == 0:
return
if string[-1] == '('or string[-1]=='+' or string[-1]=='-' or string[-1]=='*' or string[-1]=='/' :
return
e.insert('insert',')')
list=[]
byou=tk.Button(root,text=')',width=5,height=1,command=hityou,font=('Arial',15))
byou.place(x=210,y=180)
def hitchu():
global list
if isfuhao()==True:
return
e.insert('insert','/')
list=[]
bchu=tk.Button(root,text='/',width=5,height=1,command=hitchu,font=('Arial',15))
bchu.place(x=300,y=180)
def hitdian():
if len(e.get())==0 or len(list)==0:
list1(0)
list1('.')
e.insert('insert', '0.')
return
i=0
for i in range(len(list)):
if list[i-1]=='.':
i=0
return
i=i+1
if i==0:
return
global iseval
iseval=1
e.insert('insert','.')
list1('.')
bdian=tk.Button(root,text='.',width=5,height=1,command=hitdian,font=('Arial',15))
bdian.place(x=30,y=225)
def hitc():
global list
list=[]
e.delete(0,'end')
t.delete(0, 'end')
bce=tk.Button(root,text='C',width=5,height=1,command=hitc,font=('Arial',15))
bce.place(x=120,y=225)
def hitd():
global list
if len(list)!=0:
del(list[-1])
n=len(e.get())
e.delete(n-1,n)
bd=tk.Button(root,text='<',width=5,height=1,command=hitd,font=('Arial',15))
bd.place(x=210,y=225)
def hitsin():
global list,iseval
if len(list)!=0:
return
e.insert('insert','sin(')
iseval=1
list=[]
bsin=tk.Button(root,text='sin',width=5,height=1,command=hitsin,font=('Arial',15))
bsin.place(x=30,y=270)
def hitcos():
global list,iseval
if len(list)!=0:
return
e.insert('insert','cos(')
iseval=1
list =[]
bcos=tk.Button(root,text='cos',width=5,height=1,command=hitcos,font=('Arial',15))
bcos.place(x=120,y=270)
def hitmi():
global list,iseval
e.insert('insert','**')
iseval=1
list=[]
bmi=tk.Button(root,text='**',width=5,height=1,command=hitmi,font=('Arial',15))
bmi.place(x=210,y=270)
def hitlog2():
global list,iseval
if len(list)!=0:
return
e.insert('insert','log2(')
iseval=1
list=[]
bsin=tk.Button(root,text='log2',width=5,height=1,command=hitlog2,font=('Arial',15))
bsin.place(x=30,y=315)
def hitloge():
global list,iseval
if len(list)!=0:
return
e.insert('insert','log(')
iseval=1
list=[]
bsin=tk.Button(root,text='loge',width=5,height=1,command=hitloge,font=('Arial',15))
bsin.place(x=120,y=315)
def hitbin():
global list,iseval
if len(list)!=0:
return
e.insert('insert','bin(')
iseval=1
list=[]
bsin=tk.Button(root,text='bin',width=5,height=1,command=hitbin,font=('Arial',15))
bsin.place(x=210,y=315)
def hitoct():
global list,iseval
if len(list)!=0:
return
e.insert('insert','oct(')
iseval=1
list=[]
bsin=tk.Button(root,text='oct',width=5,height=1,command=hitoct,font=('Arial',15))
bsin.place(x=300,y=315)
#等号按钮的功能,得出结果的同时保存计算记录。可以判断运算式是否正确,正确就try运行计算,否则except输出错误。
def cul():
try:
var=e.get()
if iseval==0:
num=Solution().calculate(var)
elif iseval==1:
num=eval(var)
t.delete(0, 'end')
t.insert('end',num)
path = r'D:\Python\新建 文本文档.txt'#txt文档保存记录
file = open(path, 'a')#文档尾部添加记录
file.write(e.get()+'='+f'{num}'+'\n')
except :
t.delete(0, 'end')
t.insert('end', '表达式错误')
b=tk.Button(root,text='=',width=5,height=1,command=cul,font=('Arial',15))
b.place(x=300,y=225)
#打开历史记录,读出文档内容,显示到文本框中
def txt():
path1 = r'D:\Python\新建 文本文档.txt'
file1 = open(path1, 'r')
content = file1.read()
root1= tk.Tk()
root1.title('历史记录')
root1.geometry('300x800')
t1=tk.Text(root1, width=30, bd=2,height=35, font=('Arial', 15))
t1.pack()
t1.insert('insert',content)
btxt=tk.Button(root,text='记录',width=5,height=1,command=txt,font=('Arial',15))
btxt.place(x=300,y=270)
root.mainloop()