【Python项目实战】构建GUI桌面计算器(下)


前情回顾

上两期课程,我们为大家详细介绍了PyQt中基础的组件信息,同时也教大家如何处理相关的一些事件循环和信号槽位机制,还未阅读的小伙伴可以点击文末链接事先预览下。

作为本系列课程的最后一期文章,我们将带大家从头构建属于你的第一款GUI应用程序,一款简易的桌面计算器。

Calculator App

在本节中,我们将遵循模型-视图-控制器(MVC)的设计模式开发一个计算器GUI应用程序。这个模式有三层代码,每一层都有不同的角色:

  1. 模型负责应用程序的业务逻辑。它包含核心功能和数据。在计算器应用程序中,模型将处理输入值和计算。

  2. 视图实现了应用程序的GUI。它承载终端用户与应用程序交互所需的所有小组件。视图还接收用户的操作和事件。在这里,视图对应屏幕上的计算器窗口。

  3. 控制器连接模型和视图以使应用程序工作。用户的事件或请求被发送到控制器,控制器使模型工作。当模型以正确的格式交付所请求的结果或数据时,控制器将其转发给视图。在计算器应用程序中,控制器将从GUI接收目标数学表达式,要求模型执行计算,并使用结果更新GUI


下面,让我们简单描述下,构建GUI计算器应用程序的详细步骤:

  1. 用户在视图上执行操作或请求(事件);
  2. 视图通知控制器用户的操作;
  3. 控制器获取用户的请求并查询模型以获得响应;
  4. 模型处理控制器的查询,执行所需的计算,并返回结果;
  5. 控制器接收模型的响应并相应地更新视图。
  6. 用户最终在视图上看到请求的结果。

Creating the Skeleton

首先,让我们在一个名为pycalc.py的文件中为应用程序实现一个最小的框架。现在,打开你最喜欢的代码编辑器或IDE并键入以下代码:

# pycalc.py

"""PyCalc is a simple calculator built with Python and PyQt."""

import sys

from PyQt6.QtWidgets import QApplication, QMainWindow, QWidget

WINDOW_SIZE = 235

class PyCalcWindow(QMainWindow):
    """PyCalc's main window (GUI or view)."""

    def __init__(self):
        super().__init__()
        self.setWindowTitle("PyCalc")
        self.setFixedSize(WINDOW_SIZE, WINDOW_SIZE)
        centralWidget = QWidget(self)
        self.setCentralWidget(centralWidget)

def main():
    """PyCalc's main function."""
    pycalcApp = QApplication([])
    pycalcWindow = PyCalcWindow()
    pycalcWindow.show()
    sys.exit(pycalcApp.exec())

if __name__ == "__main__":
    main()

这个脚本实现了运行基本GUI应用程序所需的所有代码,我们将使用这个框架来构建计算器应用程序。下面是这段代码的工作原理:

第5行导入sys。这个模块提供了exit()函数,可用于终止应用程序。

第7行从PyQt6.QtWidgets中导入所需的类。

第9行创建一个Python常量,为计算器应用程序保存一个固定的窗口大小(以像素为单位)。

第11行创建PyCalcWindow类来提供应用程序的GUI。注意,这个类继承自QMainWindow

第14行定义了类初始化式。

第15行调用超类的.__init__()以实现初始化目的。

第16行将窗口标题设置为PyCalc

第17行使用. setfixedsize()给窗口一个固定的大小。这确保了用户在应用程序执行期间不能调整窗口的大小。

第18行和第19行创建一个QWidget对象,并将其设置为窗口的中心小部件。这个对象将是计算器应用程序中所有必需的GUI组件的父组件。

第21行定义了计算器的主要函数。这个函数提供应用程序的入口点。

23行创建一个名为pycalcAppQApplication对象。

第24行创建了应用程序窗口pycalcWindow的一个实例。

第25行通过在窗口对象上调用.show()来显示GUI

第26行使用.exec()运行应用程序的事件循环。

最后,第29行调用main()来执行计算器应用程序。当运行上述脚本时,屏幕上会出现以下窗口:

【Python项目实战】构建GUI桌面计算器(下)
calculator-skeleton

Completing the App’s View

在之前的基础上,我们可以通过添加显示数学运算的显示器和表示数字和基本数学运算符的按钮键盘来完成这个GUI。因此,我们还需要添加表示其他所需符号和操作的按钮,比如清除显示。

首先,导入一些必要的库:

# pycalc.py

import sys

from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import (
    QApplication,
    QGridLayout,
    QLineEdit,
    QMainWindow,
    QPushButton,
    QVBoxLayout,
    QWidget,
)

# ...

你将使用QVBoxLayout布局管理器来进行计算器的全局布局。要排列按钮,我们创建了一个QGridLayout对象。QLineEdit类将作为计算器的显示,而QPushButton将提供所需的按钮。

作为这些准备工作后,先实例化这些对象:

# pycalc.py
# ...

class PyCalcWindow(QMainWindow):
    """PyCalc's main window (GUI or view)."""

    def __init__(self):
        super().__init__()
        self.setWindowTitle("PyCalc")
        self.setFixedSize(WINDOW_SIZE, WINDOW_SIZE)
        self.generalLayout = QVBoxLayout()
        centralWidget = QWidget(self)
        centralWidget.setLayout(self.generalLayout)
        self.setCentralWidget(centralWidget)
        self._createDisplay()
        self._createButtons()

# ...

好了,到这里我们添加完突出显示的代码行。你将使用.generallayout作为应用程序的总体布局。在这个布局中,我们把显示器放在顶部,键盘按钮放在底部的网格布局中。

._createdisplay()._createbuttons()的调用此时还不能工作,因为我们还没有实现这些方法。要修复这个问题,首先编写`._createdisplay()“。

回到代码编辑器,继续更新pycalc.py脚本:

# pycalc.py
# ...

WINDOW_SIZE = 235
DISPLAY_HEIGHT = 35

class PyCalcWindow(QMainWindow):
    # ...

    def _createDisplay(self):
        self.display = QLineEdit()
        self.display.setFixedHeight(DISPLAY_HEIGHT)
        self.display.setAlignment(Qt.AlignmentFlag.AlignRight)
        self.display.setReadOnly(True)
        self.generalLayout.addWidget(self.display)

# ...

在此代码片段中,首先定义一个新常量来保存以像素为单位的显示高度。然后在PyCalcWindow中定义._createdisplay()

要创建计算器的显示,可以使用QLineEdit小部件。然后使用DISPLAY_HEIGHT常量为显示设置35个像素的固定高度。显示的文本将左对齐。最后,显示将是只读的,以防止用户直接编辑。最后一行代码将显示添加到计算器的总体布局中。

接下来,实现._createbuttons()方法,为计算器键盘创建所需的按钮。这些按钮将位于网格布局中,因此您需要一种方法在网格中表示它们的坐标。每个坐标对将由一行和一列组成。要表示坐标对,你将使用列表的列表,每个嵌套列表将表示一行。

现在继续,用以下代码更新pycalc.py文件:

# pycalc.py
# ...

WINDOW_SIZE = 235
DISPLAY_HEIGHT = 35
BUTTON_SIZE = 40

# ...

在这段代码中,我们定义了一个名为BUTTON_SIZE的新常量,用于提供计算器按钮的大小。在这个特定的例子中,所有的按钮都是一个40像素的正方形。

通过这个初始设置,你可以编写._createbuttons()方法,并使用嵌套列表来保存键或按钮及其在计算器键盘上的位置。QGridLayout允许我们设置计算器窗口上的按钮:

# pycalc.py
# ...

class PyCalcWindow(QMainWindow):
    # ...

    def _createButtons(self):
        self.buttonMap = {}
        buttonsLayout = QGridLayout()
        keyBoard = [
            ["7""8""9""/""C"],
            ["4""5""6""*""("],
            ["1""2""3""-"")"],
            ["0""00"".""+""="],
        ]

        for row, keys in enumerate(keyBoard):
            for col, key in enumerate(keys):
                self.buttonMap[key] = QPushButton(key)
                self.buttonMap[key].setFixedSize(BUTTON_SIZE, BUTTON_SIZE)
                buttonsLayout.addWidget(self.buttonMap[key], row, col)

        self.generalLayout.addLayout(buttonsLayout)

# ...

首先创建空字典self.buttonMap,用于保存计算器按钮。然后,创建一个嵌套列表来存储键标签。每一行或嵌套列表将表示网格布局中的一行,而每个键标签的索引将表示布局中相应的列。

接下来定义两个for循环。外部循环遍历行,内部循环遍历列。在内部循环中,创建按钮并将它们添加到两个self.buttonMapbuttonsLayout变量中。每个按钮都有一个40x40像素的固定大小,这可以通过.setfixedsize()BUTTON_SIZE常量来设置。

最后,通过调用.generalayout对象上的.addlayout()方法将网格布局嵌入到计算器的通用布局中。

注意:当涉及到组件大小时,你很少会在PyQt文档中找到度量单位。测量单位被假定为像素,除非使用QPrinter类

现在,计算器的GUI将优雅地显示显示和按钮。但是,你无法更新显示的信息。你可以通过在PyCalcWindow中添加一些额外的方法来解决这个问题:

【Python项目实战】构建GUI桌面计算器(下)
methods

这些方法将提供GUI的公共接口,并为Python计算器应用程序完成视图类。下面让我们来完成最后的代码部分:

# pycalc.py
# ...

class PyCalcWindow(QMainWindow):
    # ...

    def setDisplayText(self, text):
        """Set the display's text."""
        self.display.setText(text)
        self.display.setFocus()

    def displayText(self):
        """Get the display's text."""
        return self.display.text()

    def clearDisplay(self):
        """Clear the display."""
        self.setDisplayText("")

# ...

以下是每种方法的具体功能:

  • .setdisplaytext()使用.settext()来设置和更新显示的文本。它还使用.setfocus()将光标的焦点设置在显示器上。

  • .displaytext()是一个返回显示当前文本的getter方法。当用户点击计算器键盘上的等号(=)时,应用程序将使用.displaytext()的返回值作为要求值的数学表达式。

  • .cleardisplay()将显示的文本设置为空字符串(“”),以便用户可以引入一个新的数学表达式。每当用户按下计算器面板上的C按钮时,就会触发此方法。

现在,我们的计算器终于有个雏形了!当你运行这个应用程序时,你会看到一个如下所示的窗口:

【Python项目实战】构建GUI桌面计算器(下)
calculator-gui

到这里,大家已经完成了计算器的GUI,它看起来非常流畅!然而,如果此时你尝试进行一些计算,那么计算器将不会像预期的那样响应。这是因为我们还没有实现模型和控制器组件。在下一节中,我们将编写计算器的模型。

Implementing the Calculator’s Model

MVC模式中,模型是负责业务逻辑的代码层。在计算器应用程序中,业务逻辑都是关于基本的数学计算。因此,我们的模型负责计算用户在计算器的GUI中输入的数学表达式。此外,计算器还需要能够处理异常错误。为此,我们首先定义以下全局常数:

# pycalc.py
# ...

ERROR_MSG = "ERROR"
WINDOW_SIZE = 235
# ...

如果用户引入了无效的数学表达式,则ERROR_MSG常量是用户将在计算器显示器上看到的消息。有了以上的更改,你就可以编写应用程序的模型了,在这个例子中,它将是一个单独的函数:

# pycalc.py
# ...

class PyCalcWindow(QMainWindow):
    # ...

def evaluateExpression(expression):
    """Evaluate an expression (Model)."""
    try:
        result = str(eval(expression, {}, {}))
    except Exception:
        result = ERROR_MSG
    return result

# ...

evaluateExpression()中,使用eval()对作为字符串的数学表达式求值。如果计算成功,则返回结果。否则,将返回预定义的错误消息。注意,这个函数并不完美。它有几个重要的问题:

  • try…except块不会捕获特定的异常,因此它使用的是Python中不鼓励使用的实践。
  • 该函数使用eval(),这可能会导致一些严重的安全问题。

你可以自由地修改该函数,使其更加可靠和安全。在本教程中,你将按原样使用该函数,将重点放在实现GUI上。

Creating the Controller Class for Your Calculator

下面让我们继续编写计算器的控制器类。这个类将视图连接到刚刚编写的模型,我们将使用控制器类使计算器执行响应用户事件的操作,其主要包含以下几个功能:

  • 访问GUI的公共接口。
  • 处理数学表达式的创建。
  • 连接所有按钮的点击信号与适当的插槽。

下面编写一个新的PyCalc类,继续沿用前面的代码:

# pytcalc.py

import sys
from functools import partial
# ...

def evaluateExpression(expression):
    # ...

class PyCalc:
    """PyCalc's controller class."""

    def __init__(self, model, view):
        self._evaluate = model
        self._view = view
        self._connectSignalsAndSlots()

    def _calculateResult(self):
        result = self._evaluate(expression=self._view.displayText())
        self._view.setDisplayText(result)

    def _buildExpression(self, subExpression):
        if self._view.displayText() == ERROR_MSG:
            self._view.clearDisplay()
        expression = self._view.displayText() + subExpression
        self._view.setDisplayText(expression)

    def _connectSignalsAndSlots(self):
        for keySymbol, button in self._view.buttonMap.items():
            if keySymbol not in {"=""C"}:
                button.clicked.connect(
                    partial(self._buildExpression, keySymbol)
                )
        self._view.buttonMap["="].clicked.connect(self._calculateResult)
        self._view.display.returnPressed.connect(self._calculateResult)
        self._view.buttonMap["C"].clicked.connect(self._view.clearDisplay)

# ...

pycalc.py的开头,我们从functools导入partial()函数,此函数将信号与需要接受额外参数的方法连接起来。

PyCalc函数体里面,定义类初始化式,它接受两个参数,即应用程序的模型及其视图。然后将这些参数存储在适当的实例属性中。最后,调用._connectsignalsandslots()来建立所有必需的信号和插槽连接。

._calculateresult()中,我们使用._evaluate()对用户刚刚输入计算器显示的数学表达式求值。然后在计算器视图上调用.setdisplaytext(),用计算结果更新显示文本。顾名思义,._buildexpression()负责构建目标数学表达式。为此,该方法将初始显示值与用户在计算器键盘上输入的每个新值连接起来。

最后,._connectsignalsandslots()法将所有按钮的clicked号与控制器类中的适当slots方法连接起来。好了,现在控制器类已经准备好了,让我们在主函数调用它:

# pytcalc.py
# ...

def main():
    """PyCalc's main function."""
    pycalcApp = QApplication([])
    pycalcWindow = PyCalcWindow()
    pycalcWindow.show()
    PyCalc(model=evaluateExpression, view=pycalcWindow)
    sys.exit(pycalcApp.exec())

这段代码创建了PyCalc的一个新实例。PyCalc类构造函数的模型参数保存了对evaluateExpression()函数的引用,而视图参数保存了对pycalcWindow对象的引用,该对象提供了应用程序的GUI。现在PyQt计算器应用程序可以运行了。

Running the Calculator

现在我们已经完成了用PythonPyQt编写计算器应用程序,是时候进行现场测试了!如果你从你的命令行运行应用程序,那么你会得到这样的结果:

【Python项目实战】构建GUI桌面计算器(下)
pycalc-final

大家可以尝试输入一段数学表达式,不出意外的话应该能得到期望的结果。最后,让我们为这个应用增添一些额外的功能。

Additional Tools

PyQt6提供了一组有用的附加工具,可以帮助你构建可靠的、现代的、功能齐全的GUI应用程序。与PyQt相关的一些最显著的工具包括Qt Designerinternationalization kit

Qt Designer允许用户使用拖放的功能进行界面设计和构建图形用户界面。你可以使用这个工具通过使用屏幕上的表单和拖放机制来设计小部件、对话框和主窗口。下面的动画展示了Qt Designer的一些功能:


【Python项目实战】构建GUI桌面计算器(下)


qt-designer

Qt Designer使用XML .ui文件来存储GUI设计。PyQt包含一个名为uic的模块来帮助处理.ui文件。你还可以使用名为pyuic6的命令行工具将.ui文件内容转换为Python代码。

注意:要深入了解Qt Designer并更好地理解如何使用该工具创建图形用户界面,请查看<Qt Designer and Python: Build Your GUI Applications Faster>章节。

此外,PyQt6还提供了一套全面的工具,用于将应用程序国际化为本地语言。pylupdate6命令行工具创建和更新翻译(.ts)文件,该文件可以包含接口字符串的翻译。如果你更喜欢GUI工具,那么可以使用Qt Linguist创建和更新.ts文件,其中包含接口字符串的翻译。

Conclusion

在本教程中,我们学习了如何使用PyQt,它是基于Python开发GUI应用程序最流行和最可靠的库之一。在本系列教程中,我们学习了以下知识点:

  • 使用Python和PyQt构建图形用户界面
  • 将用户的事件与应用程序的逻辑连接起来
  • 使用适当的项目布局组织PyQt应用程序
  • 使用PyQt创建一个真实的GUI应用程序

现在,大家可以尝试使用PythonPyQt知识为自己的桌面GUI应用程序注入活力,学有余力的同学也可以尝试开发其他项目。下面将为大家列举一些常用的学习资料。

[1] PyQt6’s documentation
[2] PyQt5’s documentation
[3] Qt v6’s documentation
[4] The Rapid GUI Programming with Python and Qt book
[5] The Qt Designer manual
[6] Qt for Python’s documentation



往期回顾
DIY
Python()
Python()
Python()
Python()
Python()
Python()



原文始发于微信公众号(Pytrick):【Python项目实战】构建GUI桌面计算器(下)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/56343.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!