python计算器全代码无第三方库避免使用eval
时间: 2025-07-10 14:46:38 浏览: 8
<think>好的,我现在需要帮助用户找到一个不依赖第三方库且不使用eval函数的Python计算器完整代码。首先,我得理解用户的具体需求。他们想要一个计算器,用Python编写,不需要第三方库,比如Tkinter虽然是标准库,但用户可能指的是不需要安装的第三方库,而Tkinter是Python自带的,应该可以使用。但用户还提到要避免使用eval函数,因为eval有安全风险。
首先,我得考虑如何解析数学表达式。不使用eval的话,必须自己处理表达式中的运算符优先级、括号等。常见的方法是使用栈来处理,比如逆波兰表达式算法。或者,可以手动解析表达式,分解成数字和运算符,然后按照运算顺序逐步计算。
接下来,我需要确定如何处理运算符的优先级。比如乘除应该先于加减,括号内的部分需要优先计算。这可能需要递归或者栈结构来处理括号内的子表达式。
然后,用户界面部分。用户可能想要一个简单的图形界面,Tkinter是标准库,应该可以使用。但用户提到“不依赖第三方库”,所以Tkinter是可以接受的,因为它是Python内置的。因此,代码需要结合Tkinter来创建界面,同时处理按钮点击事件,并将输入的表达式传递给解析器进行计算。
接下来,如何实现表达式解析。可能需要编写一个表达式求值的函数,将输入的字符串分解成token(数字、运算符、括号),然后按照运算顺序进行计算。这里可以使用两个栈,一个存放操作数,一个存放运算符,遇到括号时递归处理。
例如,处理表达式字符串时,逐个字符读取,识别数字和运算符。对于多位数和小数点的情况,需要正确处理。比如遇到'123'时,要将其解析为整数123,遇到'12.34'则解析为浮点数。
然后,运算符优先级处理。当遇到新的运算符时,比较其与栈顶运算符的优先级,决定是否先执行栈顶的运算。例如,当前运算符是加号,而栈顶是乘号,则先计算乘号。
关于括号的处理,当遇到左括号时,需要将后续的表达式视为子表达式,递归处理,直到遇到右括号为止,然后计算结果。
接下来,需要测试各种情况,确保表达式解析正确,比如处理负数、连续运算符、括号嵌套等情况。例如,表达式“3+5*2/(6-2)”应该正确计算为3+ (5*2)/(6-2) = 3 + 10/4 = 5.5。
然后,界面部分需要创建按钮和输入框。使用Tkinter的Entry组件来显示输入的表达式,按钮布局按照计算器的常见排列,数字0-9,运算符+-*/,括号,等于号,清除键等。
在按钮点击事件处理中,将字符追加到输入框,当点击等号时,触发解析和计算函数,显示结果。如果有错误,如除零或无效表达式,需要捕获异常并提示用户。
现在,将这些部分整合起来。首先编写表达式解析和计算的函数,然后创建Tkinter界面,将两者结合起来。确保代码不使用eval,也没有第三方库依赖。
可能的难点在于正确处理运算符优先级和括号嵌套,这需要仔细设计算法。可能需要分步骤处理:词法分析、语法分析、求值。或者使用调车场算法(Shunting-yard algorithm)将中缀表达式转换为后缀表达式(逆波兰表示法),然后计算后缀表达式。
例如,Shunting-yard算法的大致步骤是:遍历中缀表达式的每个token,如果是数字则输出到后缀表达式;如果是运算符,则与栈顶运算符比较优先级,将优先级高的或相等的弹出并输出,直到遇到左括号或栈空,然后将当前运算符压栈;遇到左括号压栈;遇到右括号则弹出栈顶直到左括号,并输出这些运算符。最后将栈中剩余运算符弹出输出。得到后缀表达式后,再遍历每个token,遇到数字压栈,遇到运算符则弹出栈顶两个元素进行计算,结果压栈,直到结束,栈顶即为结果。
这样,整个计算过程不需要eval,完全手动处理。因此,需要实现这个算法。
现在,我需要编写相应的代码,实现上述逻辑。首先处理字符串分解成token,然后转换为后缀表达式,最后计算后缀表达式。
在分解token时,需要考虑多位数、小数点和可能的负数。例如,处理像“-3+4*(5.5-2)”这样的表达式,正确分解为“-3”、“+”、“4”、“*”、“(”、“5.5”、“-”、“2”、“)”。
在处理负数时,需要注意当减号出现在表达式开头或前一个token是左括号时,视为负号而非减号,此时需要处理为一元运算符。这部分可能需要特殊处理,比如在词法分析阶段将这种情况标记为负数,或者在后续处理中调整。
不过,这会增加复杂度。为了简化,可能暂时不支持负数作为一元运算符,或者需要特别处理这种情况。
假设用户输入的表达式是合法的,并且不需要处理负数,或者可以处理像“0-3”这样的情况。或者在词法分析时,如果遇到减号前面没有数字或右括号,则视为负号,此时添加一个0,变成0-3。
这可能会使代码复杂,但为了正确性,可能需要处理这种情况。
综上所述,整个代码结构大致分为:
1. 词法分析:将表达式字符串拆分为token列表,包括数字、运算符、括号。
2. 中缀转后缀:使用Shunting-yard算法将中缀表达式转换为后缀表达式。
3. 计算后缀表达式:根据后缀表达式计算最终结果。
4. 界面部分:使用Tkinter创建输入框和按钮,处理用户输入,调用解析和计算函数,显示结果。
现在,我需要逐步实现这些部分,并测试是否正常工作。
首先,词法分析函数:
def tokenize(expression):
tokens = []
i = 0
while i < len(expression):
if expression[i] in '+-*/^()':
tokens.append(expression[i])
i += 1
elif expression[i].isdigit() or expression[i] == '.':
num = []
while i < len(expression) and (expression[i].isdigit() or expression[i] == '.'):
num.append(expression[i])
i += 1
tokens.append(''.join(num))
elif expression[i] == ' ':
i += 1
else:
raise ValueError("无效字符: {}".format(expression[i]))
return tokens
但这样处理可能无法正确识别负数的情况,比如表达式“-3+4”会被分解为['-', '3', '+', '4'],而正确的应该是负数符号作为数字的一部分。或者可能需要判断减号是否出现在表达式开始或前一个token是运算符或左括号,此时视为负号。
例如,处理负数的逻辑:
在词法分析时,如果当前字符是减号,并且前一个token是运算符、左括号或者没有前一个token(即位置0),则视为负号,将后续数字作为负数处理。
这可能需要在词法分析时维护前一个token的类型。例如:
def tokenize(expression):
tokens = []
i = 0
while i < len(expression):
if expression[i] in '+-*/^()':
# 处理负号的情况
if expression[i] == '-' and (i == 0 or tokens and tokens[-1] in '+-*/^('):
# 可能是负号,检查后面是否有数字
if i + 1 < len(expression) and (expression[i+1].isdigit() or expression[i+1] == '.'):
num = ['-']
i += 1
while i < len(expression) and (expression[i].isdigit() or expression[i] == '.'):
num.append(expression[i])
i += 1
tokens.append(''.join(num))
continue
else:
tokens.append('-') # 否则作为减号处理
else:
tokens.append(expression[i])
i += 1
elif expression[i].isdigit() or expression[i] == '.':
num = []
while i < len(expression) and (expression[i].isdigit() or expression[i] == '.'):
num.append(expression[i])
i += 1
tokens.append(''.join(num))
elif expression[i] == ' ':
i += 1
else:
raise ValueError("无效字符: {}".format(expression[i]))
return tokens
这样,像“-3+4”会被分解为['-3', '+', '4'],而“3-4”会被分解为['3', '-', '4']。
接下来是中缀转后缀:
需要定义运算符的优先级,比如:
precedence = {
'+': 1,
'-': 1,
'*': 2,
'/': 2,
'^': 3,
'(': 0
}
然后,处理每个token:
def infix_to_postfix(tokens):
output = []
stack = []
for token in tokens:
if token.replace('.', '', 1).isdigit() or (token.startswith('-') and token[1:].replace('.', '', 1).isdigit()):
output.append(token)
elif token == '(':
stack.append(token)
elif token == ')':
while stack and stack[-1] != '(':
output.append(stack.pop())
stack.pop() # 弹出左括号
else:
while stack and precedence.get(stack[-1], 0) >= precedence.get(token, 0):
output.append(stack.pop())
stack.append(token)
while stack:
output.append(stack.pop())
return output
但这里可能需要处理运算符的结合性,比如指数运算符^是右结合的,但这里暂时忽略,或者调整比较条件。
接下来,计算后缀表达式:
def evaluate_postfix(postfix):
stack = []
for token in postfix:
if token.replace('.', '', 1).isdigit() or (token.startswith('-') and token[1:].replace('.', '', 1).isdigit()):
stack.append(float(token))
else:
b = stack.pop()
a = stack.pop() if stack else 0 # 处理单目运算符的情况,但这里假设所有运算符都是二元的
if token == '+':
stack.append(a + b)
elif token == '-':
stack.append(a - b)
elif token == '*':
stack.append(a * b)
elif token == '/':
if b == 0:
raise ValueError("除数不能为零")
stack.append(a / b)
elif token == '^':
stack.append(a ** b)
else:
raise ValueError("未知运算符: {}".format(token))
return stack[0]
不过,这里需要注意运算符的操作数顺序。例如,减法和除法需要按顺序a - b,所以弹出顺序是先b后a。例如,对于后缀表达式['3','4','-'],应该是3 -4吗?或者原中缀是3-4,对应的后缀是3 4 -,所以计算时弹出4,然后弹出3,执行3 -4?不对,应该是a - b,即3 -4吗?但实际应该是3 -4,结果-1。或者可能我搞反了顺序?
比如,对于中缀表达式3-4,对应的后缀是3 4 -,此时计算时弹出4,然后弹出3,做3-4=-1,正确。是的,所以顺序是对的。
现在,将这些函数整合到一个计算函数中:
def calculate(expression):
tokens = tokenize(expression)
postfix = infix_to_postfix(tokens)
result = evaluate_postfix(postfix)
return result
然后,测试这个函数是否正常工作。例如,表达式“3+5*2/(6-2)”应得到5.5。测试步骤如下:
1. 词法分析:['3', '+', '5', '*', '2', '/', '(', '6', '-', '2', ')']
这里可能需要调整tokenize函数,因为括号的处理可能有问题。原表达式是3+5*2/(6-2),所以正确分解应该是['3', '+', '5', '*', '2', '/', '(', '6', '-', '2', ')'],对吗?
不过,在代码中,遇到括号会被单独作为token,所以是正确的。
然后,中缀转后缀:
处理到括号时,会将其中的运算符优先处理。例如,括号内的6-2变为6 2 -,整个表达式转换为:
3 5 2 * 6 2 - / +
所以后缀表达式应该是['3', '5', '2', '*', '6', '2', '-', '/', '+']
计算过程:
5*2=10 → 压栈
6-2=4 → 压栈
然后执行10 /4=2.5 → 压栈
最后3+2.5=5.5 → 结果正确。
现在,编写界面部分:
使用Tkinter创建窗口,添加输入框和按钮。按钮包括数字0-9,运算符+-*/,括号,等于,清除等。
处理按钮点击事件,将字符添加到输入框,等于号时调用计算函数,显示结果。
例如:
import tkinter as tk
class Calculator:
def __init__(self, master):
self.master = master
master.title("计算器")
self.equation = tk.StringVar()
entry = tk.Entry(master, textvariable=self.equation, justify='right', font=('Arial', 14))
entry.grid(row=0, column=0, columnspan=4, padx=10, pady=10, sticky='nsew')
buttons = [
'7', '8', '9', '/',
'4', '5', '6', '*',
'1', '2', '3', '-',
'0', '.', '(', ')',
'C', '=', '+', '^'
]
row = 1
col = 0
for btn in buttons:
cmd = lambda x=btn: self.on_button_click(x)
tk.Button(master, text=btn, command=cmd, width=5, height=2).grid(row=row, column=col, padx=2, pady=2)
col += 1
if col > 3:
col = 0
row += 1
def on_button_click(self, char):
if char == 'C':
self.equation.set('')
elif char == '=':
try:
expr = self.equation.get()
result = calculate(expr)
self.equation.set(str(result))
except Exception as e:
self.equation.set("错误")
else:
current = self.equation.get()
current += char
self.equation.set(current)
root = tk.Tk()
calc = Calculator(root)
root.mainloop()
这里需要将之前的计算函数整合进去。需要注意错误处理,比如除零、无效表达式等,捕获异常并提示错误。
现在,测试整个代码是否工作。例如,输入3+5*2/(6-2),点击等号,应显示5.5。如果有错误,可能需要调试词法分析、转换或计算函数。
可能的问题点包括:
1. 词法分析没有正确处理负数。
2. 运算符优先级设置错误。
3. 后缀表达式转换错误。
4. 计算时操作数顺序错误。
例如,测试表达式“-3+4”应得到1。在词法分析阶段,应该分解为['-3', '+', '4'],转换成后缀表达式['-3', '4', '+'],计算结果为1。正确。
另一个测试表达式“(3+5)*2”,应得到16。词法分解为['(', '3', '+', '5', ')', '*', '2'],转换为后缀['3','5','+','2','*'],计算3+5=8,8*2=16。正确。
但可能存在的问题是,在tokenize函数中,如何处理负号,比如当表达式是“3*-4”时,应该分解为['3', '*', '-4']。原逻辑是否能处理这种情况?
例如,当遇到'*'后面的'-',此时前一个token是'*',属于运算符,所以触发负号处理,将后面的数字作为负数。所以分解为['3', '*', '-4'],转换后缀为['3', '-4', '*'],计算得到-12,正确。
这说明代码能够处理这种情况。
现在,综合所有代码,检查是否满足用户需求:不使用第三方库,不使用eval,完全手动解析和计算。
是的,Tkinter是Python标准库,所以允许使用。整个代码不依赖第三方库,且没有使用eval函数。
因此,完整的代码如下:
需要将tokenize、infix_to_postfix、evaluate_postfix函数整合到计算器类中,并处理可能的异常。
在界面部分,当用户点击等号时,调用calculate函数,并处理错误。
现在,将整个代码整合:
import tkinter as tk
def tokenize(expression):
tokens = []
i = 0
while i < len(expression):
if expression[i] in '+-*/^()':
if expression[i] == '-' and (i == 0 or (tokens and tokens[-1] in '+-*/^(')):
# 处理负号
if i + 1 < len(expression) and (expression[i+1].isdigit() or expression[i+1] == '.'):
num = ['-']
i += 1
while i < len(expression) and (expression[i].isdigit() or expression[i] == '.'):
num.append(expression[i])
i += 1
tokens.append(''.join(num))
continue
else:
tokens.append('-')
else:
tokens.append(expression[i])
i += 1
elif expression[i].isdigit() or expression[i] == '.':
num = []
while i < len(expression) and (expression[i].isdigit() or expression[i] == '.'):
num.append(expression[i])
i += 1
tokens.append(''.join(num))
elif expression[i] == ' ':
i += 1
else:
raise ValueError("无效字符")
return tokens
def infix_to_postfix(tokens):
precedence = {'+':1, '-':1, '*':2, '/':2, '^':3, '(':0}
output = []
stack = []
for token in tokens:
if token.replace('.', '', 1).isdigit() or (token.startswith('-') and token[1:].replace('.', '', 1).isdigit()):
output.append(token)
elif token == '(':
stack.append(token)
elif token == ')':
while stack and stack[-1] != '(':
output.append(stack.pop())
stack.pop()
else:
while stack and precedence.get(token, 0) <= precedence.get(stack[-1], 0):
output.append(stack.pop())
stack.append(token)
while stack:
output.append(stack.pop())
return output
def evaluate_postfix(postfix):
stack = []
for token in postfix:
if token.replace('.', '', 1).isdigit() or (token.startswith('-') and token[1:].replace('.', '', 1).isdigit()):
stack.append(float(token))
else:
b = stack.pop()
a = stack.pop()
if token == '+':
stack.append(a + b)
elif token == '-':
stack.append(a - b)
elif token == '*':
stack.append(a * b)
elif token == '/':
if b == 0:
raise ZeroDivisionError("除数不能为零")
stack.append(a / b)
elif token == '^':
stack.append(a ** b)
else:
raise ValueError("未知运算符")
return stack[0] if stack else 0
def calculate(expression):
try:
tokens = tokenize(expression)
postfix = infix_to_postfix(tokens)
result = evaluate_postfix(postfix)
return result
except:
raise
class Calculator:
def __init__(self, master):
self.master = master
master.title("计算器")
self.equation = tk.StringVar()
entry = tk.Entry(master, textvariable=self.equation, justify='right', font=('Arial', 14))
entry.grid(row=0, column=0, columnspan=4, padx=10, pady=10, sticky='nsew')
buttons = [
'7', '8', '9', '/', '^',
'4', '5', '6', '*', '(',
'1', '2', '3', '-', ')',
'0', '.', 'C', '=', '+'
]
row, col = 1, 0
for btn in buttons:
if btn == '^':
cmd = lambda x='^': self.on_button_click(x)
else:
cmd = lambda x=btn: self.on_button_click(x)
tk.Button(master, text=btn, command=cmd, width=5, height=2).grid(row=row, column=col, padx=2, pady=2)
col += 1
if col > 4:
col = 0
row += 1
def on_button_click(self, char):
if char == 'C':
self.equation.set('')
elif char == '=':
try:
expr = self.equation.get()
result = calculate(expr)
self.equation.set(str(result))
except:
self.equation.set("错误")
else:
current = self.equation.get()
self.equation.set(current + char)
if __name__ == "__main__":
root = tk.Tk()
calc = Calculator(root)
root.mainloop()
注意,这里可能需要对按钮布局进行调整,确保所有按钮正确显示。例如,原按钮列表可能列数超过4,导致布局问题。可能需要调整按钮排列。
在当前的按钮列表中,按钮的数量为19个,可能需要调整布局的行列分布。例如,可以分成5列:
buttons = [
'7', '8', '9', '/', '^',
'4', '5', '6', '*', '(',
'1', '2', '3', '-', ')',
'0', '.', 'C', '=', '+'
]
然后,每行5列,所以grid的columnspan可能需要调整,或者按钮的排列方式。例如,原来的代码中,列循环到4时换行。这可能需要将Entry的columnspan设为5,并调整按钮的排列。
此外,可能需要处理运算符的优先级和指数运算的正确性。例如,验证2^3^2是否正确处理为2^(3^2)=512还是(2^3)^2=64,这取决于运算符的结合性。在Shunting-yard算法中,指数运算符通常是右结合的,所以应该调整infix_to_postfix函数中的比较逻辑。
例如,在infix_to_postfix函数中,当当前运算符的优先级等于栈顶运算符的优先级时,如果运算符是右结合的,则不入栈。对于指数运算符^,通常右结合,所以比较时应将条件改为precedence.get(token, 0) < precedence.get(stack[-1], 0)。或者调整比较方式。
例如,修改infix_to_postfix中的循环条件:
elif token in precedence:
while stack and (
(precedence[token] < precedence.get(stack[-1], 0)) or
(precedence[token] == precedence.get(stack[-1], 0) and token != '^')
):
output.append(stack.pop())
stack.append(token)
这样,对于右结合的^运算符,当优先级相同时不会弹出,从而保持右结合。
但为了简化,可能暂时不处理右结合,假设所有运算符左结合。这样,2^3^2会被处理为(2^3)^2=64。这可能与数学中的常规运算不符,因为通常2^3^2=2^(3^2)=512。因此,需要调整算法以处理右结合的运算符。
这可能会增加代码复杂度,但用户可能期望指数运算符是右结合的。因此,需要修改中缀转后缀的逻辑。
修改infix_to_postfix函数中的运算符比较部分:
def infix_to_postfix(tokens):
precedence = {'+':1, '-':1, '*':2, '/':2, '^':4, '(':0}
associativity = {'^': 'right', '+': 'left', '-': 'left', '*': 'left', '/': 'left'}
output = []
stack = []
for token in tokens:
if token.replace('.', '', 1).isdigit() or (token.startswith('-') and token[1:].replace('.', '', 1).isdigit()):
output.append(token)
elif token == '(':
stack.append(token)
elif token == ')':
while stack and stack[-1] != '(':
output.append(stack.pop())
stack.pop()
else:
while stack and (
(associativity.get(token, 'left') == 'left' and precedence.get(token, 0) <= precedence.get(stack[-1], 0)) or
(associativity.get(token, 'left') == 'right' and precedence.get(token, 0) < precedence.get(stack[-1], 0))
):
output.append(stack.pop())
stack.append(token)
while stack:
output.append(stack.pop())
return output
这样,对于右结合的^运算符,只有当当前运算符的优先级严格小于栈顶运算符时才会弹出。例如,当前token是^,栈顶也是^,由于右结合,所以不弹出,继续压栈。这样,表达式“2^3^2”会被转换为2 3 2 ^ ^,即计算为2^(3^2)=512。
这需要测试是否正确。例如,中缀表达式2^3^2对应的后缀表达式应该是2 3 2 ^ ^,计算顺序是3^2=9,然后2^9=512。
因此,在evaluate_postfix函数中,处理^运算符时,正确的顺序是a^b,其中a是栈中弹出的第二个元素,b是第一个元素。例如,对于后缀['2','3','^','2','^'],计算顺序:
3^2=9 → 压栈,然后2^9=512。但根据当前的后缀转换,原表达式2^3^2会被转换为2 3 2 ^ ^,因此计算顺序:
栈操作:
遇到2,压栈 → [2]
遇到3,压栈 → [2,3]
遇到2,压栈 → [2,3,2]
遇到^ → 弹出2和3,计算3^2=9 → 压栈 → [2,9]
遇到^ → 弹出9和2,计算2^9=512 → 压栈 → [512]
正确。
因此,需要调整infix_to_postfix函数中的处理逻辑,以支持右结合的运算符。
现在,将这些调整后的函数整合到代码中,并测试是否正确处理指数运算的右结合性。
综上,完整的代码应该满足用户需求,不使用eval,不依赖第三方库,使用Tkinter作为界面,手动解析和计算表达式。同时处理运算符优先级、括号、负数等情况。</think>以下是一个基于Python标准库tkinter实现的计算器完整代码,不使用eval函数和第三方库,支持基本四则运算、括号和幂运算:
```python
import tkinter as tk
# 数学表达式解析核心模块
def tokenize(expression):
"""将表达式字符串转换为标记列表"""
tokens = []
i = 0
while i < len(expression):
if expression[i] in '+-*/^()':
if expression[i] == '-' and (i == 0 or tokens[-1] in '+-*/^(' if tokens else True):
if i+1 < len(expression) and (expression[i+1].isdigit() or expression[i+1] == '.'):
num = ['-']
i += 1
while i < len(expression) and (expression[i].isdigit() or expression[i] == '.'):
num.append(expression[i])
i += 1
tokens.append(''.join(num))
continue
tokens.append(expression[i])
i += 1
elif expression[i].isdigit() or expression[i] == '.':
num = []
while i < len(expression) and (expression[i].isdigit() or expression[i] == '.'):
num.append(expression[i])
i += 1
tokens.append(''.join(num))
elif expression[i] == ' ':
i += 1
else:
raise ValueError("无效字符")
return tokens
def infix_to_postfix(tokens):
"""将中缀表达式转换为后缀表达式"""
precedence = {'+':1, '-':1, '*':2, '/':2, '^':3, '(':0}
associativity = {'^': 'right', '+': 'left', '-': 'left', '*': 'left', '/': 'left'}
output = []
stack = []
for token in tokens:
if token.replace('.', '', 1).isdigit() or (token.startswith('-') and token[1:].replace('.', '', 1).isdigit()):
output.append(token)
elif token == '(':
stack.append(token)
elif token == ')':
while stack and stack[-1] != '(':
output.append(stack.pop())
stack.pop()
else:
while stack and (
(associativity.get(token, 'left') == 'left' and precedence.get(token, 0) <= precedence.get(stack[-1], 0)) or
(associativity.get(token, 'left') == 'right' and precedence.get(token, 0) < precedence.get(stack[-1], 0))
):
output.append(stack.pop())
stack.append(token)
while stack:
output.append(stack.pop())
return output
def evaluate_postfix(postfix):
"""计算后缀表达式"""
stack = []
for token in postfix:
if token.replace('.', '', 1).isdigit() or (token.startswith('-') and token[1:].replace('.', '', 1).isdigit()):
stack.append(float(token))
else:
b = stack.pop()
a = stack.pop()
if token == '+':
stack.append(a + b)
elif token == '-':
stack.append(a - b)
elif token == '*':
stack.append(a * b)
elif token == '/':
if b == 0:
raise ZeroDivisionError("除数不能为零")
stack.append(a / b)
elif token == '^':
stack.append(a ** b)
else:
raise ValueError("未知运算符")
return stack[0] if stack else 0
# GUI界面实现
class Calculator:
def __init__(self, master):
self.master = master
master.title("科学计算器")
# 创建显示框
self.equation = tk.StringVar()
entry = tk.Entry(master, textvariable=self.equation, font=('Arial', 16),
justify='right', bd=10, insertwidth=4, width=18)
entry.grid(row=0, column=0, columnspan=5, padx=10, pady=10)
# 按钮布局
buttons = [
'7', '8', '9', '/', '^',
'4', '5', '6', '*', '(',
'1', '2', '3', '-', ')',
'0', '.', 'C', '=', '+'
]
# 生成按钮
row, col = 1, 0
for btn in buttons:
cmd = lambda x=btn: self.on_button_click(x)
tk.Button(master, text=btn, command=cmd,
width=4, height=2, font=('Arial', 14)).grid(
row=row, column=col, padx=2, pady=2)
col += 1
if col > 4:
col = 0
row += 1
def on_button_click(self, char):
"""按钮点击事件处理"""
if char == 'C':
self.equation.set('')
elif char == '=':
try:
result = evaluate_postfix(infix_to_postfix(tokenize(self.equation.get())))
self.equation.set(str(round(result, 10)))
except:
self.equation.set("错误")
else:
current = self.equation.get()
self.equation.set(current + char)
if __name__ == "__main__":
root = tk.Tk()
calc = Calculator(root)
root.mainloop()
```
阅读全文
相关推荐



















