Python制作简易计算器(GUI)—Tkinter

  • Tkinter简介
  • Tkinter 与 PyQt5 的比较
    • Tkinter
    • PyQt5
  • 项目展示
  • 导入模块
  • 函数封装
    • 1. 运算公式的拼接与展示
    • 2. 将显示框的内容删除
    • 3. 使用eval()函数对表达式求值
  • 主逻辑
    • 1. 布局窗口
    • 2. 布局表达式展示区域
    • 3. 布局按钮
  • 代码汇总
  • 实现桌面点击直接启动计算器(无须人为开启Python)

Tkinter简介

tkinter模块是Python标准的TK GUI工具包接口,可以实现一些较为简单GUI的创建。

Tkinter 与 PyQt5 的比较

Tkinter

  1. 麻雀虽小,五脏俱全
  2. TK工具集使用TCL(Tool Command Language)语言开发
  3. 运行相同的程序时,运行速度快于pyqt5
  4. 组件能满足基本使用
  5. 程序界面较简陋
  6. 开发时,使用的语句更短更易懂
  7. Python内置的标准库

PyQt5

  1. Qt框架使用C++开发
  2. 组件丰富
  3. 运行相同的程序时,运行速度慢与tkinter
  4. 使用Qt designer可以在不使用代码的情况下创建一个程序
  5. 界面的观赏性更强
  6. 调试难度更大

项目展示


可以通过点击桌面快捷方式直接运行,无须人为打开Python。
修改代码保存后,程序也会发生相应的改变。

导入模块

import tkinter as tkimport tkinter.messagebox as mes

注:
要想使用tkinter的弹窗组件,必须导入tkinter.messagebox

函数封装

1. 运算公式的拼接与展示

def operation(num):content = buf_bottom.get()# 使用变量对象buf_bottom的get方法,获取标签内的文字信息buf_bottom.set(content+num)

2. 将显示框的内容删除

def clear():buf_top.set('') # 使用变量对象的set方法将标签内容设置为空字符串buf_bottom.set('')

显示框分上下两部分,之所以这样分,是希望实现这样的一个效果。

上面的标签对象用于展示计算过程中使用的表达式,下面的标签则是展示未完成的表达式以及计算结果的展示。

3. 使用eval()函数对表达式求值

这里我们使用eval实现计算结果的获取,eval()是Python的内建函数,它接受一个字符串,将该字符串作为Python代码运行后,返回结果。

def calculate():content = buf_bottom.get()buf_top.set(content + ' = ')try:buf_bottom.set(eval(content))# 将except ZeroDivisionError放在except的前面可以保证except ZeroDivisionError的正常执行# except 包含 except ZeroDivisionErrorexcept ZeroDivisionError:# 处理除数为零的异常情况buf_bottom.set('Infiniti')# 弹出警示窗口mes.showwarning('注意', '请不要使用零作为除数')except:# 处理所有的异常情况# 弹出错误提示窗口mes.showerror('错误', '输入的表达式有误,请重新输入')

在异常处理时要注意顺序,否则可能有些操作永远也执行不了。

主逻辑

1. 布局窗口

if __name__ == '__main__':# 保证下方代码在本文件作为模块导入其他文件时不会执行# 创建主窗口calc = tk.Tk()# 指定程序的宽高WIDTH_R = 283HEIGHT_R = 335# 获取计算机屏幕的宽高WIDTH_W = calc.winfo_screenwidth()HEIGHT_W = calc.winfo_screenheight()# 设置应用名称calc.title('计算器')# 设置应用图标(仅指定ico文件才有效,png等图片无法正常显示)calc.iconbitmap('./calculation.ico')# 设置主窗口背景颜色calc['background'] = '#d9d6c3'# 将程序移动到屏幕中央calc.geometry(f'{WIDTH_R}x{HEIGHT_R}+{(WIDTH_W - WIDTH_R) // 2}+{(HEIGHT_W - HEIGHT_R) // 2}')

注:

calc.geometry(f’{WIDTH_R}x{HEIGHT_R}+{(WIDTH_W – WIDTH_R) // 2}+{(HEIGHT_W – HEIGHT_R) // 2}’)

  1. geometry方法接受一个字符串参数,依据它设置程序的宽高以及左上角的xy坐标。
    宽高之间必须 小写的字母x连接 ,其他数据之间则用 + 号连接。
  2. 至于将程序移动到屏幕中间的代码为什么会这样写,改变一下表达式或许你就能够理解

(WIDTH_W – WIDTH_R) // 2 –> WIDTH_W // 2 – WIDTH_R // 2
(HEIGHT_W – HEIGHT_R) // 2 –> HEIGHT_W // 2 – HEIGHT_R // 2

程序中使用的图标在这个链接里:
https://s1.chu0.com/src/img/png/bf/bfc39031c057479aad0e94706d4d26bb.png?imageMogr2/auto-orient/thumbnail/!132x132r/gravity/Center/crop/132×132/quality/85/&e=1735488000&token=1srnZGLKZ0Aqlz6dk7yF4SkiYf4eP-YrEOdM1sob:q3YCeYP0-2kOdXQG7LDXmls337w=

2. 布局表达式展示区域

# 布局表达式展示区域# 创建变量对象,便于后续获取或更改变量的值buf_top = tk.StringVar()# 如果需要让标签对象的显示内容由一个字符串变量来实现,Label的参数就不能用text,需要使用textvariablebuffer = tk.Label(calc, textvariable=buf_top, bg='#6f6d85', fg='#d3d7d4', height=1, width=10,font=('arial', 14, 'normal'), anchor='se', pady=3, padx=2)# sticky参数可以实现类似pack方法的fill参数, 使标签横向铺满buffer.grid(sticky=tk.W + tk.E, row=0, column=0, columnspan=4)buf_bottom = tk.StringVar()buffer = tk.Label(calc, textvariable=buf_bottom, bg='#6f6d85', fg='#fff', height=1, width=10,font=('arial', 18, 'normal'), anchor='se', pady=3, padx=2)buffer.grid(sticky=tk.W + tk.E, row=1, column=0, columnspan=4)

标签的位置,由参数anchor的属性值决定


上述图片引用自C语言中文网

标签的位置默认是居中的。
参数值及其代表的方位可能不太好记忆,但只要记住了东西南北四个方位,记忆起来就方便多了。

方位英文表示
East
西West
South
North

3. 布局按钮

 # Ctk.Button(calc, text='C', cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: clear()).grid(row=2, column=0, padx=5, pady=5)# 0tk.Button(calc, text=0, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('0')).grid(row=5, column=1, padx=5, pady=5)# 1tk.Button(calc, text=1, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('1')).grid(row=2, column=1, padx=5, pady=5)# 2tk.Button(calc, text=2, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('2')).grid(row=2, column=2, padx=5, pady=5)# 3tk.Button(calc, text=3, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('3')).grid(row=3, column=0, padx=5, pady=5)# 4tk.Button(calc, text=4, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('4')).grid(row=3, column=1, padx=5, pady=5)# 5tk.Button(calc, text=5, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('5')).grid(row=3, column=2, padx=5, pady=5)# 6tk.Button(calc, text=6, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('6')).grid(row=4, column=0, padx=5, pady=5)# 7tk.Button(calc, text=7, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('7')).grid(row=4, column=1, padx=5, pady=5)# 8tk.Button(calc, text=8, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('8')).grid(row=4, column=2, padx=5, pady=5)# 9tk.Button(calc, text=9, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('9')).grid(row=5, column=0, padx=5, pady=5)# 加号tk.Button(calc, text='+', cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('+')).grid(row=2, column=3, padx=5, pady=5)# 乘号tk.Button(calc, text='*', cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('*')).grid(row=4, column=3, padx=5, pady=5)# 除号tk.Button(calc, text='/', cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('/')).grid(row=3, column=3, padx=5, pady=5)# 等号tk.Button(calc, text='=', cursor='hand2', width=7, height=1, bg='#f2eada', font='arial 22 normal',command= calculate).grid(row=5, column=2, columnspan=2, padx=5, pady=5)# 开启主循环calc.mainloop()

本想先用循环创建一些按钮,但由于点击按钮后,无法判断点击了哪个按钮。
于是:

# 使用for循环创建0-8的按钮,其余按钮为了布局,得一个一个布置。face = 0for row in range(1, 4):for column in range(3):tk.Button(calc, text=face, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal', command=lambda: number(face)).grid \(row=row, column=column, padx=5, pady=5)face += 1

由于number是一个回调函数,当你点击后才会执行,而此时for循环已经结束(排除你手速足够快的情况),face=9。
因此无论你点哪一个数字按钮,显示的都会是9。
暂时并没有更好的解决方案,如果你知道怎么做,还请不吝赐教。

代码汇总

# 导入模块import tkinter as tkimport tkinter.messagebox as mes# 运算公式的拼接与展示def operation(num):content = buf_bottom.get()# 使用变量对象buf_bottom的get方法,获取标签内的文字信息buf_bottom.set(content+num)# 将显示框的内容删除def clear():buf_top.set('')buf_bottom.set('')# 使用eval()函数对表达式求值def calculate():content = buf_bottom.get()buf_top.set(content + ' = ')try:buf_bottom.set(eval(content))# 将except ZeroDivisionError放在except的前面可以保证except ZeroDivisionError的正常执行# except 包含 except ZeroDivisionErrorexcept ZeroDivisionError:# 处理除数为零的异常情况# 弹出警示窗口buf_bottom.set('Infiniti')mes.showwarning('注意', '请不要使用零作为除数')except:# 处理所有的异常情况# 弹出错误提示窗口mes.showerror('错误', '输入的表达式有误,请重新输入')if __name__ == '__main__':# 保证下方代码在本文件作为模块导入其他文件时不会执行# 创建主窗口calc = tk.Tk()# 指定程序的宽高WIDTH_R = 283HEIGHT_R = 335# 获取计算机屏幕的宽高WIDTH_W = calc.winfo_screenwidth()HEIGHT_W = calc.winfo_screenheight()# 设置应用名称calc.title('计算器')# 设置应用图标(仅指定ico文件才有效,png等图片无法正常显示)calc.iconbitmap('./calculation.ico')# 设置主窗口背景颜色calc['background'] = '#d9d6c3'# 将程序移动到屏幕中央calc.geometry(f'{WIDTH_R}x{HEIGHT_R}+{(WIDTH_W - WIDTH_R) // 2}+{(HEIGHT_W - HEIGHT_R) // 2}')# 布局表达式展示区域# 创建变量对象,便于后续获取或更改变量的值buf_top = tk.StringVar()# 如果需要让标签对象的显示内容由一个字符串变量来实现,Label的参数就不能用text,需要使用textvariablebuffer = tk.Label(calc, textvariable=buf_top, bg='#6f6d85', fg='#d3d7d4', height=1, width=10,font=('arial', 14, 'normal'), anchor='se', pady=3, padx=2)# sticky参数可以实现类似pack方法的fill参数, 使标签横向铺满buffer.grid(sticky=tk.W + tk.E, row=0, column=0, columnspan=4)buf_bottom = tk.StringVar()buffer = tk.Label(calc, textvariable=buf_bottom, bg='#6f6d85', fg='#fff', height=1, width=10,font=('arial', 18, 'normal'), anchor='se', pady=3, padx=2)buffer.grid(sticky=tk.W + tk.E, row=1, column=0, columnspan=4)# Ctk.Button(calc, text='C', cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: clear()).grid(row=2, column=0, padx=5, pady=5)# 0tk.Button(calc, text=0, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('0')).grid(row=5, column=1, padx=5, pady=5)# 1tk.Button(calc, text=1, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('1')).grid(row=2, column=1, padx=5, pady=5)# 2tk.Button(calc, text=2, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('2')).grid(row=2, column=2, padx=5, pady=5)# 3tk.Button(calc, text=3, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('3')).grid(row=3, column=0, padx=5, pady=5)# 4tk.Button(calc, text=4, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('4')).grid(row=3, column=1, padx=5, pady=5)# 5tk.Button(calc, text=5, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('5')).grid(row=3, column=2, padx=5, pady=5)# 6tk.Button(calc, text=6, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('6')).grid(row=4, column=0, padx=5, pady=5)# 7tk.Button(calc, text=7, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('7')).grid(row=4, column=1, padx=5, pady=5)# 8tk.Button(calc, text=8, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('8')).grid(row=4, column=2, padx=5, pady=5)# 9tk.Button(calc, text=9, cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('9')).grid(row=5, column=0, padx=5, pady=5)# 加号tk.Button(calc, text='+', cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('+')).grid(row=2, column=3, padx=5, pady=5)# 乘号tk.Button(calc, text='*', cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('*')).grid(row=4, column=3, padx=5, pady=5)# 除号tk.Button(calc, text='/', cursor='hand2', width=3, height=1, bg='#f2eada', font='arial 22 normal',command=lambda: operation('/')).grid(row=3, column=3, padx=5, pady=5)# 等号tk.Button(calc, text='=', cursor='hand2', width=7, height=1, bg='#f2eada', font='arial 22 normal',command= calculate).grid(row=5, column=2, columnspan=2, padx=5, pady=5)# 开启主循环calc.mainloop()

实现桌面点击直接启动计算器(无须人为开启Python)

创建了一个GUI程序,如果还每次都要在命令行这样打开:

python calculation.py

怕是一种罪孽。
如果想让程序也能像其他桌面应用一样使用,可以这样做:

  1. 将文件的后缀名由 .py 改为 .pyw
  2. 在命令行中输入:

pythonw calculation.pyw

即可。
此后,即便你重启计算机,也能点击文件直接打卡程序。
相当于你点击.pyw 文件后,系统隐式执行了:

python calculation.py