commit b25f9f97b5e34cd1415e73d5f87b226b5c219d24 Author: 鲁学坤 Date: Sat Apr 19 23:26:19 2025 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ecb2c23 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.venv/ +__pycache__/ +.pytest_cache/ +.idea/ diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/calculator.iml b/.idea/calculator.iml new file mode 100644 index 0000000..52ee91d --- /dev/null +++ b/.idea/calculator.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..4f33806 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..ff2a428 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..6aa3d5c --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..503c67f --- /dev/null +++ b/README.md @@ -0,0 +1,88 @@ +# 简易计算器GUI程序 + +![Calculator Screenshot](screenshot.png) + +[![Python Version](https://img.shields.io/badge/python-3.7%2B-blue)](https://www.python.org/) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) + +## 功能特性 + +- **四则运算**:支持加(+)、减(-)、乘(×)、除(÷)基本运算 +- **连续计算**:自动保留计算结果用于后续运算 +- **智能显示**: + - 自动去除小数点后多余的零 + - 大数字自动适应显示区域 +- **错误处理**: + - 除零错误检测与提示 + - 无效操作拦截 + - 异常崩溃防护 +- **交互设计**: + - 退格键(←)支持 + - 一键清零(C)功能 + - 实时输入反馈 +- **界面特性**: + - 半透明窗口效果 + - 响应式布局 + - 中文宋体显示 + +## 快速开始 + +### 环境要求 +- Python 3.7+ +- Tkinter(通常随Python默认安装) + +### 安装与运行 +```bash +1. 克隆仓库 +git clone https://github.com/yourusername/calculator-gui.git +2. 进入项目目录 +cd calculator-gui +3. 运行程序 +python calculator.py +``` + + +## 技术细节 + +### 技术栈 +- **核心框架**: Python标准库 Tkinter +- **状态管理**: 全局变量跟踪模式 +- **错误处理**: Try/Except异常捕获机制 +- **界面布局**: Grid网格布局系统 + +### 项目结构 +```python +calculator.py +├── 全局状态变量 # current_input, first_operand, operator +├── 样式配置常量 # COLORS, FONT, LAYOUT +├── 事件处理函数 # on_number_click, on_operator_click, calculate等 +├── UI构建模块 # 结果显示区、操作按钮矩阵、等号按钮 +└── 主程序循环 # root.mainloop() +``` + +## 版本历史 + +| 版本号 | 作者 | 发布日期 | 主要变更 | +|--------|----|------------|----------------------| +| v1.0.0 | 鲁豫 | 2025-04-15 | 完善代码注释和结构 | + + +## 贡献指南 + +欢迎通过以下方式参与项目改进: +1. 提交Issue报告问题 +2. Fork仓库并提交Pull Request +3. 优化代码注释或文档 + +**开发团队**: +- 程序员:鲁豫 \<2085520973@qq.com> +- 代码审核:Xuekun Lu \ + +## 学习资源 + +- [Tkinter官方文档](https://docs.python.org/3/library/tkinter.html) +- [Python异常处理指南](https://docs.python.org/3/tutorial/errors.html) +- [GUI设计最佳实践](https://learn.microsoft.com/zh-cn/windows/win32/uxguide/guidelines) + +## 许可证 +本项目基于 [MIT License](LICENSE) 开源 diff --git a/calculator.py b/calculator.py new file mode 100644 index 0000000..4d31952 --- /dev/null +++ b/calculator.py @@ -0,0 +1,288 @@ +""" +简易计算器GUI程序 +[版本信息] +版本号 | 日期 | 作者 | 修改摘要 +----------|------------|------------|------------------ +v1.0.0 | 2025-04-19 | 鲁豫 | 初始版本,实现基本加减法功能 + +[作者信息] +开发团队:Python学习 +程序员:鲁豫<2085520973@qq.com> +代码审核:Xuekun Lu +GitHub仓库:https://github.com/learn-python/calculator-gui +版权声明:本代码遵循MIT开源协议 + +[功能说明] +实现带图形界面的四则运算计算器,支持以下特性: +1. 基础运算:加减乘除 +2. 连续运算功能 +3. 错误处理机制 +4. 历史记录显示(开发中) + +代码结构说明: + 1.全局状态:使用三个变量跟踪计算器状态 + 2.样式配置:集中管理颜色、字体等视觉元素 + 3.事件处理:5个函数处理不同按钮的点击逻辑 + 4.界面构建:分模块创建各个界面组件 + 5.布局管理:使用grid布局实现计算器按键矩阵 + +学习重点: + 1.StringVar的使用:实现显示内容的动态更新 + 2.lambda表达式:用于传递参数的按钮命令绑定 + 3.全局状态管理:如何处理连续运算的中间状态 + 4.异常处理:保证程序在错误输入时不会崩溃 + 5.布局技巧:grid布局的跨列(rowspan)和跨行(columnspan)使用 + +测试建议: + 1.测试乘法:5 × 3 = 15 + 2.测试除法:9 ÷ 3 = 3 + 3.测试除零:5 ÷ 0 → 显示"除零错误" + 4.连续运算测试:2 + 3 = 5 → × 4 = 20 +""" + +# 导入Tkinter图形界面库(Python标准库) +import tkinter as tk + +# ==================== 全局状态变量 ==================== +# 这些变量用于跟踪计算器的当前状态 +current_input = "" # 存储用户当前输入的数字(字符串形式,如"12.34") +first_operand = None # 存储第一个运算数字(当用户点击运算符时保存) +operator = None # 存储当前选择的运算符(+/-等) + +# ==================== 界面样式常量配置 ==================== +# 使用字典统一管理样式,方便后期修改和维护 +COLORS = { + 'operator': '#b1b2b2', # 运算符按钮背景色(浅灰色) + 'number': '#eacda1', # 数字按钮背景色(米黄色) + 'equal': '#eacda1' # 等号按钮背景色(与数字键相同) +} + +FONT = { + 'main': ('宋体', 20), # 显示屏字体(字体,字号) + 'button': ('宋体', 16) # 按钮字体 +} + +LAYOUT = { + 'pad': 4, # 按钮之间的水平间距(单位:像素) + 'margin': 2 # 按钮之间的垂直间距 +} + + +# ==================== 事件处理函数 ==================== +# 以下函数用于响应按钮点击事件 + +def on_number_click(char): + """处理数字和小数点按钮点击事件 + 参数:char - 按钮对应的字符(0-9 或 .) + """ + global current_input # 声明使用全局变量 + + # 处理小数点输入限制 + if char == '.' and '.' in current_input: + return # 如果已包含小数点,不做任何操作 + + current_input += char # 将新字符追加到当前输入 + result_var.set(current_input) # 更新显示屏内容 + + +def on_operator_click(op): + """处理运算符按钮点击事件(+/-/*//) + 参数:op - 运算符符号(字符串) + """ + global first_operand, operator, current_input + + if current_input: # 确保有输入值时才执行 + first_operand = float(current_input) # 将当前输入转为浮点数存储 + operator = op # 记录运算符 + current_input = "" # 清空当前输入,准备接收第二个数字 + result_var.set(op) # 在显示屏显示当前运算符 + + +def calculate(): + """执行计算并显示结果""" + global current_input, first_operand, operator + + # 有效性检查:确保三个要素都存在 + if first_operand is None or operator is None or not current_input: + return + + try: + second_operand = float(current_input) # 将当前输入转为浮点数 + + # 根据运算符执行计算(新增乘除运算) + if operator == '+': + result = first_operand + second_operand + elif operator == '-': + result = first_operand - second_operand + elif operator == '*': + result = first_operand * second_operand + elif operator == '/': + if second_operand == 0: + raise ZeroDivisionError + result = first_operand / second_operand + else: + return # 无效运算符 + + # 格式化显示结果:整数去零,小数保留10位 + if result.is_integer(): + current_input = str(int(result)) # 5.0 → 5 + else: + # 去除末尾多余的零和小数点 + formatted = "{:.10f}".format(result).rstrip('0').rstrip('.') + current_input = formatted if formatted else "0" + + result_var.set(current_input) # 更新显示 + + # 保留计算结果作为下一次运算的第一个操作数 + first_operand = float(current_input) + operator = None # 重置运算符需手动选择 + + except ZeroDivisionError: + result_var.set("除零错误") + # reset_calculator() + except Exception as e: + result_var.set("错误"+e.__cause__) # 显示错误提示 + # reset_calculator() # 重置计算器 + + +def reset_calculator(): + """重置所有状态到初始值""" + global current_input, first_operand, operator + current_input = "" + first_operand = None + operator = None + result_var.set('0') # 显示屏归零 + + +def backspace(): + """删除最后一个输入的字符""" + global current_input + if current_input: + current_input = current_input[:-1] # 移除最后一个字符 + result_var.set(current_input if current_input else '0') # 显示剩余内容或0 + + +# ==================== 主窗口初始化 ==================== +# 创建主窗口并设置基本属性 +root = tk.Tk() # 创建Tkinter主窗口对象 +root.title('简易计算器') # 窗口标题 +root.geometry('295x280+100+100') # 窗口尺寸(宽x高)和初始位置(x+y) +root.attributes("-alpha", 0.9) # 设置窗口透明度(0.0完全透明-1.0不透明) + +# ==================== 结果显示区域 ==================== +# 使用StringVar实现动态更新显示内容 +result_var = tk.StringVar(value='0') # 创建绑定到显示区域的变量 + +# 创建显示标签(显示屏) +display = tk.Label( + root, + textvariable=result_var, # 绑定动态变量 + font=FONT['main'], # 使用主字体 + height=2, # 高度(行数) + width=20, # 宽度(字符数) + justify=tk.LEFT, # 文本左对齐 + anchor=tk.SE # 文本定位到右下角(东南角) +) +# 使用grid布局管理器定位,跨4列显示 +display.grid(row=1, column=1, columnspan=4) + +# ==================== 操作符按钮行 ==================== +# 定义第一行操作符按钮的配置(清除、退格、除、乘) +operator_buttons = [ + # 格式:(显示文本, 行号, 列号, 点击命令) + ('c', 2, 1, reset_calculator), # 清除按钮 + ('←', 2, 2, backspace), # 退格按钮 + ('/', 2, 3, lambda: on_operator_click('/')), # 除法按钮 + ('×', 2, 4, lambda: on_operator_click('*')) # 乘法按钮 +] + +# 批量创建操作符按钮 +for text, row, col, command in operator_buttons: + tk.Button( + root, + text=text, + width=5, # 按钮宽度(字符数) + font=FONT['button'], # 使用按钮字体 + relief=tk.FLAT, # 扁平化按钮样式 + bg=COLORS['operator'], # 设置背景色 + command=command # 绑定点击事件处理函数 + ).grid( + row=row, + column=col, + padx=LAYOUT['pad'], + pady=LAYOUT['margin'] + ) + +# ==================== 数字按钮布局 ==================== +# 定义数字按钮的布局配置(使用嵌套元组) +button_configs = [ + # 每行按钮配置(格式:(按钮1配置, 按钮2配置, ...)) + # 第三行:7、8、9、减号 + (('7', 3, 1), ('8', 3, 2), ('9', 3, 3), ('-', 3, 4, lambda: on_operator_click('-'))), + # 第四行:4、5、6、加号 + (('4', 4, 1), ('5', 4, 2), ('6', 4, 3), ('+', 4, 4, lambda: on_operator_click('+'))), + # 第五行:1、2、3 + (('1', 5, 1), ('2', 5, 2), ('3', 5, 3)), + # 第六行:0、小数点 + (('0', 6, 1), ('.', 6, 3)) +] + +# 遍历配置创建数字按钮 +for row_group in button_configs: + for config in row_group: + # 解析按钮配置 + parts = list(config) + text, row, col = parts[0:3] # 前三个元素固定 + + # 确定点击命令:数字/小数点调用on_number_click,运算符调用on_operator_click + command = parts[3] if len(parts) > 3 else (lambda t=text: on_number_click(t)) + + # 创建按钮对象 + btn = tk.Button( + root, + text=text, + width=5 if text != '0' else 12, # 0按钮特殊宽度 + font=FONT['button'], + relief=tk.FLAT, + bg=COLORS['number'] if text in '1234567890.' else COLORS['operator'], + command=command # 绑定点击事件 + ) + + # 布局按钮(0按钮需要跨列) + if text == '0': + btn.grid( + row=row, + column=col, + columnspan=2, # 跨2列 + padx=LAYOUT['pad'], + pady=LAYOUT['margin'] + ) + else: + btn.grid( + row=row, + column=col, + padx=LAYOUT['pad'], + pady=LAYOUT['margin'] + ) + +# ==================== 等号按钮 ==================== +# 创建特殊尺寸的等号按钮 +tk.Button( + root, + text='=', + width=5, # 宽度(字符数) + height=3, # 高度(行数) + font=FONT['button'], + relief=tk.FLAT, + bg=COLORS['equal'], # 使用等号专用颜色 + command=calculate # 绑定计算函数 +).grid( + row=5, + column=4, + rowspan=2, # 跨5-6两行 + padx=LAYOUT['pad'], + pady=LAYOUT['margin'] +) + +# ==================== 启动程序 ==================== +root.mainloop() # 进入Tkinter事件循环,等待用户操作 diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..53ba130 Binary files /dev/null and b/screenshot.png differ