Python版本:3.7
By : zeis
最近在学Python,一开始是看Python教程 - 廖雪峰的官方网站,但是看到“面对对象高级编程”那边就没耐心看了,不过网站还是很不错的。后来把《简明Python教程》(原著叫《A Byte of Python》)看完了,这个教程我感觉还是很不错,适合我这样没耐心的人看。
又了解了图形界面后,就想着做个简单的计算器,为此找了好多资料,可能是我太笨了吧,emmmmm…
现在计算器算是完成一部分了,所以我就打算写个笔记记录一下,也是为了让其他跟我一样的人不用再去找好多资料,只看我这个就可以。
tkinter是Python自带的标准GUI(图形用户界面)包,使用它不需要再安装其他的东西,直接使用即可。
from tkinter import *
from tkinter import messagebox
import operator
程序的第一部分当然是import啦,第一行是导入tkinter包的“所有”内容,第二行是导入tkinter 包的messagebox模块,messagebox模块是用来做弹窗的,第三行operator包在本次程序中是用来实现对比两个列表是否相等,其他功能这里不讲。
要注意的是其实第一行这样导入并不会真正导入tkinter 包的所有内容,messagebox就是没有导入的部分之一,所以还需要第二行。
这是我找到的解释:
包导入的过程和模块的基本一致,只是导入包的时候会执行此包目录下的__init__.py,而不是模块里面的语句了。另外,如果只是单纯的导入包,而包的__init__.py中又没有明确的其他初始化操作,那么此包下面的模块是不会自动导入的。
lists = [] #设置一个变量 保存运算数字和符号的列表
class Application(Frame):
def __init__(self,master = None):
Frame.__init__(self, master)
self.master.geometry('320x450')
self.master.resizable(0, 0) # 阻止Python GUI的大小调整
self.createWidgets()
首先这里定义了一个列表lists ,用来保存输入内容。
在GUI中,每个Button、Label、输入框等,都是一个Widget(控件)。在tkinter中用Frame表示窗口,Frame也是一个Widget,Frame可以容纳其它Widget。本程序中从Frame类派生出Application类,来容纳各种Widget。
Application类的构造函数 __init__(self,master=None)
有两个参数,self
代表Application
本身,(在tkinter中,一个Widget可能属于另一个Widget,这时的另一个Widget就是这个控件的master
)而窗口没有master
,所以在这里master=None
。
geometry('320x450')
表示将窗口大小设为“320*450”大小,self.createWidgets()
是创建Widgets的方法,具体在后面。
resizable(0, 0)
表示不允许调节窗口大小,将参数设为1则表示允许调节。
def createWidgets(self):
#增加菜单
self.menuBar = Menu(self.master) #创建菜单的实例
self.master.config(menu = self.menuBar) #将根窗口的顶级菜单设置为menu
#设置菜单选项
#创建一个下拉菜单‘关于’,这个菜单是挂在menubar(顶级菜单)上的
#tearoff的值有0和1,为0时表示子菜单不独立出来。
aboutMenu=Menu(self.menuBar,tearoff=0)
#创建一个下拉菜单‘功能’,这个菜单是挂在menubar(顶级菜单)上的
moreMenu=Menu(self.menuBar,tearoff=0)
#用add_cascade()将菜单添加到顶级菜单中,按添加顺序排列
self.menuBar.add_cascade(label='功能',menu=moreMenu)
self.menuBar.add_cascade(label='帮助',menu=aboutMenu)
#下拉菜单的具体项目,使用add_command()方法
aboutMenu.add_command(label='关于',command=self.About)
moreMenu.add_command(label='其他',command=self.Other)
# 显示面板
self.result = StringVar()
self.result.set(0) # 显示面板显示结果1,用于显示默认数字0
self.result2 = StringVar() # 显示面板显示结果2,用于显示计算过程
self.result2.set('')
# 显示面板设置
self.label = Label(self.master, font=('微软雅黑', 20), bg='#FFFFFF', bd='0', fg='#828282', anchor='se',\
textvariable=self.result2)
self.label.place(width=320, height=130)
self.label2 = Label(self.master, font=('微软雅黑', 25), bg='#FFFFFF', bd='0', fg='black', anchor='se',\
textvariable=self.result)
self.label2.place(y=130, width=320, height=70)
######################数字键###########################
self.Button1 = Button(self.master, text='7', font=('微软雅黑', 20), fg=('#4F4F4F'), bg=('#FFFFF0'), bd=0,\
command=lambda: self.pressNum('7'))
self.Button1.place(x=0, y=250, width=80, height=50)
self.Button2 = Button(self.master, text='8', font=('微软雅黑', 20), fg=('#4F4F4F'), bg=('#FFFFF0'), bd=0, \
command=lambda: self.pressNum('8'))
self.Button2.place(x=80, y=250, width=80, height=50)
self.Button3 = Button(self.master, text='9', font=('微软雅黑', 20), fg=('#4F4F4F'), bg=('#FFFFF0'), bd=0, \
command=lambda: self.pressNum('9'))
self.Button3.place(x=160, y=250, width=80, height=50)
self.Button4 = Button(self.master, text='4', font=('微软雅黑', 20), fg=('#4F4F4F'), bg=('#FFFFF0'), bd=0.1, \
command=lambda: self.pressNum('4'))
self.Button4.place(x=0, y=300, width=80, height=50)
self.Button5 = Button(self.master, text='5', font=('微软雅黑', 20), fg=('#4F4F4F'), bg=('#FFFFF0'), bd=0.1, \
command=lambda: self.pressNum('5'))
self.Button5.place(x=80, y=300, width=80, height=50)
self.Button6 = Button(self.master, text='6', font=('微软雅黑', 20), fg=('#4F4F4F'), bg=('#FFFFF0'), bd=0.1, \
command=lambda: self.pressNum('6'))
self.Button6.place(x=160, y=300, width=80, height=50)
self.Button7 = Button(self.master, text='1', font=('微软雅黑', 20), fg=('#4F4F4F'), bg=('#FFFFF0'), bd=0.1, \
command=lambda: self.pressNum('1'))
self.Button7.place(x=0, y=350, width=80, height=50)
self.Button8 = Button(self.master, text='2', font=('微软雅黑', 20), fg=('#4F4F4F'), bg=('#FFFFF0'), bd=0.1, \
command=lambda: self.pressNum('2'))
self.Button8.place(x=80, y=350, width=80, height=50)
self.Button9 = Button(self.master, text='3', font=('微软雅黑', 20), fg=('#4F4F4F'), bg=('#FFFFF0'), bd=0.1, \
command=lambda: self.pressNum('3'))
self.Button9.place(x=160, y=350, width=80, height=50)
self.Button10 = Button(self.master, text='.', font=('微软雅黑', 20), fg=('#4F4F4F'), bg=('#FFFFF0'), bd=0.1, \
command=lambda: self.pressNum('.'))
self.Button10.place(x=0, y=400, width=80, height=50)
self.Button11 = Button(self.master, text='0', font=('微软雅黑', 20), fg=('#4F4F4F'), bg=('#FFFFF0'), bd=0.1, \
command=lambda: self.pressNum('0'))
self.Button11.place(x=80, y=400, width=80, height=50)
######################运算键###########################
self.Button12 = Button(self.master, text='=', font=('微软雅黑', 20), fg=('#4F4F4F'), bg=('#EED8AE'), bd=0.1, \
command=lambda: self.pressEqual())
self.Button12.place(x=160, y=400, width=80, height=50)
self.Button13 = Button(self.master, text='+', font=('微软雅黑', 20), fg=('#4F4F4F'), bg=('#F5FFFA'), bd=0.1, \
command=lambda: self.pressOperator('+'))
self.Button13.place(x=240, y=250, width=80, height=50)
self.Button14 = Button(self.master, text='-', font=('微软雅黑', 20), fg=('#4F4F4F'), bg=('#F5FFFA'), bd=0.1, \
command=lambda: self.pressOperator('-'))
self.Button14.place(x=240, y=300, width=80, height=50)
self.Button15 = Button(self.master, text='*', font=('微软雅黑', 20), fg=('#4F4F4F'), bg=('#F5FFFA'), bd=0.1, \
command=lambda: self.pressOperator('*'))
self.Button15.place(x=240, y=350, width=80, height=50)
self.Button16 = Button(self.master, text='/', font=('微软雅黑', 20), fg=('#4F4F4F'), bg=('#F5FFFA'), bd=0.1, \
command=lambda: self.pressOperator('/'))
self.Button16.place(x=240, y=400, width=80, height=50)
######################特殊键###########################
self.Button17 = Button(self.master, text='清零', font=('微软雅黑', 15), fg=('#4F4F4F'),bg=('#FFEC8B'), bd=0.1, \
command=lambda: self.Clr())
self.Button17.place(x=0, y=200, width=80, height=50)
self.Button18 = Button(self.master, text='^2', font=('微软雅黑', 20), fg=('#4F4F4F'), bg=('#F5FFFA'), bd=0.1, \
command=lambda: self.Square())
self.Button18.place(x=80, y=200, width=80, height=50)
self.Button19 = Button(self.master, text='sqrt', font=('微软雅黑', 20), fg=('#4F4F4F'), bg=('#F5FFFA'), bd=0.1, \
command=lambda: self.Sqrt())
self.Button19.place(x=160, y=200, width=80, height=50)
self.Button20 = Button(self.master, text='删除', font=('微软雅黑', 15), fg=('#4F4F4F'), bg=('#FFEC8B'), bd=0.1, \
command=lambda: self.BackSp())
self.Button20.place(x=240, y=200, width=80, height=50)
createWidgets(self)
方法可以分为三个部分:
显示面板:
StringVar()
是Tk库内部定义的字符串变量类型,可以用它来显示计算内容,set()
用来改变值,get()
用来获取值。
在 Tkinter 中,Label 控件用以显示文字和图片。本程序用了两个Label分别来显示计算过程和计算结果,本次用到的参数有:
参数 | 解释 |
---|---|
font | 设置字体跟文字大小 |
bg | 设置背景颜色 |
bd | 指定Label的边框宽度 |
fg | 设置Label的文本和位图的颜色 |
anchor | 控制文本(或图像)在Label中显示位置 n,ne,e,se,s,sw,w,nw,或center来定位(ewsn代表东西南北,上北下南左西右东) |
textvariable | Label显示Tkinter变量(通常是一个StringVar变量)的内容 |
最后用place()
来设置位置和大小。
参数 | 解释 |
---|---|
text | 指定按钮上显示的文本 |
font | 按钮上文本的字体和大小 |
fg | 按钮的前景色,即文本颜色 |
bg | 按钮的背景色 |
bd | 按钮的边框宽度 |
command | 按钮消息的回调函数 |
由于tkinter要求由按钮(或者其它的插件)触发的控制器函数不能含有参数,目的就是为了以统一的方式去调用他们。我们可以使用lambda来给函数传递参数。
lambda可以使在任何地方创建一个没有名字的单行函数,即匿名函数。我的理解是在
Button(self.master, text='7', font=('微软雅黑', 20), fg=('#4F4F4F'), bg=('#FFFFF0'), bd=0,command=lambda: self.pressNum('7'))
中lambda: self.pressNum('7')
是一个匿名函数,而不是单纯调用self.pressNum()
,但最后得到的效果就是调用self.pressNum()
的效果是一样的。只不过如果command=self.pressNum('7')
这样的形式的话,那么在创建按键时就会直接执行self.pressNum('7')
,程序就会出问题。
#数字键处理函数,属于Application()类
def pressNum(self,val):
global lists
lists.append(val)
#使用join()将列表转换成字符串,再显示到Label中
app.result_set(''.join(lists))
当按下数字键时调用的函数,每按下一次则会向全局列表lists
添加一个元素,然后显示出来。
#运算符号键处理函数,属于Application()类
def pressOperator(self,val):
global lists
if len(lists) > 0:
if lists[-1] in ['+','-','*','/']:
lists[-1] = val
else:
lists.append(val)
app.result_set(''.join(lists))
相比数字键处理函数多了一个判断当前已输入内容最后一位是否为运算符号,如果是替换它,否则添加该元素。
#等号键处理函数
def pressEqual(self):
global lists
if len(lists) > 0:
if operator.eq(lists,['1','+']):
lists.clear()
app.result2_set('')
app.result_set('Python')
else:
if lists[-1] in ['+','-','*','/']:
del lists[-1]
computrStr = ''.join(lists) # 将列表内容用join命令将字符串链接起来
endNum = eval(computrStr) # 用eval命令运算字符串中的内容
app.result_set(endNum) # 将运算结果显示到屏幕1
app.result2_set(computrStr) # 将运算过程显示到屏幕2
lists.clear() # 清空列表内容
lists.append(str(endNum)) # 将计算结果存到列表中,以便下一步计算
#清零键处理函数
def Clr(self):
app.result_set('0') # 将运算结果显示到屏幕1
app.result2_set('') # 将运算过程显示到屏幕2
lists.clear() # 清空列表内容
#删除键处理函数
def BackSp(self):
del lists[-1]
if len(lists) == 0:
app.result_set('0') # 将运算结果显示到屏幕1
else:
computrStr = ''.join(lists) # 将列表内容用join命令将字符串链接起来
app.result_set(computrStr) # 将运算结果显示到屏幕1
def Square(self):
if len(lists) == 0:
app.result_set('0') # 讲运算结果显示到屏幕1
else:
if lists[-1] in ['+','-','*','/']:
del lists[-1]
computrStr = ''.join(lists) # 将列表内容用join命令将字符串链接起来
endNum = eval(computrStr)
endNum = endNum*endNum
app.result_set(endNum) # 将运算结果显示到屏幕1
app.result2_set('('+computrStr+')^2')
lists.clear() # 清空列表内容
lists.append(str(endNum)) # 将计算结果存到列表中,以便下一步计算
def Sqrt(self):
if len(lists) != 0:
if lists[-1] in ['+','-','*','/']:
del lists[-1]
computrStr = ''.join(lists) # 将列表内容用join命令将字符串链接起来
endNum = eval(computrStr)
endNum = endNum ** 0.5
app.result_set(endNum) # 将运算结果显示到屏幕1
app.result2_set('sqrt('+computrStr+')')
lists.clear() # 清空列表内容
lists.append(str(endNum)) # 将计算结果存到列表中,以便下一步计算
各个函数的具体实现,有一个彩蛋,输1+
然后按等于就会出现。
#弹窗
def About(self):
messagebox.showinfo('关于', 'Ver 1.0 \n---\nBy zeis\n')
def Other(self):
messagebox.showinfo('功能', ' 暂时没有其他功能\n emmmm...')
About()
和Other()
分别是关于和功能菜单的弹窗函数。
#实例化Application()类
app = Application()
# 设置窗口标题:
app.master.title('计算器')
# 主消息循环:
app.mainloop()
程序完成。
有许多未解决bug。