将 C++ 应用程序迁移到 Python#

Qt for Python 允许您在 Python 应用程序中使用 Qt API。那么,将现有 C++ 应用程序迁移过来需要什么?尝试将 Qt C++ 应用程序迁移到 Python 以了解这一点。

在开始之前,请确保 Qt for Python 所需的所有先决条件都已满足。有关更多信息,请参阅入门指南。此外,熟悉 C++ 中的 Qt 和 Python 中的基本差异。

基本差异#

本节突出显示了 C++ 和 Python 之间的基本差异以及 Qt 在这两个上下文中的差异。

C++ 与 Python#

  • 为了提高代码重用性,C++ 和 Python 都提供了将一个文件中的代码用于另一个文件提供的设施的方法。在 C++ 中,这通过使用 #include 指令来访问重用代码的 API 定义来实现。Python 的等效方法是 import 语句。

  • C++ 类的构造函数与其类名相同,并在执行之前自动调用任何基类(以预定义的顺序)的构造函数。在 Python 中,__init__() 方法是类的构造函数,它可以根据任何顺序显式调用基类构造函数。

  • C++使用关键字this隐式引用当前对象。在Python中,需要在每个实例方法的第一个参数明确定义当前对象;通常命名为self

  • 更重要的是,忘记大括号{}和分号;

  • 只有当变量需要全局作用域时,才使用global关键字来定义变量。

var = None
def func(key, value = None):
  """Does stuff with a key and an optional value.

  If value is omitted or None, the value from func()'s
  last call is reused.
  """
  global var
  if value is None:
      if var is None:
          raise ValueError("Must pass a value on first call", key, value)
      value = var
  else:
      var = value
  doStuff(key, value)

在这个例子中,func()会将var视为不使用global语句的局部名称。这会导致在访问var时出现NameError错误。有关更多信息,请参阅Python参考文档

提示

Python是一个解释语言,最简单的方法是在解释器中尝试你的想法。你可以在解释器中调用任何Python内置函数或关键字的help()函数。例如,调用help(import)应该提供关于import语句的文档。

最后但同样重要的是,尝试一些示例来熟悉Python编码风格,并遵循PEP8 - 风格指南中概述的指南。

import sys

from PySide6.QtWidgets import QApplication, QLabel

app = QApplication(sys.argv)
label = QLabel("Hello World")
label.show()
sys.exit(app.exec())

注意

Qt提供了一些类,这些类根据应用程序是否为仅控制台(QCoreApplication)、带QtWidgets的GUI(QApplication)或无QtWidgets的GUI(QGuiApplication)来管理特定的应用程序需求。这些类会加载必要的插件,例如应用程序需要的GUI库。在本例中,由于应用程序具有带QtWidgets的GUI,所以首先初始化QApplication。

在C++和Python中的Qt#

Qt的行为与它是在C++还是Python应用程序中使用的无关。考虑到C++和Python使用不同的语言语义,两者之间必然存在一些差异。以下是一些重要差异,你必须了解:

  • Qt属性:在C++中使用Q_PROPERTY宏来添加带有获取器和设置器函数的公共成员变量。Python的替代方法是,在获取器和设置器函数定义之前使用@property装饰器。

  • Qt信号和槽:Qt提供了一种独特的回调机制,其中信号被发射以通知事件的触发,从而使连接到该信号的槽可以对其做出响应。在C++中,类定义必须在public Q_SLOTS:和信号在QSignals:访问说明符下定义槽和信号。您可以使用QObject::connect()函数的多个变体之一连接这两个对象。Python的相关功能是在函数定义之前使用@Slot装饰器。这是将槽与QtMetaObject注册所必需的。

  • QString, QVariant和其他类型

    • Qt for Python不提供对QString和QVariant的访问权限。您必须使用Python的本地类型。

    • QChar和QStringRef表示为Python字符串,QStringList则转换为字符串列表。

    • QDate, QDateTime, QTime 和 QUrl 的 __hash__() 方法返回一个字符串表示,以便相同的日期(和相同的日期/时间、时间或 URL)具有相同的散列值。

    • QTextStream 的 bin()、hex() 和 oct() 函数分别更名为 bin_()、hex_() 和 oct_()。这应可避免与 Python 内置函数命名冲突。

  • QByteArray:QByteArray 作为一个不带编码的字节数组列表处理。Python 3 使用“bytes”。QString 表示为编码的可读字符串,这意味着它是一个“str”。

以下是改进的 Hello World 示例,展示了这些差异之一

 1
 2import sys
 3import random
 4
 5from PySide6.QtWidgets import (QApplication, QLabel,
 6     QPushButton, QVBoxLayout, QWidget)
 7from PySide6.QtCore import Qt, Slot
 8
 9class MyWidget(QWidget):
10    def __init__(self):
11        super().__init__()
12
13        self.hello = ["Hallo Welt", "Hei maailma", "Hola Mundo", "Привет мир"]
14
15        self.button = QPushButton("Click me!")
16        self.text = QLabel("Hello World")
17        self.text.setAlignment(Qt.AlignCenter)
18
19        self.layout = QVBoxLayout()
20        self.layout.addWidget(self.text)
21        self.layout.addWidget(self.button)
22        self.setLayout(self.layout)
23
24        self.button.clicked.connect(self.magic)
25
26    @Slot()
27    def magic(self):
28        self.text.setText(random.choice(self.hello))
29
30if __name__ == "__main__":
31    app = QApplication(sys.argv)
32
33    widget = MyWidget()
34    widget.resize(800, 600)
35    widget.show()
36
37    sys.exit(app.exec())

注意

在开发 Python 应用程序时,if 块只是一个良好的实践。它允许 Python 文件根据它是否作为另一个文件中的模块导入或直接运行而表现不同。在此两种情况中,__name__ 变量的值会有所不同。当文件直接运行时,它是 __main__,而当作为模块导入时,它是该模块的文件名(在此例中为 hello_world_ex)。在后一种情况下,除了 if 块之外,模块中定义的所有内容对该导入文件都是可用的。

请注意,QPushButton 的 clicked 信号连接到 magic 函数,以随机更改 QLabel 的 text 属性。`@Slot` 装饰器标记了作为槽的方法,并告知 QtMetaObject 有关这些方法。

移植 Qt C++ 示例#

Qt 提供了几个 C++ 示例来展示其特性并帮助初学者学习。您可以尝试将其中一个 C++ 示例移植到 Python。`books SQL 示例` 是一个好的起点,因为它不需要您在 Python 中编写特定的 UI 代码,而是可以使用它的 `.ui` 文件。

以下章节将指导您通过移植过程