Python結(jié)合PyQt5打造一個(gè)高級(jí)多功能計(jì)算器
?你有沒(méi)有遇到過(guò)這種場(chǎng)景——正在專心寫(xiě)代碼或者處理報(bào)表,突然需要算點(diǎn)東西。打開(kāi)系統(tǒng)自帶的計(jì)算器,發(fā)現(xiàn)它只是個(gè)“小學(xué)生水平”的工具,開(kāi)個(gè)根號(hào)都要切到科學(xué)模式,更別提算三角函數(shù)或者做單位換算了。你嘆了口氣,要么打開(kāi)網(wǎng)頁(yè)搜一個(gè)在線計(jì)算器,忍受一堆廣告的轟炸,要么干脆手算。
我當(dāng)時(shí)的想法很簡(jiǎn)單:為什么不自己寫(xiě)一個(gè)?
反正天天跟Python打交道,PyQt5這框架也聽(tīng)說(shuō)過(guò)不少次。與其讓別人的工具打斷工作流,不如親手打造一個(gè)趁手的“兵器”。這篇文章就是我折騰出來(lái)的成果——一個(gè)能當(dāng)主力工具用的多功能計(jì)算器,順便聊聊踩過(guò)的那些坑。
從界面開(kāi)始,像搭積木一樣擺按鈕
寫(xiě)GUI程序的第一步永遠(yuǎn)是搭界面。PyQt5有個(gè)好處,可視化工具Qt Designer可以直接拖拖拽拽把框架搭好,不過(guò)我習(xí)慣手寫(xiě)代碼——這樣以后改樣式、加功能心里更有數(shù)。
界面布局其實(shí)挺像擺積木。最上面放一個(gè)QLCDNumber作為顯示屏,要那種老式計(jì)算器的數(shù)字風(fēng)格,看著舒服。下面用QGridLayout把按鈕排成網(wǎng)格,四行四列不夠就加行,數(shù)字、運(yùn)算符、功能鍵各就各位。
顏色我做了區(qū)分:數(shù)字按鈕白底藍(lán)字,運(yùn)算符灰底黑字,AC(清空)用紅色,等號(hào)用綠色。這種視覺(jué)分組能讓操作更快,不用仔細(xì)看就知道哪個(gè)區(qū)域是干嘛的。
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys
import math
class Calculator(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("多功能計(jì)算器")
self.setFixedSize(400, 500)
# 顯示屏
self.display = QLCDNumber()
self.display.setDigitCount(15)
self.display.setSegmentStyle(QLCDNumber.Flat)
# 按鈕布局
self.create_buttons()
這里有個(gè)小技巧:顯示屏的digitCount設(shè)15位夠用了,真要算天文數(shù)字也不會(huì)用這個(gè)計(jì)算器。setSegmentStyle讓數(shù)字顯示更現(xiàn)代一點(diǎn),老式凸起風(fēng)格有點(diǎn)過(guò)時(shí)。
按鈕動(dòng)起來(lái),信號(hào)與槽的靈魂所在
PyQt最核心的機(jī)制叫“信號(hào)與槽”,聽(tīng)起來(lái)高大上,說(shuō)白了就是“點(diǎn)一下按鈕,做一件事”。每個(gè)按鈕被點(diǎn)擊時(shí)發(fā)出clicked信號(hào),我們把這個(gè)信號(hào)連接到對(duì)應(yīng)的處理函數(shù)上。
def create_buttons(self):
buttons = [
['7', '8', '9', '/'],
['4', '5', '6', '*'],
['1', '2', '3', '-'],
['0', '.', '=', '+'],
['C', '√', '^', 'AC']
]
grid = QGridLayout()
central_widget = QWidget()
central_widget.setLayout(grid)
self.setCentralWidget(central_widget)
# 把顯示屏和按鈕網(wǎng)格拼在一起
main_layout = QVBoxLayout()
main_layout.addWidget(self.display)
main_layout.addLayout(grid)
central_widget.setLayout(main_layout)
for row, row_buttons in enumerate(buttons):
for col, btn_text in enumerate(row_buttons):
btn = QPushButton(btn_text)
btn.clicked.connect(self.on_button_clicked)
grid.addWidget(btn, row, col)
上面代碼里的connect就是關(guān)鍵。不管按了哪個(gè)按鈕,都交給同一個(gè)on_button_clicked函數(shù)處理,通過(guò)按鈕上的文字來(lái)判斷要做什么。
計(jì)算邏輯,比想象中要麻煩一點(diǎn)
一開(kāi)始我圖省事,直接用Python的eval函數(shù)計(jì)算表達(dá)式。這玩意兒確實(shí)方便,你給它字符串"2+3*4",它真能算出14。但eval有個(gè)大問(wèn)題——不安全。惡意輸入可能執(zhí)行系統(tǒng)命令,雖然計(jì)算器場(chǎng)景風(fēng)險(xiǎn)不高,但這習(xí)慣不好。
再說(shuō)用戶體驗(yàn)。eval計(jì)算時(shí)沒(méi)有實(shí)時(shí)反饋,你按完一連串?dāng)?shù)字和運(yùn)算符,最后按等號(hào)才出結(jié)果。更好的做法是維護(hù)兩個(gè)狀態(tài):當(dāng)前輸入的數(shù)字和等待執(zhí)行的運(yùn)算。
def on_button_clicked(self):
text = self.sender().text()
if text.isdigit() or text == '.':
# 數(shù)字或小數(shù)點(diǎn),拼接到當(dāng)前輸入
self.current_input += text
self.display.display(self.current_input)
elif text in ['+', '-', '*', '/']:
# 運(yùn)算符,保存當(dāng)前數(shù)字和運(yùn)算符
self.last_number = float(self.current_input)
self.current_operator = text
self.current_input = ''
elif text == '=':
# 計(jì)算結(jié)果
current = float(self.current_input)
if self.current_operator == '+':
result = self.last_number + current
elif self.current_operator == '-':
result = self.last_number - current
# ... 其他運(yùn)算
self.display.display(result)
self.current_input = str(result)
這種狀態(tài)機(jī)的方式讓計(jì)算器更“懂”你在做什么。按完“2+3”,顯示屏清空讓你輸?shù)诙€(gè)數(shù),最后按等號(hào)才出結(jié)果——這就是計(jì)算器該有的樣子。
進(jìn)階功能,讓計(jì)算器真正“高級(jí)”起來(lái)
光有四則運(yùn)算稱不上“高級(jí)”??茖W(xué)計(jì)算器該有的東西得加上:三角函數(shù)、對(duì)數(shù)、開(kāi)方、冪運(yùn)算。
def calculate_function(self, func_name):
value = float(self.current_input)
if func_name == 'sqrt':
result = math.sqrt(value)
elif func_name == 'sin':
# 角度轉(zhuǎn)弧度
result = math.sin(math.radians(value))
elif func_name == 'cos':
result = math.cos(math.radians(value))
elif func_name == 'log':
result = math.log10(value)
elif func_name == 'ln':
result = math.log(value)
# ...
self.display.display(result)
self.current_input = str(result)
這里用Python標(biāo)準(zhǔn)庫(kù)的math模塊就夠了,不需要額外安裝東西。三角函數(shù)的坑是角度和弧度轉(zhuǎn)換,很多人第一次用math.sin(90)發(fā)現(xiàn)不是1,就是因?yàn)閭髁私嵌冗M(jìn)去,但函數(shù)要的是弧度。
還有個(gè)實(shí)用的功能是單位換算。長(zhǎng)度、重量、溫度之間的轉(zhuǎn)換其實(shí)都是乘個(gè)系數(shù),用字典存起來(lái)就行。比如長(zhǎng)度換算:{'m':1, 'cm':0.01, 'km':1000, 'inch':0.0254},用戶選“100 cm in m”這種格式,解析出來(lái)一乘一除就完事。
鍵盤(pán)支持,真正的效率提升
一個(gè)只能用鼠標(biāo)點(diǎn)的計(jì)算器算不上趁手。加上鍵盤(pán)監(jiān)聽(tīng),讓數(shù)字小鍵盤(pán)也能操作。
def keyPressEvent(self, event):
key = event.key()
if key in [Qt.Key_0, Qt.Key_1, ..., Qt.Key_9]:
self.current_input += chr(key)
elif key == Qt.Key_Plus:
self.set_operator('+')
elif key == Qt.Key_Return or key == Qt.Key_Enter:
self.calculate_result()
elif key == Qt.Key_Backspace:
self.current_input = self.current_input[:-1]
elif key == Qt.Key_Escape:
self.clear_all()
self.display.display(self.current_input)
重寫(xiě)keyPressEvent方法后,用戶甚至不需要碰鼠標(biāo)。右手操作數(shù)字小鍵盤(pán),左手輔助,這效率比系統(tǒng)計(jì)算器高太多了。
踩坑記錄,給后來(lái)人的避坑指南
Qt Designer確實(shí)能拖出界面,但生成的.ui文件要轉(zhuǎn)成.py,改一次轉(zhuǎn)一次,特別煩。不如手寫(xiě)代碼,版本控制也方便。
布局管理器用QVBoxLayout和QGridLayout組合,別用絕對(duì)坐標(biāo)place()。不同屏幕分辨率下,寫(xiě)死的坐標(biāo)會(huì)讓界面亂掉。布局管理器會(huì)自動(dòng)適配,省心。
除法要處理除零錯(cuò)誤,開(kāi)方不能開(kāi)負(fù)數(shù)。這些邊界條件不處理,程序一崩潰用戶體驗(yàn)直接歸零。try-except包一下,彈個(gè)提示框就行。
try:
result = self.last_number / current
except ZeroDivisionError:
QMessageBox.warning(self, "錯(cuò)誤", "除數(shù)不能為零")
return
為什么需要自己的計(jì)算器
市面上計(jì)算器那么多,為什么非要自己寫(xiě)一個(gè)?因?yàn)闆](méi)有人比你更懂你的工作流。
你可能經(jīng)常需要把計(jì)算結(jié)果復(fù)制到某個(gè)特定格式里,或者要記錄每一次計(jì)算的歷史,又或者要用到某個(gè)冷門(mén)的物理常數(shù)。這些需求通用軟件不會(huì)幫你實(shí)現(xiàn),但自己的工具可以。代碼就在那里,想加什么功能隨時(shí)改。
寫(xiě)完之后我把這個(gè)計(jì)算器設(shè)成了快捷鍵啟動(dòng),Ctrl+Alt+C呼出,算完自動(dòng)把結(jié)果復(fù)制到剪貼板。這個(gè)功能實(shí)現(xiàn)起來(lái)也就十來(lái)行代碼,但每天都能省下幾十秒,一年算下來(lái)是個(gè)不小的數(shù)字。
好的工具不是買來(lái)的,是自己造的。當(dāng)你需要的時(shí)候,動(dòng)手寫(xiě)一個(gè)就是最好的選擇。
?到此這篇關(guān)于Python結(jié)合PyQt5打造一個(gè)高級(jí)多功能計(jì)算器的文章就介紹到這了,更多相關(guān)Python計(jì)算器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Python結(jié)合Tkinter實(shí)現(xiàn)法定退休時(shí)間計(jì)算器
- Python結(jié)合Tkinter手搓一個(gè)寄存器計(jì)算器
- 使用Python寫(xiě)一個(gè)標(biāo)準(zhǔn)版和程序員版計(jì)算器
- Python使用PyQt開(kāi)發(fā)一個(gè)桌面計(jì)算器的實(shí)戰(zhàn)指南
- Python+pyqt實(shí)現(xiàn)即時(shí)進(jìn)制轉(zhuǎn)換計(jì)算器
- 使用Python實(shí)現(xiàn)一個(gè)簡(jiǎn)易計(jì)算器的新手指南
- 基于Python的tkinter庫(kù)開(kāi)發(fā)一個(gè)計(jì)算器的完整代碼
相關(guān)文章
詳解Python list 與 NumPy.ndarry 切片之間的對(duì)比
這篇文章主要介紹了詳解Python list 與 NumPy.ndarry 切片之間的區(qū)別的相關(guān)資料,list 切片返回的是不原數(shù)據(jù),對(duì)新數(shù)據(jù)的修改不會(huì)影響原數(shù)據(jù)而NumPy.ndarry 的切片返回的是原數(shù)據(jù)需要的朋友可以參考下2017-07-07
Python telnet登陸功能實(shí)現(xiàn)代碼
這篇文章主要介紹了Python telnet登陸功能實(shí)現(xiàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
簡(jiǎn)單介紹使用Python解析并修改XML文檔的方法
這篇文章主要介紹了使用Python解析并修改XML文檔的方法,是Python入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-10-10
Google colab中從kaggle中接入數(shù)據(jù)的操作方法
這篇文章主要介紹了Google colab中如何從kaggle中接入數(shù)據(jù),本文涉及到兩大平臺(tái)內(nèi)容,所以我默認(rèn)你已經(jīng)擁有了,并且使用過(guò)了一段時(shí)間的google賬號(hào)和kaggle賬號(hào),需要的朋友可以參考下2024-03-03
python神經(jīng)網(wǎng)絡(luò)編程實(shí)現(xiàn)手寫(xiě)數(shù)字識(shí)別
這篇文章主要為大家詳細(xì)介紹了python神經(jīng)網(wǎng)絡(luò)編程實(shí)現(xiàn)手寫(xiě)數(shù)字識(shí)別,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05
python GUI庫(kù)圖形界面開(kāi)發(fā)之PyQt5 MDI(多文檔窗口)QMidArea詳細(xì)使用方法與實(shí)例
這篇文章主要介紹了python GUI庫(kù)圖形界面開(kāi)發(fā)之PyQt5 MDI(多文檔窗口)QMidArea詳細(xì)使用方法與實(shí)例,需要的朋友可以參考下2020-03-03

