文章目录(Table of Contents)
简介
我们在上一篇文章中对 PyQt 的基础知识进行了介绍,例如我们了解了 PyQt 中的 Widgets 个 Layout Managers,也了解了关于 Application,Event loop 的相关概念,最后讲了 Signal and Slots。在这一篇中,我们会将学到的内容进行使用,制作来一个简单的计算器。
这一篇文章主要参考自,Creating a Calculator With Python and PyQt
使用 PyQt 制作计算器
我们会使用 Model-View-Controller(MVC)的设计模式来制作一个计算器。这种设计模式会有三层代码,每一层有不同的功能:
The Model,这部分是模型的核心功能模块,包含主要功能的代码。对于计算器来说,这一部分就是负责计算的模块。
The View,这一部分负责 GUI 界面。会包含所有的组件,与用户的交互等。对于计算器来说,就是在使用过程中我们看到的窗口界面。
The Controller,这一部分是用来连接上面的 Model 和 View 这两个部分。用户的事件会发送给 controller,然后控制 Model 进行工作。Model 的结果会给 controller,controller 在控制 view 的显示。对于计算器来说,这一部分就是会接受用户在界面的操作,让模型执行相应的计算,更新 GUI。
下面我们会依次对上面的三个模块的代码进行介绍。我们将这三个模块分别放在三个文件中,总的文件结构如下所示:
其中:
- calcModel.py,包含 Model 的模块,主要控制运算。
- calcView.py,包含计算器的界面。
- calcController.py,用来连接 Model 和 View 这两个模块。
- pycalc.py,主函数
Model 模块
Model 模块主要是负责程序的主要逻辑部分的。在我们这个「计算器」的例子中,我们就是要把输入的式子进行计算,得到运算结果。我们可以使用 eval 来完成相关的操作。如下所示,eval 可以对给定的字符串进行运算:
于是,我们构建一个函数,输入是字符串,输出是对应的计算结果,如下所示(写入 calcModel.py
文件中):
- ERROR_MSG = 'ERROR' # 遇到除数是 0 的时候的报错
- def evaluateExpression(expression):
- """将 str 类型的表达式使用 eval 来计算得到结果, 例如 eval('1+1')=2
- Args:
- expression (str): 计算表达式, 例如 '1+2'
- """
- try:
- result = str(eval(expression))
- except Exception:
- result = ERROR_MSG
- return result
View 模块
在 View 模块中,我们创建整个应用的主界面。整个界面中有两个大的部件,分别是上面用来显示数字的组件,和按钮的组件,所以整体使用 QVBoxLayout
。在按钮部分使用 QGridLayout
。我们在这里还定义了一些方法,例如刷新显示。
下面是完整的代码,十分建议看着注释,自己亲自敲一遍。
- from PyQt5.QtCore import Qt
- from PyQt5.QtWidgets import QMainWindow, QWidget, QGridLayout, QLineEdit, QPushButton, QVBoxLayout
- class PyCalcUi(QMainWindow):
- """计算器的主界面
- """
- def __init__(self) -> None:
- super().__init__()
- self.setWindowTitle('PyCalc')
- self.setFixedSize(235, 235) # 不可以缩放
- # Main Windows 必须要 Central Widget
- self.generalLayout = QVBoxLayout()
- self._centralWidget = QWidget() # 这里可以放不同的 sub Widgets
- self.setCentralWidget(self._centralWidget)
- self._centralWidget.setLayout(self.generalLayout)
- # 创建 按钮 和 显示
- self._createDisplay()
- self._createButtons()
- def _createDisplay(self):
- """用来显示计算器顶部的数字
- """
- self.display = QLineEdit()
- # 设置 display 的属性
- self.display.setFixedHeight(35)
- self.display.setAlignment(Qt.AlignRight) # 右对齐
- self.display.setReadOnly(True)
- # 设置布局
- self.generalLayout.addWidget(self.display)
- def _createButtons(self):
- """显示计算器的按键
- """
- self.buttons = {}
- buttonLayout = QGridLayout() # 按钮的布局
- # 记录按钮的文字和坐标
- buttons = {'7': (0, 0),
- '8': (0, 1),
- '9': (0, 2),
- '/': (0, 3),
- 'C': (0, 4),
- '4': (1, 0),
- '5': (1, 1),
- '6': (1, 2),
- '*': (1, 3),
- '(': (1, 4),
- '1': (2, 0),
- '2': (2, 1),
- '3': (2, 2),
- '-': (2, 3),
- ')': (2, 4),
- '0': (3, 0),
- '00': (3, 1),
- '.': (3, 2),
- '+': (3, 3),
- '=': (3, 4),
- }
- for binText, pos in buttons.items():
- self.buttons[binText] = QPushButton(binText)
- self.buttons[binText].setFixedSize(40, 40)
- buttonLayout.addWidget(self.buttons[binText], pos[0], pos[1])
- # 将 button layout 添加到 general layout
- self.generalLayout.addLayout(buttonLayout)
- def setDisplayText(self, text):
- """设置 计算器显示器显示的内容
- """
- self.display.setText(text)
- self.display.setFocus() # 将光标聚焦在显示屏上
- def getdisplayText(self):
- """返回显示器中现在的内容
- """
- return self.display.text()
- def clearDisplay(self):
- """将显示器的内容设置为空
- """
- self.setDisplayText('')
在有了这一个部分之后,我们就有了整体的效果,如下所示:
Controller 模块
在这一部分中我们完成 Model
与 View
之间的连接。
- 定义
_buildExpression
方法,是每个按键按下后,需要刷新屏幕的显示,例如按下数字 5 那么屏幕上就需要显示数字 5. - 定义
_calculateResult
方法,按下等号后,对显示的式子进行计算。 - 我们对 View 中每一个组件(这里是计算器中每一个按键)都进行绑定,这里的
_connectSignals
方法,数字键和等号这两个键绑定的是不同的。
- from functools import partial
- class PyCalcCtrl(object):
- def __init__(self, model, view) -> None:
- super().__init__()
- self._view = view # UI 界面对应的类
- self._model = model # 用于计算
- # connect signal and slots
- self._connectSignals() # 初始化
- def _calculateResult(self):
- """调用 model, 来计算结果并更新 DisplayText
- """
- result = self._model(expression=self._view.getdisplayText()) # 得到计算结果
- self._view.setDisplayText(result) # 将计算结果用于显示
- def _buildExpression(self, sub_exp):
- """修改计算器显示界面
- Args:
- sub_exp (str): 输入的运算符
- """
- expression = self._view.getdisplayText() + sub_exp
- self._view.setDisplayText(expression)
- def _connectSignals(self):
- """对每一个按键进行功能函数的绑定
- """
- for btnText, btn in self._view.buttons.items():
- if btnText not in ('=', 'C'):
- btn.clicked.connect(partial(self._buildExpression, sub_exp=btnText))
- self._view.buttons['C'].clicked.connect(self._view.clearDisplay)
- self._view.buttons['='].clicked.connect(self._calculateResult)
主函数
再有了上面 Model,View 和 Controller 三个模块之后,我们需要将三者组合在一起。完整的代码如下所示:
- import sys
- from PyQt5.QtWidgets import QApplication
- from calcView import PyCalcUi
- from calcController import PyCalcCtrl
- from calcModel import evaluateExpression
- def main():
- # 创建一个 Application
- pycalc = QApplication(sys.argv)
- # 展示界面
- view = PyCalcUi()
- view.show()
- # 创建 model 和 controller
- model = evaluateExpression
- PyCalcCtrl(view=view, model=model)
- # 执行 event loop
- sys.exit(pycalc.exec_())
- if __name__ == '__main__':
- main()
主要有这样的几个步骤:
- 创建一个 Application
- 创建 View 和 Model
- 创建 Controller,将 Model 与 View 连接在一起
最后我们运行上面的代码,就完成了一个「计算器」的制作。最终的效果如下所示:
- 微信公众号
- 关注微信公众号
- QQ群
- 我们的QQ群号
评论