python中的GUI實現(xiàn)計算器
一、學習目標:學會利用python的GUI做界面布局
- 手寫計算器代碼熟悉控件的使用方法
- 優(yōu)化計算器代碼,解決 獲取按鈕文本 的方法
- 了解
lambda
函數(shù)的傳參優(yōu)點和局限 - 打包生成自己的計算器軟件,并獨立運行
二、學習內(nèi)容:手寫一個簡單計算器
1、計算器目標圖
目標計算器設(shè)計分為三個部分:
背景部分 :
根:Tk()
展示部分:上方展示算式,下方展示計算結(jié)果:
按鈕部分:
2、 計算器計算功能
- 加減乘除,放在 = ,按鈕上
- 回退,放在 <- 按鈕上
- 清除,放在 MC 按鈕上
3、 代碼實現(xiàn)與詳細說明
外觀布局:
首先引入包: from tkinter import *
根據(jù)目標圖片設(shè)計布局按鈕:定義計算器類,初始化界面控件。
class Calculator: def __init__(self, master): self.master = master self.master.title("Calculator") self.master.resizable(0, 0) # 設(shè)置窗口不可拉伸 self.master.geometry('320x420') # 設(shè)置主窗口的初始尺寸 self.result = StringVar() # 用于顯示結(jié)果的可變文本 self.equation = StringVar() # 顯示計算方程 self.result.set(' ') self.equation.set('0') # 顯示框 self.show_result_eq = Label(self.master, bg='white', fg='black', font=('Arail', '16'), bd='0', textvariable=self.equation, anchor='se') self.show_result = Label(self.master, bg='white', fg='black', font=('Arail', '20'), bd='0', textvariable=self.result, anchor='se') # 按鈕 self.button_back = Button(self.master, text='<-', bg='DarkGray', command=self.back) # 返回 self.button_lbracket = Button(self.master, text='(', bg='DarkGray', command=lambda: self.getNum('(')) # 左括號 self.button_rbracket = Button(self.master, text=')', bg='DarkGray', command=lambda: self.getNum(')')) # 左括號 self.button_division = Button(self.master, text='÷', bg='DarkGray', command=lambda: self.getNum('÷')) # 除號 # 7 8 9 4 5 6 1 2 3 self.button_7 = Button(self.master, text='7', bg='DarkGray', command=lambda: self.getNum('7')) # 7號 self.button_8 = Button(self.master, text='8', bg='DarkGray', command=lambda: self.getNum('8')) # 8號 self.button_9 = Button(self.master, text='9', bg='DarkGray', command=lambda: self.getNum('9')) # 9號 self.button_multiplication = Button(self.master, text='*', bg='DarkGray', command=lambda: self.getNum('*')) # 乘號 # 按鈕的command參數(shù),是回調(diào)函數(shù)。lambda函數(shù)是為了可以傳參數(shù)給回調(diào)函數(shù) self.button_4 = Button(self.master, text='4', bg='DarkGray', command=lambda: self.getNum('4')) # 4號 self.button_5 = Button(self.master, text='5', bg='DarkGray', command=lambda: self.getNum('5')) # 5號 self.button_6 = Button(self.master, text='6', bg='DarkGray', command=lambda: self.getNum('6')) # 6號 self.button_minus = Button(self.master, text='-', bg='DarkGray', command=lambda: self.getNum('-')) # -號 self.button_1 = Button(self.master, text='1', bg='DarkGray', command=lambda: self.getNum('1')) # 1號 self.button_2 = Button(self.master, text='2', bg='DarkGray', command=lambda: self.getNum('2')) # 2號 self.button_3 = Button(self.master, text='3', bg='DarkGray', command=lambda: self.getNum('3')) # 3號 self.button_plus = Button(self.master, text='+', bg='DarkGray', command=lambda: self.getNum('+')) # +號 # 控制按鈕 0 . self.button_MC = Button(self.master, text='MC', bg='DarkGray', command=self.clear) # MC self.button_0 = Button(self.master, text='0', bg='DarkGray', command=lambda: self.getNum('0')) # 0 self.button_dot = Button(self.master, text='.', bg='DarkGray', command=lambda: self.getNum('.')) # . self.button_eq = Button(self.master, text='=', bg='DarkGray', command=self.run) # = # Layout布局 self.show_result_eq.place(x='10', y='10', width='300', height='50') self.show_result.place(x='10', y='60', width='300', height='50') self.button_back.place(x='10', y='150', width='60', height='40') self.button_lbracket.place(x='90', y='150', width='60', height='40') self.button_rbracket.place(x='170', y='150', width='60', height='40') self.button_division.place(x='250', y='150', width='60', height='40') self.button_7.place(x='10', y='205', width='60', height='40') self.button_8.place(x='90', y='205', width='60', height='40') self.button_9.place(x='170', y='205', width='60', height='40') self.button_multiplication.place(x='250', y='205', width='60', height='40') self.button_4.place(x='10', y='260', width='60', height='40') self.button_5.place(x='90', y='260', width='60', height='40') self.button_6.place(x='170', y='260', width='60', height='40') self.button_minus.place(x='250', y='260', width='60', height='40') self.button_1.place(x='10', y='315', width='60', height='40') self.button_2.place(x='90', y='315', width='60', height='40') self.button_3.place(x='170', y='315', width='60', height='40') self.button_plus.place(x='250', y='315', width='60', height='40') self.button_MC.place(x='10', y='370', width='60', height='40') self.button_0.place(x='90', y='370', width='60', height='40') self.button_dot.place(x='170', y='370', width='60', height='40') self.button_eq.place(x='250', y='370', width='60', height='40')
重點說明:
按鈕的command
參數(shù),是回調(diào)函數(shù)。lambda
函數(shù)是為了可以傳參數(shù)給回調(diào)函數(shù)。
lambda匿名函數(shù)的使用: command=lambda: self.getNum('3'))
注意這里傳的參數(shù)是字符串: '3' 。每一個按鈕點擊想要獲取的文本值不同,所以對應的參數(shù)各不相同。
從而,也導致初始化界面代碼看起來太冗長了。
功能布局:
設(shè)置回退 back
,符號獲取 getNum
,清除 clear
,計算 run
方法。
def back(self): temp_equ = self.equation.get() self.equation.set(temp_equ[:-1]) # 一個一個刪 def getNum(self, arg): temp_equ = self.equation.get() # 輸入算式 temp_result = self.result.get() # 判斷基本語法錯誤 if temp_result != ' ': # 計算器輸入前還沒有結(jié)果,那么結(jié)果區(qū)域應該設(shè)置為空。 self.result.set(' ') if temp_equ == '0' and (arg not in ['.', '+', '-', '*', '÷']): # 如果首次輸入為0,則緊跟則不能是數(shù)字,只是小數(shù)點或運算符 temp_equ = '' if len(temp_equ) > 2 and temp_equ[-1] == '0': # 運算符后面也不能出現(xiàn)0+數(shù)字的情形03,09,x if (temp_equ[-2] in ['+', '-', '*', '÷']) and ( arg in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '(']): temp_equ = temp_equ[:-1] temp_equ = temp_equ + arg self.equation.set(temp_equ) def clear(self): self.equation.set('0') self.result.set(' ') def run(self): temp_equ = self.equation.get() temp_equ = temp_equ.replace('÷', '/') if temp_equ[0] in ['+', '-', '*', '÷']: temp_equ = '0' + temp_equ print(temp_equ) try: answer = '%.4f' % eval(temp_equ) # 保留兩位小數(shù) self.result.set(str(answer)) except (ZeroDivisionError, SyntaxError): # 其他除0錯誤,或語法錯誤返回Error self.result.set(str('Error'))
測試實驗與總結(jié):
測試:
if __name__ == "__main__": root = Tk() my_cal = Calculator(root) root.mainloop()
小結(jié)
- 1. 按鈕的
command
參數(shù)設(shè)置的是回調(diào)函數(shù),點擊按鈕后的操作由函數(shù)完成。 - 2.將回調(diào)函數(shù)設(shè)置成匿名函數(shù),
lambda
函數(shù)是可以傳參數(shù)給回調(diào)函數(shù)。這個參數(shù)是在調(diào)用函數(shù)的時候才傳入,不會在定義的時候保存。 - 3. 關(guān)于控件的類型,控件的屬性,控件的放置方式
控件類型,可以通過多dir函數(shù)查看:比如 dir
(tkinter) ,找到控件
控件:19
'Button','Canvas'畫布,'Text'文本,'Checkbutton'復選按鈕,'Radiobutton'單選按鈕,'Frame'框架,'Message'消息,
'Entry'條目實體,'Label'標簽,'LabelFrame'標簽框架, 'Listbox'列表框,'Menu'菜單, 'Menubutton'菜單按鈕,
'Scale'縮放,'Scrollbar'滾動條,'Toplevel'頂級,'Spinbox'旋轉(zhuǎn)框,'PanedWindow'窗格窗口,'tkMessageBox'消息框
控件屬性查看: dir
(Button) 把用得到的控件都查一遍,就清楚怎么用它們了。
放置方式–集合布局:當定義一個控件,均需要讓它布局到窗口上,用到三個函數(shù): pack
, grid
, place
pack
的參數(shù)side,可設(shè)置 上下左右布局 ;
grid 的參數(shù)row
,column
,可設(shè)置 行列網(wǎng)格布局 ;
同一塊畫布不能混合使用。
關(guān)于如何避免同志們亂寫算式,使軟件奔潰的問題。
本計算器計算功能是通過獲取將輸入表達式,利用eval()
函數(shù)來執(zhí)行python
代碼字符串的。
那么就要杜絕不合理的輸入表達式,不能阻止人家亂點,可以在程序中設(shè)置 try...except...else...finally
語句捕捉異常,設(shè)置合理的的響應。
三、學習優(yōu)化:學會優(yōu)化冗于代碼
優(yōu)化方向:
- 1、 初始化布局頁面代碼冗余;
- 2、 lambda匿名函數(shù)包裹回調(diào)函數(shù)時傳參的問題
代碼實現(xiàn):這里主要優(yōu)化初始化函數(shù),其他函數(shù)采用繼承。
優(yōu)化計算器類Calc繼承Calculator
的back
,clear
,run
,getNum
。增加initPage
來定義頁面控件布局。
# 計算器,優(yōu)化程序 class Calc(Calculator): def __init__(self, master): self.master = master self.master.title("Calculator") self.master.resizable(0, 0) # 設(shè)置窗口不可拉伸 self.master.geometry('320x420') # 設(shè)置主窗口的初始尺寸 self.result = StringVar() # 用于顯示結(jié)果的可變文本 self.equation = StringVar() # 顯示計算方程 self.result.set(' ') self.equation.set('0') self.labels = ['<-', '(', ')', '÷', '7', '8', '9', '*', '4', '5', '6', '-', '1', '2', '3', '+', 'MC', '0', '.', '=', ] # 顯示框 self.show_result_eq = Label(self.master, bg='white', fg='black', font=('Arail', '16'), bd='0', textvariable=self.equation, anchor='se') self.show_result = Label(self.master, bg='white', fg='black', font=('Arail', '20'), bd='0', textvariable=self.result, anchor='se') # 按鈕 # self.button_dict = {} # Layout布局 self.show_result_eq.place(x='10', y='10', width='300', height='50') self.show_result.place(x='10', y='60', width='300', height='50') self.initPage() def initPage(self): X = ['10', '90', '170', '250'] Y = ['150', '205', '260', '315', '370'] lengths = len(self.labels) # 20 y_ = -1 # 設(shè)置按鈕并布局 for label in self.labels: print(label) index = self.labels.index(label) x_ = index % 4 if x_ == 0: y_ += 1 if label == '<-': button = Button(self.master, text=label, bg='DarkGray', command=self.back) button.place(x=X[x_], y=Y[y_], width='60', height='40') elif label == '=': button = Button(self.master, text=label, bg='DarkGray', command=self.run) button.place(x=X[x_], y=Y[y_], width='60', height='40') elif label == 'MC': button = Button(self.master, text=label, bg='DarkGray', command=self.clear) button.place(x=X[x_], y=Y[y_], width='60', height='40') else: # 因為lambda函數(shù)有傳參功能,但只有調(diào)用的時候才傳參,所以數(shù)字按鈕永遠會調(diào)用最后一個label值。解決方案是自己洗一個button來保存label值 button = NumButton(self.master, text=label, bg='DarkGray', fun=self.getNum) button.btn.place(x=X[x_], y=Y[y_], width='60', height='40') # self.button_dict[label] = button
重點:以上代碼,倒數(shù)第二行,我采用自定義的NumButton,而不是原有的Butoon。
因為即使匿名函數(shù)lambda
函數(shù)有傳參功能,但只有調(diào)用的時候才傳參,所以for循環(huán)到最后,label變量的值永遠為列表最后一個 = 等于符號,一度讓人無解,只能自定義一個按鈕類型 NumButton
,來保存中間值。使按鈕們都有自己的文本值,并且command回調(diào)的時候能夠準確傳參。
class NumButton(): def __init__(self, frame, text, fun, **kwargs): # side = kwargs.get('side') if 'side' in kwargs else () # 此處沒用上 self.btn = Button( frame, text=text, activeforeground="blue", activebackground="pink", bg='DarkGray', command=lambda: fun(text) )
注意:
- 形式參數(shù) frame, text, fun
- frame是根部件。
- text按鈕的標簽文本,
- fun函數(shù)對象,就是待回調(diào)的函數(shù)。
測試總結(jié):
if __name__ == "__main__": root = Tk() # my_cal = Calculator(root) my_cal = Calc(root) root.mainloop()
自定義的NumButton
設(shè)置了按鈕激活時背景和字體的顏色變化,所以有點顏色。Button
自己也可以設(shè)置的。
測試沒有問題,就要開始打包:
1.確定安裝pyinstaller
包,沒有可以在環(huán)境下安裝: conda install pyinstaller
或 pip install pyinstaller
2. 打包: pyinstaller -F calc.py -w
3. 打包生成可執(zhí)行exe文件不了解請參考博文。
到此這篇關(guān)于python中的GUI實現(xiàn)計算器的文章就介紹到這了,更多相關(guān)python的GUI計算器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python利用Beautiful Soup模塊修改內(nèi)容方法示例
Beautiful Soup是一個可以從HTML或XML文件中提取數(shù)據(jù)的Python 庫。它能夠通過你喜歡的轉(zhuǎn)換器實現(xiàn)慣用的文檔導航、查找、修改文檔的方式。他還能夠修改HTML/XML文檔的內(nèi)容。這篇文章主要介紹了Python利用Beautiful Soup模塊修改內(nèi)容的方法,需要的朋友可以參考下。2017-03-03在python下使用tensorflow判斷是否存在文件夾的實例
今天小編就為大家分享一篇在python下使用tensorflow判斷是否存在文件夾的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-06-06Pandas.DataFrame時間序列數(shù)據(jù)處理的實現(xiàn)
本文主要介紹了Pandas.DataFrame時間序列數(shù)據(jù)處理的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-02-02python利用蒙版摳圖(使用PIL.Image和cv2)輸出透明背景圖
這篇文章主要介紹了python利用蒙版摳圖(使用PIL.Image和cv2)輸出透明背景圖,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-08-08python判斷列表字典字符串元組是否存在某個值或者空值(多種方法)
這篇文章主要介紹了python判斷列表字典字符串元組是否存在某個值或者空值,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2024-02-02