使用tkinter实现的计算器
#!/usr/bin/env pythonright
-- coding: UTF-8 --
#使用tkinter模块实现一个简单的加减乘除计算器
import tkinter as tk
import functools
#常量
maxLen = 40
operator = ('/','*','-','+') #计算符号
class calcuatorApp(tk.Frame):
#保存计算式内容(由于计算式长度有可能超过能够表示的最大长度,所以需要另外保存)
processTextBack = ''
def __init__(self, master=None):
super().__init__(master)
self.processTextBack = ''
self.grid(padx=25, pady=25,sticky=tk.NSEW)
self.create_widgets() #生成窗口上的显示区域和按钮
def create_widgets(self):
#app随着外层窗口缩放
top=self.winfo_toplevel()
top.rowconfigure(0, weight=1)
top.columnconfigure(0, weight=1)
# 7行4列的各个组件随着app的大小缩放
for i in range(7): #7行自动缩放
self.rowconfigure(i, weight=1)
for i in range(4): #4列自动缩放
self.columnconfigure(i, weight=1)
#按钮上显示的字符
textForBtn = (('C','(',')','%'), #%为百分号
('7','8','9','/'),
('4','5','6','*'),
('1','2','3','-'),
('0','.','=','+'))
#界面上第一行用于输出计算式
self.process = tk.Label(self,text='',font='courier 15',bg='#DCDCDC',anchor = 'e')
self.process.grid(row=0, columnspan=4, padx=1, pady=1, ipadx=200, ipady=10, sticky=tk.NSEW)
#界面上第二行用于输出计算结果
self.result = tk.Label(self,text='',font='courier 15 bold',bg='#DCDCDC',anchor = 'e')
self.result.grid(row=1, columnspan=4, padx=1, pady=1, ipadx=200, ipady=10, sticky=tk.NSEW)
#界面上第三行开始用于显示0-9的数字和计算符号
for i in range(5): #5行
for j in range(4): #4列
tempbtn = tk.Button(self,text=textForBtn[i][j],font='courier 15',bg='#DCDCDC')
#点击=按钮(计算)
if(textForBtn[i][j] == '='):
tempbtn['command'] = self.equals
#点击C按钮(清空)
elif(textForBtn[i][j] == 'C'):
tempbtn['command'] = functools.partial(self.inputClear)
else:
#其他
tempbtn['command'] = functools.partial(self.inputText,tempbtn)
tempbtn.grid(row=i+2, column=j, padx=1, pady=1, ipadx=50, ipady=10, sticky=tk.NSEW)
# 点击C按钮,清空
def inputClear(self):
self.processTextBack=''
self.process['text']=''
self.result['text']=''
# 点击=按钮,进行计算
def equals(self):
#获取计算式
processText = self.processTextBack
#检查计算式: 计算式长度为空,=无效
if(len(processText) == 0):
return
#检查计算式: 计算式最后一个为=,=重复无效
if(processText[-1] == '='):
return
#检查计算式: 计算式最后一个为(,报错
if(processText[-1] == '('):
self.result['text']= 'Input Error'
return
#检查计算式:操作数不足报错
if(processText[-1] in '+-/*'):
self.result['text']= 'Input Error'
return
#)不足的话,自动补齐
countLeft = processText.count('(')
countRight = processText.count(')')
if(countLeft > 0 and countLeft > countRight):
processText += ')' * (countLeft - countRight)
#计算用的字符串里面百分号%替换成/100 (要保证%的优先级)
processForEval = processText
sIndex = 0
while('%' in processForEval):
#开始查找%,切分成左右两边
Index1 = processForEval.find('%')
strLeft = processForEval[:Index1]
strRight = processForEval[Index1+1:]
#%的前一位为数字: 操作数% 变成 (操作数/100)
if(strLeft[-1].isdigit()):
#%左边字符串的最后一个操作数
strTmp = self.operandLast(strLeft)
strLeft = strLeft[0:len(strLeft) - len(strTmp)] + '('+ strTmp +'/100)'
else:
#%的前一位为): (操作数1 + 操作数2)% 变成((操作数1 + 操作数2)/100)
indexTmp = strLeft.rfind('(')
if(indexTmp == -1):
indexTmp = 0
strLeft = strLeft[:indexTmp] + '(' + strLeft[indexTmp:] + '/100)'
processForEval = strLeft + strRight
#计算
try:
result = str(eval(processForEval))
except:
result = 'Input Error'
#表示计算式和结果
self.processTextBack = processText + '='
self.process['text']= processText + '='
self.result['text']= result
# 点击=,C以外的其他按钮
def inputText(self, btn):
#按钮名称
btnname = btn['text']
resultClearFlg = False
resultText = self.result['text']
#计算式内容确认
processText = self.processTextBack
if(len(processText) > 0 and processText[-1] == '='): #如果计算式以=结尾,则清空既存计算式和结果
processText = ''
resultClearFlg = True
#根据按钮名称进行相应判定
chkflg = True
if(btnname.isdigit()):
#数字按钮, 前面不能为),%
if(len(processText) > 0 and processText[-1] in (')','%') ):
chkflg = False
elif( btnname == '.' ):
#小数点按钮, 前面不能为),%,.
if(len(processText) > 0 and processText[-1] in (')','%','.') ):
chkflg = False
#一个操作数里面不能出现两个(例如:2.33.5)
if(chkflg and processText.rfind('.') > -1):
operand = self.operandLast(processText)
if('.' in operand):
chkflg = False
elif( btnname == '(' ):
#左括号按钮, 前面只能为空,操作符,(
if(len(processText)>0 and not (processText[-1] in operator or processText[-1] =='(')):
chkflg = False
elif( btnname == ')' ):
#右括号按钮, 前面只能为数字或者%
if(len(processText) == 0): #前面空白
chkflg = False
elif(not (processText[-1].isdigit() or processText[-1]=='%')): #前面不为数字,%
chkflg = False
else:
#判定前面是否有对应的(
if(processText.count('(') <= processText.count(')')):
chkflg = False
elif( btnname == '%' ):
#百分号按钮,前面必须为()包含的计算式或者数字
if(len(processText) == 0): #前面空白
chkflg = False
elif(not(processText[-1].isdigit() or processText[-1]==')')): #前面必须为()包含的计算式或者数字
chkflg = False
else:
#'/''*''+''-'计算符号
#前一次计算完毕,下一次从计算符号开始的话,把结果作为计算式的开头
if(resultClearFlg):
processText = resultText
if(len(processText) == 0): #前面空白
chkflg = False
elif(processText[-1] in operator or processText[-1]=='('): #前面不能为(和计算符号
chkflg = False
#判定为True时候,计算式内容显示
if (chkflg):
if( btnname == '.'):
#前一位不是数字的时候,自动补0
if (len(processText) == 0 or (len(processText) > 1 and not(processText[-1].isdigit()))):
strTmp = processText + '0' + btnname
else:
strTmp = processText + btnname
else:
strTmp = processText + btnname
#长度超出之后左边的截断后显示
self.processTextBack = strTmp #长度不截断
self.process['text'] = strTmp if len(strTmp) < maxLen else strTmp[-maxLen:]
if(resultClearFlg):
self.result['text'] = ''
#取得计算式的最后一个操作数
def operandLast(self, text):
#text长度为0
if(len(text) == 0):
return ''
#最后一个计算符的位置
position = max([text.rfind(item) for item in operator])
#最后一个字符为计算符
if(position == len(text) -1 ):
return ''
#没有找到计算符,整个作为一个操作数
if(position == -1):
return text
else:
return text[position+1:]
if __name__ == '__main__':
#主窗口
myWindow = tk.Tk()
myWindow.title('计算器') #标题
myWindow.geometry('600x400') #初始尺寸
myWindow.config(background ='#FFFFF0') #颜色
#在主窗口创建一个计算器frame
myCalcuatorApp = calcuatorApp(master=myWindow)
myCalcuatorApp.master.minsize(600,400) #最小尺寸
myCalcuatorApp.master.maxsize(900,600) #最大尺寸
myCalcuatorApp.config(background ='#FFFFF0') #颜色
#消息循环
myWindow.mainloop()
运行结果: