手把手教你用 Python 做计算器!从命令行到图形界面(附完整 GUI 源码)

admin | 女足世界杯预测

计算器是日常生活和工作中常用的工具,也是学习编程的经典入门项目。本文将使用 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 编程的基础知识,还为你开发更复杂的应用程序打下了坚实的基础。建议你继续扩展这个计算器,添加更多功能,进一步提升自己的编程技能!