刚入门PyQt,制作了一个简易的计算器,放在这里方便大家进行学习。
因为PyCharm中安装的python版本是3.11,所以本程序利用的是PyQt 6,而非主流的PyQt 5,大家如果想要使用PyQt 5,将python相应地降低版本即可。至于PyQt的配置步骤,大家自行查阅网上的资料进行配置即可。
这个是计算器的界面,RE代表删除(return),M代表记忆最近一次的运算结果(memory),MR代表将记忆的运算结果展现出来(memory review)。
以下就是本程序的代码(包含了PyQt 6的环境配置)。
run.py
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QMainWindow
from surface import Ui_MainWindow
from logic import Callogic
class MyMainWindow(QMainWindow, Ui_MainWindow): # 继承
def __init__(self, *args, **kwargs):
super(MyMainWindow, self).__init__(*args, **kwargs)
self.setupUi(self)
self.cl: Callogic = Callogic(self.lcdNumber)
# cl是Callogic 类的实例,负责计算器应用程序的逻辑操作
self.do__signal__slot()
self.cl.init_cal()
def do__signal__slot(self):
# 给数字0到9建立clicked信号和槽函数
# lambda用于绑定参数,不需要单独定义多个带参数的回调函数,这使得代码更加简洁和易读。
self.number0.clicked.connect(lambda: self.cl.number_slot(0))
self.number1.clicked.connect(lambda: self.cl.number_slot(1))
self.number2.clicked.connect(lambda: self.cl.number_slot(2))
self.number3.clicked.connect(lambda: self.cl.number_slot(3))
self.number4.clicked.connect(lambda: self.cl.number_slot(4))
self.number5.clicked.connect(lambda: self.cl.number_slot(5))
self.number6.clicked.connect(lambda: self.cl.number_slot(6))
self.number7.clicked.connect(lambda: self.cl.number_slot(7))
self.number8.clicked.connect(lambda: self.cl.number_slot(8))
self.number9.clicked.connect(lambda: self.cl.number_slot(9))
# 给+-*/建立clicked信号和槽函数
self.click_addition.clicked.connect(lambda: self.cl.operation_slot("+"))
self.click_subtraction.clicked.connect(lambda: self.cl.operation_slot("-"))
self.click_multiplication.clicked.connect(lambda: self.cl.operation_slot("*"))
self.click_divison.clicked.connect(lambda: self.cl.operation_slot("/"))
# 给%建立clicked信号和槽函数
self.percent_click.clicked.connect(self.cl.perc_slot)
# 给RE、M、MR建立clicked信号和槽函数
self.RE.clicked.connect(self.cl.re_slot)
self.M.clicked.connect(self.cl.m_slot)
self.MR.clicked.connect(self.cl.mr_slot)
#给=建立clicked信号和槽函数
self.click_equal.clicked.connect(self.cl.eq_slot)
# 建立click_point按钮的clicked信号和槽函数连接
self.click_point.clicked.connect(self.cl.point_slot)
if __name__=='__main__':
# 创建一个QApplication对象,管理应用程序的控制流和主要设置。
app = QApplication(sys.argv)
window = MyMainWindow()
window.setWindowTitle("计算器")
window.show()
app.exec()
logic.py
from PyQt6 import QtWidgets
class CalState: # 定义计算器状态
READY = 0 # 计算器准备好接收输入
INPUT = 1 # 计算器正在接收输入
class Callogic: # 定义计算器初始类
def __init__(self, lcd_widget: QtWidgets.QLCDNumber):
self.lcd_widget = lcd_widget
self.state = None # 当前计算器的状态
self.stack = None # 存储操作中的操作数
self.last_operation = None # 保存最近一次的操作数
self.current_operator = None # 保存当前操作数
self.memory = None # 存储器,用于M和MR功能
def init_cal(self): # 初始化计算机状态
self.state = CalState.READY
self.stack = [0]
self.last_operation = None
self.current_operator = None
self.memory = None
self._display()
def _display(self): # 计算器数字显示屏显示
if self.stack[-1] == 0 and len(self.stack) == 1:
self.lcd_widget.setText('') # 如果堆栈中只有一个0,则显示为空
else:
self.lcd_widget.setText(str(self.stack[-1]))
def number_slot(self, num_val): # 数字0到9的槽函数
if self.state == CalState.READY: # 准备接收输入,将状态改成正在接收输入
self.stack[-1] = num_val
self.state = CalState.INPUT
else: # 否则将当前值追加到堆栈顶部
current_value = str(self.stack[-1])
if '.' in current_value:
# 如果当前值中已有小数点,则继续按浮点数处理
self.stack[-1] = float(current_value + str(num_val))
else:
# 否则,按整数处理
self.stack[-1] = int(current_value + str(num_val))
self._display()
def operation_slot(self, oper): # 运算符号+-*/的槽函数
if self.state == CalState.INPUT and len(self.stack) > 1:
self.eq_slot() # 处理之前未完成的操作
self.current_operator = oper
self.state = CalState.INPUT
self.stack.append(0) # 向stack列表添加一个0,此时stack中有两个元素[0,0]
def perc_slot(self): # 实现%的槽函数
self.stack[-1] = float(self.stack[-1]) * 0.01
self._display()
self.state = CalState.INPUT
def eq_slot(self): # 实现=的槽函数
if self.current_operator and len(self.stack) > 1:
try:
result = self._do_operator(self.stack[0], self.stack[1])
except ZeroDivisionError: # 可以改进一下
self.lcd_widget.setText("错误: 除数不能为零")
self.stack = [0] # 清空stack
self.state = CalState.READY
self.current_operator = None
return
except Exception: # 清空stack
self.lcd_widget.setText("错误")
self.stack = [0]
self.state = CalState.READY
self.current_operator = None
return
self.stack = [result]
self.current_operator = None
self.state = CalState.READY
self._display() # self.stack = [3]
def _do_operator(self, operand1, operand2): # 运算逻辑
if self.current_operator == "+":
result = operand1 + operand2
elif self.current_operator == "-":
result = operand1 - operand2
elif self.current_operator == "*":
result = operand1 * operand2
elif self.current_operator == "/":
if operand2 == 0:
raise ZeroDivisionError("除数不能为零")
result = operand1 / operand2
result = round(result, 15) # 限制除法结果的小数点后15位
if isinstance(result, float) and result.is_integer(): # 检查结果是否为整数
result = int(result)
return result
def re_slot(self): # 回退功能的实现
if self.state == CalState.INPUT:
current_value = str(self.stack[-1])
if len(current_value) > 1:
new_value = current_value[:-1]
if new_value == "-":
new_value = "0" # 防止单独一个负号
elif new_value[-1] == ".":
new_value = new_value[:-1]
if new_value:
if "." in new_value:
self.stack[-1] = float(new_value)
else:
self.stack[-1] = int(new_value)
else:
self.stack[-1] = 0
else:
self.stack[-1] = 0
self._display()
def m_slot(self): # 记忆功能的实现,可以优化一下
self.memory = self.stack[-1] # 将当前值存储在memory中
def mr_slot(self): # 加载记录了的数字
if self.memory is not None:
self.stack[-1] = self.memory # 读取memory中的值
self._display()
def point_slot(self): # 小数点的槽函数
current_value = str(self.stack[-1])
if '.' not in current_value:
self.stack[-1] = current_value + '.'
self._display()
surface.py
# Form implementation generated from reading ui file 'surface.ui'
#
# Created by: PyQt6 UI code generator 6.4.2
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt6 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(366, 237)
MainWindow.setMinimumSize(QtCore.QSize(366, 237))
MainWindow.setMaximumSize(QtCore.QSize(366, 237))
self.layoutWidget = QtWidgets.QWidget(parent=MainWindow)
self.layoutWidget.setGeometry(QtCore.QRect(21, 11, 322, 205))
self.layoutWidget.setObjectName("layoutWidget")
self.gridLayout_2 = QtWidgets.QGridLayout(self.layoutWidget)
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
self.gridLayout_2.setObjectName("gridLayout_2")
self.lcdNumber = QtWidgets.QLineEdit(parent=self.layoutWidget)
self.lcdNumber.setMinimumSize(QtCore.QSize(0, 50))
font = QtGui.QFont()
font.setPointSize(15)
font.setBold(True)
self.lcdNumber.setFont(font)
self.lcdNumber.setLayoutDirection(QtCore.Qt.LayoutDirection.RightToLeft)
self.lcdNumber.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.lcdNumber.setObjectName("lcdNumber")
self.gridLayout_2.addWidget(self.lcdNumber, 0, 0, 1, 1)
self.gridLayout = QtWidgets.QGridLayout()
self.gridLayout.setObjectName("gridLayout")
self.RE = QtWidgets.QPushButton(parent=self.layoutWidget)
self.RE.setObjectName("RE")
self.gridLayout.addWidget(self.RE, 0, 0, 1, 1)
self.M = QtWidgets.QPushButton(parent=self.layoutWidget)
self.M.setObjectName("M")
self.gridLayout.addWidget(self.M, 0, 1, 1, 1)
self.MR = QtWidgets.QPushButton(parent=self.layoutWidget)
self.MR.setObjectName("MR")
self.gridLayout.addWidget(self.MR, 0, 2, 1, 1)
self.click_divison = QtWidgets.QPushButton(parent=self.layoutWidget)
self.click_divison.setObjectName("click_divison")
self.gridLayout.addWidget(self.click_divison, 0, 3, 1, 1)
self.number7 = QtWidgets.QPushButton(parent=self.layoutWidget)
self.number7.setObjectName("number7")
self.gridLayout.addWidget(self.number7, 1, 0, 1, 1)
self.number8 = QtWidgets.QPushButton(parent=self.layoutWidget)
self.number8.setObjectName("number8")
self.gridLayout.addWidget(self.number8, 1, 1, 1, 1)
self.number9 = QtWidgets.QPushButton(parent=self.layoutWidget)
self.number9.setObjectName("number9")
self.gridLayout.addWidget(self.number9, 1, 2, 1, 1)
self.click_multiplication = QtWidgets.QPushButton(parent=self.layoutWidget)
self.click_multiplication.setObjectName("click_multiplication")
self.gridLayout.addWidget(self.click_multiplication, 1, 3, 1, 1)
self.number4 = QtWidgets.QPushButton(parent=self.layoutWidget)
self.number4.setObjectName("number4")
self.gridLayout.addWidget(self.number4, 2, 0, 1, 1)
self.number5 = QtWidgets.QPushButton(parent=self.layoutWidget)
self.number5.setObjectName("number5")
self.gridLayout.addWidget(self.number5, 2, 1, 1, 1)
self.number6 = QtWidgets.QPushButton(parent=self.layoutWidget)
self.number6.setObjectName("number6")
self.gridLayout.addWidget(self.number6, 2, 2, 1, 1)
self.click_subtraction = QtWidgets.QPushButton(parent=self.layoutWidget)
self.click_subtraction.setObjectName("click_subtraction")
self.gridLayout.addWidget(self.click_subtraction, 2, 3, 1, 1)
self.number1 = QtWidgets.QPushButton(parent=self.layoutWidget)
self.number1.setObjectName("number1")
self.gridLayout.addWidget(self.number1, 3, 0, 1, 1)
self.number2 = QtWidgets.QPushButton(parent=self.layoutWidget)
self.number2.setObjectName("number2")
self.gridLayout.addWidget(self.number2, 3, 1, 1, 1)
self.number3 = QtWidgets.QPushButton(parent=self.layoutWidget)
self.number3.setObjectName("number3")
self.gridLayout.addWidget(self.number3, 3, 2, 1, 1)
self.click_addition = QtWidgets.QPushButton(parent=self.layoutWidget)
self.click_addition.setObjectName("click_addition")
self.gridLayout.addWidget(self.click_addition, 3, 3, 1, 1)
self.percent_click = QtWidgets.QPushButton(parent=self.layoutWidget)
self.percent_click.setObjectName("percent_click")
self.gridLayout.addWidget(self.percent_click, 4, 0, 1, 1)
self.number0 = QtWidgets.QPushButton(parent=self.layoutWidget)
self.number0.setObjectName("number0")
self.gridLayout.addWidget(self.number0, 4, 1, 1, 1)
self.click_point = QtWidgets.QPushButton(parent=self.layoutWidget)
self.click_point.setObjectName("click_point")
self.gridLayout.addWidget(self.click_point, 4, 2, 1, 1)
self.click_equal = QtWidgets.QPushButton(parent=self.layoutWidget)
self.click_equal.setMinimumSize(QtCore.QSize(0, 25))
self.click_equal.setObjectName("click_equal")
self.gridLayout.addWidget(self.click_equal, 4, 3, 1, 1)
self.gridLayout_2.addLayout(self.gridLayout, 1, 0, 1, 1)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "计算器"))
self.RE.setText(_translate("MainWindow", "RE"))
self.M.setText(_translate("MainWindow", "M"))
self.MR.setText(_translate("MainWindow", "MR"))
self.click_divison.setText(_translate("MainWindow", "÷"))
self.number7.setText(_translate("MainWindow", "7"))
self.number8.setText(_translate("MainWindow", "8"))
self.number9.setText(_translate("MainWindow", "9"))
self.click_multiplication.setText(_translate("MainWindow", "×"))
self.number4.setText(_translate("MainWindow", "4"))
self.number5.setText(_translate("MainWindow", "5"))
self.number6.setText(_translate("MainWindow", "6"))
self.click_subtraction.setText(_translate("MainWindow", "-"))
self.number1.setText(_translate("MainWindow", "1"))
self.number2.setText(_translate("MainWindow", "2"))
self.number3.setText(_translate("MainWindow", "3"))
self.click_addition.setText(_translate("MainWindow", "+"))
self.percent_click.setText(_translate("MainWindow", "%"))
self.number0.setText(_translate("MainWindow", "0"))
self.click_point.setText(_translate("MainWindow", "."))
self.click_equal.setText(_translate("MainWindow", "="))