小白第一次发博客,可能有很多问题,望指正!

讲的不是很详细,提供思路。

目录:

  1. 成果展示
  2. 代码说明
  3. 其他补充和参考资料

——————分割线————————

1.成果展示

基本效果图:

         

如图,要实现的基本功能有:带括号的四则运算、历史记录

扩展功能:按键可随窗口大小变化而变化

2.代码说明

(1)控制输入

def get_input(entry, argu):"""控制输入"""# 从entry窗口展示中获取输入的内容input_data = entry.get()# 设置一些基础列表,方便控制listchar = ["+", "*", "/", ")"]listcharbase = ["+", "-", "*", "/"]listnums = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]listcharall = ["+", "-", "*", "/", "(", ")"]listpoint = [".", ""]listcheck = listnums + listcharall + listpoint# 控制输入字符(只对按钮有用,这里没太大意义) 和 输入长度控制if not listcheck.__contains__(argu) or len(input_data) > 20:return# 第一个输入不能为运算符(负号和左括号除外)elif not input_data and listchar.__contains__(argu):return# 前一个输入为加号:elif input_data[-1:] == '+' and listchar.__contains__(argu):return# 前一个输入为负号:elif input_data[-1:] == '-' and (listchar.__contains__(argu) or (input_data[-2:-1] == '-' and argu == '-')):return# 前一个输入为乘号:elif input_data[-1:] == '*' and listchar.__contains__(argu):return# 前一个输入为除号:elif input_data[-1:] == '/' and listchar.__contains__(argu):return# 前一个输入为左括号:elif input_data[-1:] == '(' and listchar.__contains__(argu):return# 前一个输入为右括号:elif input_data[-1:] == ')' and not (listcharbase.__contains__(argu) or argu == ')'):return# 当前输入为右括号(右括号数量必须小于等于左括号的数量):elif argu == ')' and input_data.count('(') <= input_data.count(')'):return# 前一个输入为数字:elif listnums.__contains__(input_data[-1:]) and argu == "(":return# 当前输入为小数点(一个数只能有一个小数点):elif argu == '.':i = 1while i <= len(input_data):if listcharall.__contains__(input_data[-i]):breakelif listpoint.__contains__(input_data[-i]):returnelse:i = i+1entry.insert("end", argu)# 输入合法将字符插入到entry窗口结尾else:entry.insert("end", argu)

我使用Entry组件来检测输入的算式,这里的entry在后面第4部分的代码创建。

基本的四则运算比较简单,但是还要考虑正负号、括号、小数点。这里简单说明一下括号和小数点的一些重要处理思路。

I.括号

括号里比较特殊的是“)”,因为“(”一般情况下可以随便加,但是“)”能否加上去则会被“(”的数量限制。

  • 我的处理思路是当输入“)”时,查看已输入的算式里“(”的数量是否大于“)”的数量,若是则可以输入,若不是则不可输入。

II.小数点

小数点的要求是一个数最多可以有一个小数点。

我的处理思路是当输入“.”时,从后往前遍历已输入的算式:

  • 若遍历到数字,则继续遍历;
  • 若遍历到运算符(包括括号),则说明没有多余的小数点,输入的小数点可以加入;
  • 若在遍历到运算符(包括括号)前遍历到一个小数点,说明这个小数点是多余的,不可加入。

(2)基本功能

def backspace(entry):"""撤销前一个输入"""input_len = len(entry.get())# 删除entry窗口中最后的字符entry.delete(input_len - 1)def clear(entry):"""清空窗口"""entry.delete(0, "end")def clean_history():"""清空历史记录"""for x in history_label_obj_list:history_label_obj_list.remove(x)# 插入新的历史记录(先允许写入,写入后再关闭)t.configure(state='normal')t.delete('1.0', 'end')t.configure(state='disable')def calc(entry):"""计算结果"""# 获取entry的输入内容input_data = entry.get()# 如果输入为空,不计算if not input_data:returnclear(entry)# 异常捕获,在进行数据运算时如果出现异常进行相应处理# noinspection PyBroadExceptiontry:# eval() 函数用来执行一个字符串表达式,并返回表达式的值;并将执行结果转换为字符串output_data = str(eval(input_data))except Exception:# 将提示信息输出到窗口entry.insert("end", "Calculation error")else:# 添加历史记录,并限制不允许手动修改历史记录(即历史记录只可读)equation = str(input_data)t.configure(state='normal')t.insert(INSERT, equation + "\n  =" + output_data + "\n\n")t.configure(state='disable')t.pack()history_label_obj_list.append(equation + "\n  =" + output_data + "\n\n")# 将计算结果显示在窗口中if len(output_data) > 20:entry.insert("end", "Value overflow")else:entry.insert("end", output_data)
  • 主要讲一下计算部分。我使用的是python自带的算法 “eval() 函数”,算是偷懒了。但由于我前面设计了控制输入的代码,这里又用了“try....except...”来处理错误(比如除数为0的情况,控制输入部分不方便实现),所以计算是没有什么问题的。历史记录列表在第4部分的代码创建。其他部分我在代码中有备注说明。

如果要自己写计算的算法,也可以用导入“re模块”,用正则表达式来查找和分解输入的算式,然后设置运算符优先级,将算式转换成后缀表达式来处理。感兴趣的可以去看看这篇文章:

项目3:python实现一个简易计算器_小豪的博客-CSDN博客_python计算器

我由于是新手,时间和精力有限,大部分时间都去设计控制输入部分和学习并设计GUI部分了,这里就没有落实到位,只是大体了解了一下,抱歉。

(3)按钮创建模板

def place_button(text, func, func_params, bg='grey', **place_params):"""使用偏函数partial,定义了一个模板,简化按钮创建"""my_button = partial(Button, frame_left, bg='grey', padx=26, pady=12, activebackground='darkorange')button = my_button(text=text, bg=bg, font=(1, 26), command=lambda: func(*func_params))button.grid(**place_params)

使用python中的偏函数partial,来快捷创建大量按钮。

lamda表达式是用来创建匿名函数。匿名函数和普通函数相比除了没有名字外没有什么太大的区别。使用lamda表达式也是为了方便,不用手动创建一堆功能相似的函数。

(4)创建基本框架

# 程序入口
if __name__ == '__main__':# 创建窗口root = Tk()root.geometry('670x560+300+100')root.title("计算器")# 计算器左边按钮部分frame_left = Frame(root)frame_left.pack(fill="y", side="left")# 计算器右边历史记录部分frame_right = Frame(root, width=5).pack(fill="y", side="right")Label(frame_right, text="Records\nList", font=("1", 20, "bold")).pack(side="top")# 设置按钮随窗口大小变化而变化# root.grid_columnconfigure(0, weight=0)frame_left.grid_columnconfigure(0, weight=1)frame_left.grid_columnconfigure(1, weight=1)frame_left.grid_columnconfigure(2, weight=1)frame_left.grid_columnconfigure(3, weight=1)frame_left.grid_rowconfigure(0, weight=1)frame_left.grid_rowconfigure(1, weight=1)frame_left.grid_rowconfigure(2, weight=1)frame_left.grid_rowconfigure(3, weight=1)frame_left.grid_rowconfigure(4, weight=1)frame_left.grid_rowconfigure(5, weight=1)frame_left.grid_rowconfigure(6, weight=1)# 创建entry记录输入的算式entry = Entry(frame_left, justify="right", font=(1, 34))entry.grid(row=0, column=0, columnspan=4, padx=10, pady=10, ipadx=10, ipady=25, sticky=NSEW)# 创建历史记录列表history_label_obj_list = []result = entry.get()# 给历史记录增加滚动条rollbar = Scrollbar(frame_right)rollbar.pack(fill='y', side="right")t = Text(frame_right, background="seashell", padx=26, pady=6, font=(2, 15), yscrollcommand=rollbar.set)rollbar.config(command=t.yview)

创建了基本窗口,并用Frame将其分成左边的按钮和输入区,和右边的历史记录区。将左边按钮的每行每列分别和窗口大小变化绑定。再创建entry记录算式、历史记录列表、历史记录滚动条。

这里没有什么太多好说的,都是用tkinter设计GUI的一些基础内容,需要自己去学习如何使用。我一开始也是一点都不会,然后到处找资料学习做出来的,花了很多时间和精力。

(5)创建按钮

# 创建基础运算按钮
btnText = (("7", "8", "9"),("4", "5", "6"),("1", "2", "3"))
for rindex, r in enumerate(btnText):for cindex, c in enumerate(r):# Button(root, text=c, width=2, padx=10, pady=3, command=lambda: get_input(entry, c)) \#     .grid(row=rindex + 1, column=cindex, ipadx=2, pady=2, sticky=NS)place_button(c, get_input, (entry, c), row=rindex + 2, column=cindex, ipadx=2, padx=2, pady=2, sticky=NSEW)place_button('0', get_input, (entry, '0'), row=5, column=0, columnspan=2, ipadx=2, padx=2, pady=2, sticky=NSEW)
place_button('.', get_input, (entry, '.'), bg='orange', row=5, column=2, ipadx=7, padx=2, pady=2, sticky=NSEW)
place_button('(', get_input, (entry, '('), bg='orange', row=6, column=0, ipadx=2, padx=2, pady=2, sticky=NSEW)
place_button(')', get_input, (entry, ')'), bg='orange', row=6, column=1, ipadx=2, padx=2, pady=2, sticky=NSEW)# 运算输入类按钮(只是背景色不同)
# 字符大小('+','-'宽度不一样,使用ipadx进行修正)
place_button('+', get_input, (entry, '+'), bg='orange', row=2, column=3, ipadx=3, pady=2, sticky=NSEW)
place_button('-', get_input, (entry, '-'), bg='orange', row=3, column=3, ipadx=2, pady=2, sticky=NSEW)
place_button('*', get_input, (entry, '*'), bg='orange', row=4, column=3, ipadx=2, pady=2, sticky=NSEW)
place_button('/', get_input, (entry, '/'), bg='orange', row=5, column=3, ipadx=2, pady=2, sticky=NSEW)# 功能输入类按钮(背景色、触发功能不同)
place_button('C', clear, (entry,), bg='DarkTurquoise', row=1, column=0, ipadx=2,padx=2, pady=2, sticky=NSEW)
place_button('', backspace, (entry,), bg='DarkTurquoise', row=1, column=2, columnspan=2,             ipadx=2,padx=2, pady=2, sticky=NSEW, image=photoimage)
place_button('=', calc, (entry,), bg='YellowGreen', row=6, column=2, ipadx=2, padx=2, pady=2,columnspan=2, sticky=NSEW)
place_button('RC', clean_history, (), bg='DarkTurquoise', row=1, column=1, ipadx=2,padx=2, pady=2, sticky=NSEW)# GUI循环
root.mainloop()

这里就是创建按钮的部分,大多也是些GUI基础,也同样花费了我好多时间精力,但总算做出来了。值得一提的是,我在创建9个数字按钮时,用了“enumerate”函数来遍历数字元组,比较方便。其他按钮都有些不同,只能手动创建。

另外,由于撤回键和等于键都比较大,一般的文本显得太小了,我就找了相应的图片,或者自己画了一下,再p了一下,把图片放上去了。这里放一下图(我放的是已经扣好的,背景是透明的png图片,不知道会不会被平台压缩成别的图)。

           

3.其他补充和引用资料

(1)打包程序

所有代码写完后,我浏览博客发现可以用Pyinstaller打包程序,于是打包了一下。在终端输入的代码如下:

Pyinstaller -F -w -i icon.ico caculater.py

-F参数代表制作独立的可执行程序,-w是指程序启动的时候不会打开命令窗口,-i参数用来添加图标 。后面分别是图标名称和py文件名称。具体操作可以看这里:别再问我Python打包成exe了!(终极版)_涛哥聊Python-CSDN博客

我之后又上网找了张计算器的图片,扣了一下,又找到网站将图片转为ico类型,才成功打包出来,再把快捷方式发到桌面上就可以使用了。这里分别放一下我扣好的png图片和桌面效果。ico图片放不上来,可以去这个网址转换一下:在线将PNG 转换成 ICO 。 免费将.PNG 转换成.ICO 。

               

(2)缺陷和不足

I.输入输出显示问题:

我使用的是Entry检测算式输入,只能输入和显示一行,看单个算式和结果不太方便。可以用两个Label来分别显示算式和结果,参照这篇博客:项目4:用python自带模块tkinter实现简单计算器(可用于表白)_小豪的博客-CSDN博客_python的tkinter编写计算器

II.窗口变化问题:

我把基础按钮和窗口变化绑定了,即窗口大小变化则按钮大小也会变化,但历史记录窗口不好绑定,导致交互体验并不是很好。还有,按钮上的文本并没有随窗口变化而变化,所以当窗口过小时,按钮上的文本会显得比较大。

III.历史记录窗口不足:

本来想再添加一个键来控制历史记录窗口的显现和隐藏,而不是让其一直挂在旁边,但实在是能力和精力有限,没能做出来。

IV.输入问题:

记录算式用的是Entry,所以也可以用键盘输入,当然输入正常的算式可以正常输出,但也可以输入别的乱七八糟的东西,会计算错误就是了。

历史记录我借鉴时看到别人用的是Lable,但我觉得不好看,就改用Text来作为历史记录。问题就是Text也是允许用户随便输入的,我后期加了限制才解决这个问题,而entry不好加限制所以没有解决。

V.滑动条可能多余

由于我用Text作为历史记录,而Text本身是允许上下滚动查看文本的,所以历史记录不多时,滑动条比较多余。但我考虑到可能历史记录会多到不方便这样查找,于是还是学习后加上了滑动条。

事实上,正是因为我加了滑动条,所以不方便再把历史记录区域和窗口大小变化绑定起来,一定程度上导致了第2个问题,属于功能冲突了。但也意外发现可以通过缩小窗口来“隐藏”历史记录,也是意料之外的效果了。

(3)参考资料:

[1]基础知识:(只是tkinter模块的知识)

北大的教授利用72个小时把Python讲完了!完整版的600集,拿走不谢,学不会我将退出编程界_哔哩哔哩_bilibili

tkinter模块常用参数(python3) - 覆手为云p - 博客园

[2]框架借鉴:

python实现简单计算器_风林-CSDN博客_python计算器
[3] 历史记录借鉴:

手把手教你用python制作简易计算器,能够记录你使用的情况 - 灰信网(软件开发博客聚合)

[4]打包方法借鉴:

别再问我Python打包成exe了!(终极版)_涛哥聊Python-CSDN博客

项目一:使用python tkinter模块做简单的计算器相关推荐

  1. Python Tkinter 模块简要介绍

    [本文章已于 2022/9/26 重制]         这篇博客将简要地介绍 Python 的 Tkinter 模块,主要供没了解过 Tkinter 模块的人进行了解,让初学者也能进行桌面应用开发! ...

  2. python安装tkinter模块_详解python tkinter模块安装过程

    引言: 在Python3下运行Matplotlib之时,碰到了"No module named _tkinter"的问题,花费数小时进行研究解决,这里讲整个过程记录下来,并尝试分析 ...

  3. python tkinter 安装_详解python tkinter模块安装过程

    引言: 在Python3下运行Matplotlib之时,碰到了"No module named _tkinter"的问题,花费数小时进行研究解决,这里讲整个过程记录下来,并尝试分析 ...

  4. IDEA为java项目中用到python的模块设置Python interpreter

    IDEA为java项目中用到python的模块设置Python interpreter Java项目中模块用到了python,没有为模块设置Python解释器,因此点击右上角Configure Pyt ...

  5. python计算器教程,用Python程序制作一个简单的计算器

    用Python程序制作一个简单的计算器 在此示例中,您将学习创建一个简单的计算器,该计算器可以根据用户的输入进行加,减,乘或除. 要理解此示例,您应该了解以下Python编程主题: 通过函数创建简单计 ...

  6. python+ pyqt5 实现最简单的计算器

    问题描述: 用python + pyqt5 实现最简单的计算器 [ 1: 只能进行 2 个数字的运算:2: 除法的第二位数不能是0 ] 运行效果: 问题解决: 全部代码如下: [ 只需要进行打包即可, ...

  7. Python如何使用Tkinter模块实现简单窗口化

    简介:Python Tkinter是一个非常强大的GUI(图形用户界面)工具包,它使得开发者可以快速地创建GUI应用程序.在本文中,我们将会介绍一些使用Python Tkinter的基本知识和技巧,以 ...

  8. 使用Python中内置tkinter模块做一个简易的计算器

    当前环境: windows + python3.7 + tkinter 模块 一.总体规划 1.需要用到的模块有  tkinter 2.tkinter 模块中需要用到的知识点有: 2.1.创建主窗口 ...

  9. python Tkinter Text的简单用法

    1.设置python Tkinter Text控件文本的方法 text.insert(index,string)  index = x.y的形式,x表示行,y表示列 向第一行插入数据,text.ins ...

最新文章

  1. qt 表格表头嵌入全选全不选_6个word表格基础操作,看了再说会不会
  2. Java 语言中 Enum 类型的使用介绍
  3. 10-27 插入学生总学分表 (10 分)
  4. (转)rtmp协议简单解析以及用其发送h264的flv文件
  5. 滴滴、小米启动造车,特斯拉的护城河还能守多久?
  6. Illustrator 教程,如何在 Illustrator 文档中缩放和平移?
  7. mysql表空间查看及创建
  8. 一文读懂 Spring Bean 的生命周期
  9. vue中使用友盟统计,统计到每个路由
  10. 列举五样计算机常见故障,项目五计算机常见故障和排除.ppt
  11. 【华为云技术分享】让电变“机灵”,华为云与开发者共同打造智慧用电
  12. 证明三角形中cosA+cosB+cosC=1+4sin(A/2)sin(B/2)sin(C/2)
  13. 苏州计算机岗前培训,我院召开2018年新职工岗前培训动员大会
  14. js 对日期的计算,加减天数
  15. 知识图谱预训练数据转化(附源码)
  16. 华晨宏盛分享2021年个人所得税起征点是个人所得税税率一览表
  17. 2021GDOI游记
  18. 几个国外域名解析网站
  19. Html页脚声明,HTML创建与页脚和页眉和内容
  20. android日历界面的实现,自定义view之实现日历界面(二)

热门文章

  1. mysql分组语句的子句_MySQL 中的排序与分组 语句
  2. Win7安全模式启动卡在Classpnp.sys
  3. [*CTF2022]web题目复现及wp
  4. 半导体测试概论笔记——第二章半导体测试基本概念
  5. 部门 2016 总结
  6. java qt gui_工控编程,Qt 学习之路
  7. android app自动卸载,无需ROOT 只需2招解决Android手机无法卸载的流氓APP
  8. 手机耳机插入电脑没有反应
  9. LANDESK8.8版本操作说明书之服务器安装
  10. to_char在oracle 中函数使用方法