使用 PyQt 快速搭建带有 GUI 的应用(2)–制作计算器

  • A+
所属分类:Python库介绍
摘要在本文中,我们会用上一章学习到的 PyQt 5 的基础知识来制作一个计算器,并了解什么是 Model-View-Controller(MVC)的设计模式。

简介

我们在上一篇文章中对 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

下面我们会依次对上面的三个模块的代码进行介绍。我们将这三个模块分别放在三个文件中,总的文件结构如下所示:

使用 PyQt 快速搭建带有 GUI 的应用(2)--制作计算器

其中:

  • calcModel.py,包含 Model 的模块,主要控制运算。
  • calcView.py,包含计算器的界面。
  • calcController.py,用来连接 Model 和 View 这两个模块。
  • pycalc.py,主函数

 

Model 模块

Model 模块主要是负责程序的主要逻辑部分的。在我们这个「计算器」的例子中,我们就是要把输入的式子进行计算,得到运算结果。我们可以使用 eval 来完成相关的操作。如下所示,eval 可以对给定的字符串进行运算:

使用 PyQt 快速搭建带有 GUI 的应用(2)--制作计算器

于是,我们构建一个函数,输入是字符串,输出是对应的计算结果,如下所示(写入 calcModel.py 文件中):

  1. ERROR_MSG = 'ERROR' # 遇到除数是 0 的时候的报错
  2. def evaluateExpression(expression):
  3.     """将 str 类型的表达式使用 eval 来计算得到结果, 例如 eval('1+1')=2
  4.     Args:
  5.         expression (str): 计算表达式, 例如 '1+2'
  6.     """
  7.     try:
  8.         result = str(eval(expression))
  9.     except Exception:
  10.         result = ERROR_MSG
  11.     return result

 

View 模块

在 View 模块中,我们创建整个应用的主界面。整个界面中有两个大的部件,分别是上面用来显示数字的组件,和按钮的组件,所以整体使用 QVBoxLayout。在按钮部分使用 QGridLayout。我们在这里还定义了一些方法,例如刷新显示。

下面是完整的代码,十分建议看着注释,自己亲自敲一遍

  1. from PyQt5.QtCore import Qt
  2. from PyQt5.QtWidgets import QMainWindow, QWidget, QGridLayout, QLineEdit, QPushButton, QVBoxLayout
  3. class PyCalcUi(QMainWindow):
  4.     """计算器的主界面
  5.     """
  6.     def __init__(self) -> None:
  7.         super().__init__()
  8.         self.setWindowTitle('PyCalc')
  9.         self.setFixedSize(235, 235) # 不可以缩放
  10.         # Main Windows 必须要 Central Widget
  11.         self.generalLayout = QVBoxLayout()
  12.         self._centralWidget = QWidget() # 这里可以放不同的 sub Widgets
  13.         self.setCentralWidget(self._centralWidget)
  14.         self._centralWidget.setLayout(self.generalLayout)
  15.         # 创建 按钮 和 显示
  16.         self._createDisplay()
  17.         self._createButtons()
  18.     def _createDisplay(self):
  19.         """用来显示计算器顶部的数字
  20.         """
  21.         self.display = QLineEdit()
  22.         # 设置 display 的属性
  23.         self.display.setFixedHeight(35)
  24.         self.display.setAlignment(Qt.AlignRight) # 右对齐
  25.         self.display.setReadOnly(True)
  26.         # 设置布局
  27.         self.generalLayout.addWidget(self.display)
  28.     def _createButtons(self):
  29.         """显示计算器的按键
  30.         """
  31.         self.buttons = {}
  32.         buttonLayout = QGridLayout() # 按钮的布局
  33.         # 记录按钮的文字和坐标
  34.         buttons = {'7': (0, 0),
  35.                    '8': (0, 1),
  36.                    '9': (0, 2),
  37.                    '/': (0, 3),
  38.                    'C': (0, 4),
  39.                    '4': (1, 0),
  40.                    '5': (1, 1),
  41.                    '6': (1, 2),
  42.                    '*': (1, 3),
  43.                    '(': (1, 4),
  44.                    '1': (2, 0),
  45.                    '2': (2, 1),
  46.                    '3': (2, 2),
  47.                    '-': (2, 3),
  48.                    ')': (2, 4),
  49.                    '0': (3, 0),
  50.                    '00': (3, 1),
  51.                    '.': (3, 2),
  52.                    '+': (3, 3),
  53.                    '=': (3, 4),
  54.                   }
  55.         for binText, pos in buttons.items():
  56.             self.buttons[binText] = QPushButton(binText)
  57.             self.buttons[binText].setFixedSize(40, 40)
  58.             buttonLayout.addWidget(self.buttons[binText], pos[0], pos[1])
  59.         # 将 button layout 添加到 general layout
  60.         self.generalLayout.addLayout(buttonLayout)
  61.     def setDisplayText(self, text):
  62.         """设置 计算器显示器显示的内容
  63.         """
  64.         self.display.setText(text)
  65.         self.display.setFocus() # 将光标聚焦在显示屏上
  66.     def getdisplayText(self):
  67.         """返回显示器中现在的内容
  68.         """
  69.         return self.display.text()
  70.     def clearDisplay(self):
  71.         """将显示器的内容设置为空
  72.         """
  73.         self.setDisplayText('')

在有了这一个部分之后,我们就有了整体的效果,如下所示:

使用 PyQt 快速搭建带有 GUI 的应用(2)--制作计算器

Controller 模块

在这一部分中我们完成 ModelView 之间的连接。

  • 定义 _buildExpression 方法,是每个按键按下后,需要刷新屏幕的显示,例如按下数字 5 那么屏幕上就需要显示数字 5.
  • 定义 _calculateResult 方法,按下等号后,对显示的式子进行计算。
  • 我们对 View 中每一个组件(这里是计算器中每一个按键)都进行绑定,这里的 _connectSignals 方法,数字键和等号这两个键绑定的是不同的。
  1. from functools import partial
  2. class PyCalcCtrl(object):
  3.     def __init__(self, model, view) -> None:
  4.         super().__init__()
  5.         self._view = view # UI 界面对应的类
  6.         self._model = model # 用于计算
  7.         # connect signal and slots
  8.         self._connectSignals() # 初始化
  9.     def _calculateResult(self):
  10.         """调用 model, 来计算结果并更新 DisplayText
  11.         """
  12.         result = self._model(expression=self._view.getdisplayText()) # 得到计算结果
  13.         self._view.setDisplayText(result) # 将计算结果用于显示
  14.     def _buildExpression(self, sub_exp):
  15.         """修改计算器显示界面
  16.         Args:
  17.             sub_exp (str): 输入的运算符
  18.         """
  19.         expression = self._view.getdisplayText() + sub_exp
  20.         self._view.setDisplayText(expression)
  21.     def _connectSignals(self):
  22.         """对每一个按键进行功能函数的绑定
  23.         """
  24.         for btnText, btn in self._view.buttons.items():
  25.             if btnText not in ('=', 'C'):
  26.                 btn.clicked.connect(partial(self._buildExpression, sub_exp=btnText))
  27.         self._view.buttons['C'].clicked.connect(self._view.clearDisplay)
  28.         self._view.buttons['='].clicked.connect(self._calculateResult)

 

主函数

再有了上面 Model,View 和 Controller 三个模块之后,我们需要将三者组合在一起。完整的代码如下所示:

  1. import sys
  2. from PyQt5.QtWidgets import QApplication
  3. from calcView import PyCalcUi
  4. from calcController import PyCalcCtrl
  5. from calcModel import evaluateExpression
  6. def main():
  7.     # 创建一个 Application
  8.     pycalc = QApplication(sys.argv)
  9.     # 展示界面
  10.     view = PyCalcUi()
  11.     view.show()
  12.     # 创建 model 和 controller
  13.     model = evaluateExpression
  14.     PyCalcCtrl(view=view, model=model)
  15.     # 执行 event loop
  16.     sys.exit(pycalc.exec_())
  17. if __name__ == '__main__':
  18.     main()

主要有这样的几个步骤:

  • 创建一个 Application
  • 创建 View 和 Model
  • 创建 Controller,将 Model 与 View 连接在一起

最后我们运行上面的代码,就完成了一个「计算器」的制作。最终的效果如下所示:

使用 PyQt 快速搭建带有 GUI 的应用(2)--制作计算器
  • 微信公众号
  • 关注微信公众号
  • weinxin
  • QQ群
  • 我们的QQ群号
  • weinxin

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: