计算器是日常生活和工作中常用的工具,也是学习编程的经典入门项目。本文将使用 Python 实现一个功能完备的计算器,从基础的命令行版本逐步升级到具有现代界面的图形化应用程序。通过这个项目,你将学习 Python 编程的核心概念,包括变量、数据类型、控制结构、函数、模块以及图形界面开发。
一、计算器基本原理与流程
计算器的基本功能是接收用户输入的数学表达式,计算其结果并显示出来。我们可以从最简单的命令行版本开始实现:
def calculate(expression):
"""计算数学表达式的值"""
try:
# 使用 Python 的 eval() 函数计算表达式
result = eval(expression)
return result
except Exception as e:
print(f"错误: {e}")
return None
def simple_calculator():
"""简易计算器主函数"""
print("欢迎使用简易计算器!")
print("支持基本的数学运算,如 +, -, *, /, ** 等")
print("输入 'q' 退出计算器")
while True:
expression = input("请输入数学表达式: ")
if expression.lower() == 'q':
print("感谢使用计算器,再见!")
break
result = calculate(expression)
if result is not None:
print(f"计算结果: {result}")
# 启动计算器
simple_calculator()
知识点解析:
- eval() 函数:Python 内置函数,用于执行字符串表达式并返回结果
- 异常处理:使用
try-except
捕获并处理计算过程中可能出现的错误 - 循环结构:使用
while True
创建无限循环,直到用户输入 ‘q’ 退出 - 用户交互:使用
input()
获取用户输入,print()
输出结果
二、计算器功能扩展
我们可以对基础版本进行扩展,增加以下功能:
- 历史记录:保存用户的计算历史
- 内存功能:支持将结果存入内存或从内存中读取
- 更丰富的运算:支持三角函数、对数函数等高级运算
- 表达式验证:在计算前验证表达式的合法性
下面是扩展后的代码:
import math
import re
class Calculator:
def __init__(self):
self.memory = 0 # 内存值
self.history = [] # 历史记录
self.functions = {
'sin': math.sin,
'cos': math.cos,
'tan': math.tan,
'sqrt': math.sqrt,
'log': math.log,
'exp': math.exp
}
def calculate(self, expression):
"""计算数学表达式的值"""
# 处理内存操作
expression = expression.replace('M+', f'+{self.memory}')
expression = expression.replace('M-', f'-{self.memory}')
expression = expression.replace('MR', str(self.memory))
# 替换函数调用
for func_name, func in self.functions.items():
pattern = rf'{func_name}\(([^)]+)\)'
while re.search(pattern, expression):
match = re.search(pattern, expression)
arg = match.group(1)
try:
result = func(float(arg))
expression = expression.replace(match.group(0), str(result))
except Exception as e:
return f"错误: 函数 {func_name} 参数错误"
try:
# 计算表达式
result = eval(expression)
# 更新历史记录
self.history.append(f"{expression} = {result}")
return result
except ZeroDivisionError:
return "错误: 除数不能为零"
except Exception as e:
return f"错误: {e}"
def show_history(self):
"""显示计算历史"""
if not self.history:
print("暂无计算历史")
return
print("\n=== 计算历史 ===")
for i, entry in enumerate(self.history, 1):
print(f"{i}. {entry}")
def clear_history(self):
"""清除计算历史"""
self.history = []
print("计算历史已清除")
def set_memory(self, value):
"""设置内存值"""
try:
self.memory = float(value)
print(f"内存已设置为: {self.memory}")
except ValueError:
print("错误: 无效的内存值")
def clear_memory(self):
"""清除内存值"""
self.memory = 0
print("内存已清除")
def advanced_calculator():
"""高级计算器主函数"""
calc = Calculator()
print("欢迎使用高级计算器!")
print("支持基本运算、函数计算、内存功能")
print("支持的函数: sin, cos, tan, sqrt, log, exp")
print("内存操作: M+ (加内存), M- (减内存), MR (读内存)")
print("输入 'h' 查看历史记录, 'c' 清除历史, 'm' 设置内存")
print("输入 'mc' 清除内存, 'q' 退出计算器")
while True:
expression = input("请输入数学表达式: ")
if expression.lower() == 'q':
print("感谢使用计算器,再见!")
break
elif expression.lower() == 'h':
calc.show_history()
elif expression.lower() == 'c':
calc.clear_history()
elif expression.lower() == 'm':
value = input("请输入内存值: ")
calc.set_memory(value)
elif expression.lower() == 'mc':
calc.clear_memory()
else:
result = calc.calculate(expression)
print(f"计算结果: {result}")
# 启动计算器
advanced_calculator()
知识点解析:
- 类的设计:使用
Calculator
类封装计算器的所有功能 - 正则表达式:使用
re
模块处理函数调用和内存操作的替换 - 数学函数:使用
math
模块提供的各种数学函数 - 数据结构:使用列表保存计算历史,使用变量保存内存值
- 模块化设计:将不同功能封装为独立的方法,提高代码的可维护性
三、图形界面实现
使用 Tkinter 库创建一个图形界面版本的计算器:
import tkinter as tk
from tkinter import ttk
import math
import re
class CalculatorGUI:
def __init__(self, root):
self.root = root
self.root.title("科学计算器")
self.root.geometry("400x550")
self.root.resizable(False, False)
self.root.configure(bg="#f0f0f0")
# 设置中文字体
self.font = ("SimHei", 12)
# 计算器逻辑
self.memory = 0
self.history = []
self.current_expression = ""
self.last_result = None
# 创建界面
self.create_widgets()
def create_widgets(self):
"""创建计算器界面"""
# 历史记录按钮
history_frame = tk.Frame(self.root, bg="#f0f0f0")
history_frame.pack(pady=5, fill=tk.X, padx=10)
history_button = tk.Button(history_frame, text="历史记录", font=self.font,
command=self.show_history, bg="#2196F3", fg="white")
history_button.pack(side=tk.RIGHT, padx=5)
# 显示屏
display_frame = tk.Frame(self.root, bg="#f0f0f0")
display_frame.pack(pady=10, fill=tk.X, padx=10)
self.expression_var = tk.StringVar()
self.expression_var.set("")
self.result_var = tk.StringVar()
self.result_var.set("0")
expression_label = tk.Label(display_frame, textvariable=self.expression_var,
font=("SimHei", 14), bg="white", anchor=tk.E, padx=10, pady=5)
expression_label.pack(fill=tk.X)
result_label = tk.Label(display_frame, textvariable=self.result_var,
font=("SimHei", 24, "bold"), bg="white", anchor=tk.E, padx=10, pady=5)
result_label.pack(fill=tk.X)
# 按钮框架
buttons_frame = tk.Frame(self.root, bg="#f0f0f0")
buttons_frame.pack(pady=10, fill=tk.BOTH, expand=True, padx=10)
# 按钮布局
buttons = [
['7', '8', '9', '/', 'C'],
['4', '5', '6', '*', 'CE'],
['1', '2', '3', '-', '√'],
['0', '.', '=', '+', '^'],
['(', ')', 'M+', 'M-', 'MR']
]
for i, row in enumerate(buttons):
for j, text in enumerate(row):
button = tk.Button(buttons_frame, text=text, font=self.font,
width=7, height=2, bg="#e0e0e0",
command=lambda txt=text: self.button_click(txt))
button.grid(row=i, column=j, padx=2, pady=2, sticky="nsew")
# 配置网格权重,使按钮均匀分布
for i in range(len(buttons)):
buttons_frame.grid_rowconfigure(i, weight=1)
for j in range(len(buttons[0])):
buttons_frame.grid_columnconfigure(j, weight=1)
def button_click(self, text):
"""处理按钮点击事件"""
if text == '=':
self.calculate()
elif text == 'C':
self.current_expression = ""
self.update_display()
elif text == 'CE':
# 删除最后一个字符
self.current_expression = self.current_expression[:-1]
self.update_display()
elif text == '√':
self.current_expression += "sqrt("
self.update_display()
elif text == '^':
self.current_expression += "**"
self.update_display()
elif text == 'M+':
self.current_expression += f"+{self.memory}"
self.update_display()
elif text == 'M-':
self.current_expression += f"-{self.memory}"
self.update_display()
elif text == 'MR':
self.current_expression += str(self.memory)
self.update_display()
else:
self.current_expression += text
self.update_display()
def update_display(self):
"""更新显示屏"""
self.expression_var.set(self.current_expression)
# 如果表达式为空,显示0
if not self.current_expression:
self.result_var.set("0")
def calculate(self):
"""计算表达式结果"""
if not self.current_expression:
return
expression = self.current_expression
# 替换函数调用
pattern = r'sqrt\(([^)]+)\)'
while re.search(pattern, expression):
match = re.search(pattern, expression)
arg = match.group(1)
expression = expression.replace(match.group(0), f"math.sqrt({arg})")
# 替换幂运算
expression = expression.replace('^', '**')
try:
# 计算表达式
result = eval(expression)
# 更新历史记录
self.history.append(f"{self.current_expression} = {result}")
# 更新显示
self.result_var.set(str(result))
self.last_result = result
# 如果用户想继续计算,使用结果作为新表达式的开始
self.current_expression = str(result)
except ZeroDivisionError:
self.result_var.set("错误: 除数不能为零")
except Exception as e:
self.result_var.set(f"错误: {str(e)}")
def show_history(self):
"""显示历史记录"""
if not self.history:
tk.messagebox.showinfo("历史记录", "暂无计算历史")
return
# 创建历史记录窗口
history_window = tk.Toplevel(self.root)
history_window.title("计算历史")
history_window.geometry("400x300")
history_window.resizable(False, False)
history_window.configure(bg="#f0f0f0")
history_window.transient(self.root) # 使窗口成为主窗口的子窗口
# 创建历史记录列表
history_text = tk.Text(history_window, font=self.font, bg="white", wrap=tk.WORD)
history_text.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 插入历史记录
for i, entry in enumerate(self.history, 1):
history_text.insert(tk.END, f"{i}. {entry}\n\n")
# 使文本框只读
history_text.config(state=tk.DISABLED)
# 关闭按钮
close_button = tk.Button(history_window, text="关闭", font=self.font,
command=history_window.destroy, bg="#f44336", fg="white")
close_button.pack(pady=10)
# 启动计算器
if __name__ == "__main__":
root = tk.Tk()
app = CalculatorGUI(root)
root.mainloop()
知识点解析:
- Tkinter 基础:使用 Tkinter 创建窗口、标签、按钮等界面元素
- 事件处理:通过绑定按钮点击事件触发相应的函数
- 界面布局:使用
grid()
方法进行网格布局,实现计算器按钮的排列 - 对话框:创建模态对话框用于显示计算历史
- 表达式解析:处理特殊函数(如平方根)和操作符(如幂运算)的解析
四、计算器优化与扩展
我们可以进一步优化和扩展计算器,例如:
- 添加更多功能:支持三角函数、对数函数等更多数学函数
- 美化界面:使用更现代的UI设计,添加动画和过渡效果
- 支持键盘输入:允许用户使用键盘输入表达式
- 保存历史记录:将历史记录保存到文件中,实现持久化存储
下面是一个添加更多功能的示例:
# 在CalculatorGUI类中添加以下代码
def create_widgets(self):
# ... 原有代码 ...
# 第二行按钮(函数按钮)
function_buttons = ['sin', 'cos', 'tan', 'log', 'exp']
function_frame = tk.Frame(self.root, bg="#f0f0f0")
function_frame.pack(pady=5, fill=tk.X, padx=10)
for i, text in enumerate(function_buttons):
button = tk.Button(function_frame, text=text, font=self.font,
width=7, height=1, bg="#d0d0d0",
command=lambda txt=text: self.button_click(txt))
button.pack(side=tk.LEFT, padx=2, pady=2, fill=tk.BOTH, expand=True)
# ... 原有代码 ...
def button_click(self, text):
# ... 原有代码 ...
elif text in ['sin', 'cos', 'tan', 'log', 'exp']:
self.current_expression += f"{text}("
self.update_display()
# ... 原有代码 ...
def calculate(self):
# ... 原有代码 ...
# 替换更多函数调用
for func in ['sin', 'cos', 'tan', 'log', 'exp']:
pattern = rf'{func}\(([^)]+)\)'
while re.search(pattern, expression):
match = re.search(pattern, expression)
arg = match.group(1)
expression = expression.replace(match.group(0), f"math.{func}({arg})")
# ... 原有代码 ...
总结
通过这个计算器项目,我们学习了 Python 编程的多个方面:
- 基础语法:变量、数据类型、控制结构、函数等
- 面向对象编程:使用类和对象组织代码,提高代码的可维护性
- 模块化设计:将不同功能封装为独立的模块和方法
- 图形界面开发:使用 Tkinter 创建交互式应用程序
- 用户交互与体验:设计友好的界面和流畅的用户体验
这个项目不仅帮助你掌握了 Python 编程的基础知识,还为你开发更复杂的应用程序打下了坚实的基础。建议你继续扩展这个计算器,添加更多功能,进一步提升自己的编程技能!