实战项目1:计算器¶
项目概述¶
实现一个功能完整的计算器,支持基本运算、科学计算和函数绘图功能。这是一个很好的入门项目,可以帮助你学习GUI编程、事件处理和数学函数的使用。
学习目标¶
- 掌握Python GUI编程(tkinter)
- 学习事件驱动编程
- 理解表达式求值的原理
- 掌握异常处理
- 学习图形绘制(matplotlib)
功能需求¶
基本功能¶
- 基本运算:加、减、乘、除
- 括号优先级:支持括号改变运算顺序
- 清除功能:清除当前输入或全部清除
- 小数运算:支持小数点运算
进阶功能¶
- 科学计算:三角函数、对数、指数、根号
- 记忆功能:存储和读取计算结果
- 历史记录:显示计算历史
- 错误处理:处理各种错误情况
高级功能¶
- 函数绘图:绘制数学函数图像
- 进制转换:二进制、八进制、十六进制转换
- 复数运算:支持复数计算
实现方案¶
方案一:使用tkinter(推荐)¶
import tkinter as tk
from tkinter import messagebox
import math
class Calculator:
"""计算器类"""
def __init__(self, root):
self.root = root
self.root.title("计算器")
self.root.geometry("400x600")
self.root.resizable(False, False)
self.expression = ""
self.memory = 0
self.history = []
self.create_widgets()
def create_widgets(self):
"""创建界面组件"""
# 显示区域
self.display_var = tk.StringVar()
self.display = tk.Entry(
self.root,
textvariable=self.display_var,
font=('Arial', 24),
justify='right',
state='readonly'
)
self.display.pack(fill=tk.X, padx=10, pady=10)
# 按钮布局
buttons = [
('C', '±', '%', '÷'),
('7', '8', '9', '×'),
('4', '5', '6', '-'),
('1', '2', '3', '+'),
('0', '.', '=', 'M')
]
button_frame = tk.Frame(self.root)
button_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
for i, row in enumerate(buttons):
for j, text in enumerate(row):
btn = tk.Button(
button_frame,
text=text,
font=('Arial', 18),
command=lambda t=text: self.button_click(t)
)
btn.grid(row=i, column=j, sticky="nsew", padx=2, pady=2)
button_frame.grid_columnconfigure(j, weight=1)
button_frame.grid_rowconfigure(i, weight=1)
def button_click(self, text):
"""按钮点击事件处理"""
if text == 'C':
self.expression = ""
elif text == '=':
try:
result = self.evaluate(self.expression)
self.history.append(f"{self.expression} = {result}")
self.expression = str(result)
except Exception as e:
messagebox.showerror("错误", str(e))
self.expression = ""
elif text == 'M':
self.memory = float(self.expression) if self.expression else 0
messagebox.showinfo("记忆", f"已存储: {self.memory}")
elif text == '±':
if self.expression:
if self.expression[0] == '-':
self.expression = self.expression[1:]
else:
self.expression = '-' + self.expression
else:
self.expression += text
self.display_var.set(self.expression)
def evaluate(self, expression):
"""计算表达式"""
# 替换运算符
expression = expression.replace('×', '*').replace('÷', '/')
# 安全评估
allowed_chars = set('0123456789+-*/.()')
if not all(c in allowed_chars or c.isspace() for c in expression):
raise ValueError("包含非法字符")
try:
result = eval(expression)
return result
except ZeroDivisionError:
raise ValueError("除数不能为零")
except SyntaxError:
raise ValueError("表达式语法错误")
except:
raise ValueError("计算错误")
# 创建主窗口
root = tk.Tk()
calculator = Calculator(root)
root.mainloop()
方案二:命令行计算器¶
import re
class CommandLineCalculator:
"""命令行计算器"""
def __init__(self):
self.memory = 0
self.history = []
def evaluate(self, expression):
"""计算表达式"""
# 替换运算符
expression = expression.replace('^', '**')
# 安全检查
allowed = re.compile(r'^[\d+\-*/(). ]+$')
if not allowed.match(expression):
raise ValueError("包含非法字符")
try:
result = eval(expression)
self.history.append(f"{expression} = {result}")
return result
except ZeroDivisionError:
raise ValueError("除数不能为零")
except:
raise ValueError("表达式错误")
def run(self):
"""运行计算器"""
print("=== 命令行计算器 ===")
print("输入表达式进行计算,输入 'exit' 退出")
print("输入 'history' 查看历史记录")
print("输入 'memory' 查看存储的值")
print("输入 'M' 存储当前结果到内存")
print("输入 'R' 读取内存中的值\n")
while True:
try:
expression = input(">>> ")
if expression.lower() == 'exit':
break
elif expression.lower() == 'history':
for record in self.history[-10:]: # 显示最近10条
print(f" {record}")
elif expression.lower() == 'memory':
print(f"内存: {self.memory}")
elif expression == 'M':
if self.history:
self.memory = float(self.history[-1].split(' = ')[1])
print(f"已存储: {self.memory}")
else:
print("没有可存储的结果")
elif expression == 'R':
print(f"内存: {self.memory}")
else:
result = self.evaluate(expression)
print(f"= {result}")
except ValueError as e:
print(f"错误: {e}")
except KeyboardInterrupt:
print("\n再见!")
break
except Exception as e:
print(f"错误: {e}")
# 运行
if __name__ == "__main__":
calc = CommandLineCalculator()
calc.run()
扩展功能¶
1. 添加科学计算功能¶
def scientific_calculate(self, expression):
"""科学计算"""
import math
# 替换函数名
expression = expression.replace('sin', 'math.sin')
expression = expression.replace('cos', 'math.cos')
expression = expression.replace('tan', 'math.tan')
expression = expression.replace('log', 'math.log10')
expression = expression.replace('ln', 'math.log')
expression = expression.replace('sqrt', 'math.sqrt')
expression = expression.replace('exp', 'math.exp')
expression = expression.replace('pi', 'math.pi')
expression = expression.replace('e', 'math.e')
return self.evaluate(expression)
2. 添加函数绘图功能¶
import matplotlib.pyplot as plt
import numpy as np
def plot_function(self, function_str, x_range=(-10, 10), points=1000):
"""绘制函数图像"""
try:
# 创建x值
x = np.linspace(x_range[0], x_range[1], points)
# 计算y值
y = []
for xi in x:
try:
# 替换x变量
expr = function_str.replace('x', str(xi))
yi = eval(expr)
y.append(yi)
except:
y.append(np.nan)
# 绘制图像
plt.figure(figsize=(10, 6))
plt.plot(x, y, label=f'y = {function_str}')
plt.axhline(y=0, color='k', linestyle='-', linewidth=0.5)
plt.axvline(x=0, color='k', linestyle='-', linewidth=0.5)
plt.grid(True, linestyle='--', alpha=0.6)
plt.xlabel('x')
plt.ylabel('y')
plt.title(f'函数图像: y = {function_str}')
plt.legend()
plt.show()
except Exception as e:
print(f"绘图错误: {e}")
# 使用示例
# plot_function('x**2') # 绘制抛物线
# plot_function('math.sin(x)') # 绘制正弦函数
# plot_function('math.exp(-x**2)') # 绘制高斯函数
3. 添加进制转换功能¶
def convert_base(number, from_base, to_base):
"""进制转换"""
# 将任意进制转换为十进制
decimal = int(str(number), from_base)
# 将十进制转换为目标进制
if to_base == 2:
return bin(decimal)[2:]
elif to_base == 8:
return oct(decimal)[2:]
elif to_base == 10:
return str(decimal)
elif to_base == 16:
return hex(decimal)[2:].upper()
else:
raise ValueError("不支持的进制")
# 使用示例
# convert_base(255, 10, 2) # 十进制255转二进制: '11111111'
# convert_base('FF', 16, 10) # 十六进制FF转十进制: '255'
# convert_base('1010', 2, 16) # 二进制1010转十六进制: 'A'
测试用例¶
import unittest
class TestCalculator(unittest.TestCase):
"""计算器测试"""
def setUp(self):
self.calc = Calculator(None)
def test_basic_operations(self):
"""测试基本运算"""
self.assertEqual(self.calc.evaluate("1+1"), 2)
self.assertEqual(self.calc.evaluate("2*3"), 6)
self.assertEqual(self.calc.evaluate("10-5"), 5)
self.assertEqual(self.calc.evaluate("8/2"), 4)
def test_operator_precedence(self):
"""测试运算符优先级"""
self.assertEqual(self.calc.evaluate("2+3*4"), 14)
self.assertEqual(self.calc.evaluate("(2+3)*4"), 20)
self.assertEqual(self.calc.evaluate("10-2*3"), 4)
def test_decimal_operations(self):
"""测试小数运算"""
self.assertAlmostEqual(self.calc.evaluate("1.5+2.5"), 4.0)
self.assertAlmostEqual(self.calc.evaluate("3.14*2"), 6.28)
def test_error_handling(self):
"""测试错误处理"""
with self.assertRaises(ValueError):
self.calc.evaluate("1/0")
with self.assertRaises(ValueError):
self.calc.evaluate("1+")
if __name__ == '__main__':
unittest.main()
使用说明¶
GUI版本¶
- 运行程序:
python calculator_gui.py - 点击按钮输入表达式
- 按 "=" 计算结果
- 按 "C" 清除输入
- 按 "M" 存储结果到内存
命令行版本¶
- 运行程序:
python calculator_cli.py - 输入表达式并回车
- 输入 "exit" 退出
- 输入 "history" 查看历史
扩展建议¶
- 添加更多科学函数:反三角函数、双曲函数等
- 支持变量定义:定义变量和函数
- 添加主题切换:深色/浅色主题
- 支持键盘输入:使用键盘快捷键
- 添加单位转换:长度、重量、温度等
- 支持复数运算:复数四则运算
- 添加历史记录保存:将历史保存到文件
- 支持表达式解析:解析和显示中间结果
学习要点¶
通过这个项目,你将学习到:
- GUI编程:tkinter的基本使用
- 事件处理:按钮点击、键盘事件
- 字符串处理:表达式解析
- 异常处理:错误检测和处理
- 数据结构:列表、字典的使用
- 模块化编程:代码组织和复用
- 测试驱动开发:编写测试用例
相关资源¶
开始动手吧! 从简单的功能开始,逐步添加更多特性。祝你编程愉快!