使用 PyQt6 制作一个简易计算器,支持加减乘除、小数点、百分号等基本操作,现分享完整代码,环境一致可直接运行。
运行环境:Python3.10,PyQt6版本为 6.4.2。
参考:PyQt5做计算器(详细讲解, 绝对让你学会)-CSDN博客
运行展示
由于是用 eval 函数计算的,所以复刻了计算机科学领域经典 bug:0.1+0.2不等于0.3,使用try/except语句捕获异常,避免崩溃。
完整代码
1. 导入库及构建计算器类
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QWidget, QSizePolicy, QGridLayout, QPushButton, QApplication, QTextEdit
import sys
class Calculator(QWidget):
def __init__(self):
super(Calculator, self).__init__()
self.minwh = 240, 240
self.setMinimumSize(self.minwh[0], self.minwh[1])
self.resize(self.minwh[0], self.minwh[1])
self.ui()
self.operators = '+-*/∓%.' # 用于判断按钮的值是不是操作符
self.numbers = '0123456789' # 用于判断按钮的值是不是数字
self.last_oper_flag = False # 上次按钮是否为操作符
self.complete = True # 计算完成状态
self.values = ['0'] # 储存数字
self.operas = [] # 储存操作符
2. 设计界面
def ui(self):
# 初始化界面, self.text_edit 即计算器的屏幕
self.text_edit = QTextEdit('0', self)
self.text_edit.setReadOnly(True)
self.text_edit.setMinimumHeight(40)
self.text_edit.setMaximumHeight(40)
self.text_edit.setAlignment(Qt.AlignmentFlag.AlignRight)
self.text_edit.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
grid = QGridLayout(self) # 使用girdlayout进行界面布局
btn_names = [
'C', '<', '%', '/',
'7', '8', '9', '*',
'4', '5', '6', '-',
'1', '2', '3', '+',
'∓', '0', '.', '='
]
grid.addWidget(self.text_edit, 0, 0, 1, 4)
positions = [(i, j) for i in range(1, 6) for j in range(4)]
for pos, name in zip(positions, btn_names):
btn = QPushButton(name, self)
btn.clicked.connect(self.fun) # 把每个按钮连接到点击事件上
btn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
grid.addWidget(btn, *pos)
self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
self.setWindowTitle('Calculator')
self.show()
3. 按下按钮后执行的函数
def fun(self):
sender = self.sender()
btn_text = sender.text()
if btn_text == '=': # 点击 =
if not self.complete: # 计算结果
self.compute()
# else: pass # 计算已完成则不执行操作
else:
if btn_text == 'C': # 点击 C
self.refresh()
elif btn_text == '<': # 点击 <
self.delete()
elif btn_text in self.numbers: # 点击数字
self.f_number(btn_text)
else: # 点击操作符
self.f_operta(btn_text)
4. 按下 C 按钮归零计算器
def refresh(self):
self.values = ['0']
self.operas = []
self.last_oper_flag = False
self.text_edit.setText('0')
self.text_edit.setAlignment(Qt.AlignmentFlag.AlignRight)
5. 按下 DELETE 按钮执行的函数
def delete(self):
if self.complete: # 计算已完成则归零
self.refresh()
else:
# 最后一个数字只有一位数字或一个 (-x) 就去除最后一个数字
if len(self.values[-1]) == 1 or len(self.values[-1]) == 4 and self.values[-1].startswith('(-'):
self.values.pop()
if len(self.operas) > 0: # 去除最后一个操作符
self.operas.pop()
# 删除最后一个数字的最后一个字符
elif self.values[-1].endswith(')'): # 最后一个数字含括号
self.values[-1] = self.values[-1][:-2] + ')'
else: # 最后一个数字不含括号
self.values[-1] = self.values[-1][:-1]
# 储存的数字已被清空则归零
if len(self.values) == 0:
self.refresh()
self.rend()
6. 按下数字按钮执行的函数
def f_number(self, btn_text):
if self.complete:
self.complete = False
if self.last_oper_flag: # 上次按了操作符则正常添加数字
self.values.append(btn_text)
else: # 上次为添加数字
if len(self.values[-1]) == 1 and self.values[-1] == '0': # 最后一个数只有一个0, 去掉0再添加数字
self.values[-1] = self.values[-1][:-1] + btn_text
elif self.values[-1][-1] == ')': # 最后有括号, 在 ) 前添加数字
self.values[-1] = self.values[-1][:-1] + btn_text + ')'
else: # 正常添加数字
self.values[-1] = self.values[-1] + btn_text
self.last_oper_flag = False
self.rend()
7. 按下操作符按钮执行的函数
def f_operta(self, btn_text):
if self.last_oper_flag: # 上次按了 +-*/ 则无视上次操作
self.last_oper_flag = False
self.operas.pop()
if btn_text == '∓': # 最后一个数字乘以-1
value = eval('-' + self.values[-1])
if value < 0: # 若结果为负, 则用括号包围
self.values[-1] = '(' + str(value) + ')'
else:
self.values[-1] = str(value)
elif btn_text == '%': # 最后一个数字乘以0.01
value = eval('0.01*' + self.values[-1])
self.values[-1] = str(value)
elif btn_text == '.': # 最后一个数字添加小数点
if not '.' in self.values[-1]: # 最后一个数字不含小数点
if self.values[-1][-1] == ')': # 最后一个数字含括号, 在 ) 前添加小数点
self.values[-1] = self.values[-1][:-1] + '.' + ')'
else: # 正常添加小数点
self.values[-1] += '.'
else: # 本次按了 +-*/
self.last_oper_flag = True
self.operas.append(btn_text)
self.rend()
8. 按下 = 按钮执行计算
def compute(self):
text = self.get_text()
try:
result = str(eval(text))
except Exception as e:
result = 'Error: ' + str(e) + '!'
text = text + ' = \n' + result
self.text_edit.setText(text)
self.text_edit.setAlignment(Qt.AlignmentFlag.AlignRight)
self.values = ['0']
self.operas = []
self.last_oper_flag = False
self.complete = True
9. 屏显及自适应缩放
def get_text(self):
# 获得屏幕显示字符
n = len(self.values)
text = ''
for i in range(n):
text += self.values[i]
if i < n - 1:
text += ' ' + self.operas[i] + ' '
return text
def rend(self):
# 在屏幕上显示字符
text = self.get_text()
self.text_edit.setText(text)
self.text_edit.setAlignment(Qt.AlignmentFlag.AlignRight)
def resizeMainWindow(self):
# 自适应缩放
self.adjustSize()
10. 运行程序
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Calculator()
sys.exit(app.exec())