用python的tkinter实现计算器功能

简单的计算器功能:

用python的tkinter实现计算器功能_第1张图片

用python的tkinter实现计算器功能_第2张图片

用到的库为tkinter和math 

计算器运算功能包括+、-、*、/、sin、cos、幂运算(**)、对数运算、进制转换。

同时还能C:删除全部内容,<:删除最后一个字符,=:得出运算结果。

思路不难

原理是将输入框的内容(字符串)通过双栈法函数得出数值。

双栈法运算表达式,确定优先级,由于双栈法难以实现对数、进制转换,所以目前只用双栈法计算四则运算表达式的值,对数和进制的运算的功能用eval()来实现。

tkinter库含有输入框和按钮等控件,因为本计算器是将字符串用双栈法得出结果,所以首先要获取字符串。tkinter.Entry()为输入框,可以键入,也可以调用函数插入字符,用计算器界面按钮tkinter.Button()来添加窗口按钮,按钮响应函数是将各自代表的字符串插入到Entry的输入框中。

得到的字符串用双栈法运算结果,双栈法是一个栈存数字,另一个栈存字符。先要确定运算符号的优先级,括号最高,如果遇到右括号“)”,则运算右括号前的表达式直到上一个最近的左括号。依次计算,得出结果。

此外,除了要得出运算结果,还要将运算结果显示到输入框。调用函数即可。

最后历史记录。将运算式连同结果保存到txt文件中,利用python的文件函数调用txt文件,写入内容,浏览历史记录的话就读出内容。

重点就是将按钮内容输入到输入框。

总体框架:用python的tkinter实现计算器功能_第3张图片

详细设计:

用python的tkinter实现计算器功能_第4张图片

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()

你可能感兴趣的:(python)