注意事项#

API变更#

PySide6的一个目标是与PyQt API兼容,但有某些例外。

最新的注意事项和已知问题也将报告在开发者注意事项中。

__hash__()函数返回值#

对于类PySide6.QtCore.QDatePySide6.QtCore.QDateTimePySide6.QtCore.QTimePySide6.QtCore.QUrl返回的hash值将基于它们的字符串表示形式,因此具有相同值的对象将产生相同的hash值。

QString#

改变了QString参数的内容的方法和函数已被修改为接收不可变的Python Unicode(或str)并返回另一个Python Unicode/str作为修改后的字符串。

以下方法已按此方式更改了返回类型

类: QAbstractSpinBox, QDateTimeEdit, QDoubleSpinBox, QSpinBox, QValidator

  • fixup(string): string

  • validate(string, int): [QValidator.State, string, int]

类: QDoubleValidator, QIntValidator, QRegExpValidator

  • validate(string, int): [QValidator.State, string, int]

类: QClipboard

  • text(string, QClipboard.Mode mode=QClipboard.Clipboard): [string, string]

类: QFileDialog

与PyQt不同,PySide已将原始方法修改为返回一个元组,而不是getOpenFileNameAndFilter()getOpenFileNamesAndFilter()getSaveFileNameAndFilter()

  • getOpenFileName(QWidget parent=None, str caption=None, str dir=None, str filter=None, QFileDialog.Options options=0): [string, filter]

  • getOpenFileNames(QWidget parent=None, str caption=None, str dir=None, str filter=None, QFileDialog.Options options=0): [list(string), filter]

  • getSaveFileName(QWidget parent=None, str caption=None, str dir=None, str filter=None, QFileDialog.Options options=0): [string, filter]

类: QWebPage

  • javaScriptPrompt(QWebFrame, string, string): [bool, string]

类: QFontMetrics 和 QFontMetricsF

添加了两个新方法。这两个方法都接受一个字符字符串并将它转换为 QChar (以调用其 C++ 对应物)

  • widthChar(string)

  • boundingRectChar(string)

QTextStream#

在这个类中,一些函数名更改为避免与原生 Python 函数冲突。它们是:bin_()hex_()oct_()。唯一的修改是添加了‘_’字符。

QVariant#

由于移除了 QVariant,任何期望它作为参数的函数都可以接受任何 Python 对象(None 是无效的 QVariant)。当返回值时,返回的 QVariant 将被转换回其原始的 Python 对象类型。

当方法期望传入 QVariant::Type 时,程序员可以使用字符串(类型的名称)或该类型本身。

qApp “宏”#

QtWidgets 的 C++ API 提供了一个名为 qApp 的宏,它大致相当于 QtWidgets::QApplication</code>>->instance()

在 PySide 中,我们试图创建类似宏的体验。为此,qApp 变量被实现为一个正常的内置变量。导入 PySide6 后,你可以立即使用 qApp

作为一个用于“创建应用(如果尚未创建)”的有用快捷方式,我们建议使用以下方法:

qApp or QtWidgets.QApplication()

或者,如果你想要检查是否存在一个,只需简单地使用布尔值即可

if qApp:
    # do something if an application was created
    pass

使用 None 进行比较也是可能的,但这略微有些多余。

测试支持#

对于测试目的,您还可以通过调用以下方法来去除应用:

qApp.shutdown()

对于 5.14.2 版本,这目前是一个实验性功能,尚未完全测试。

嵌入状态#

在嵌入式模式下,C++ 中预先创建的应用对象没有 Python 包装器。与包装好的应用一起创建 qApp 变量。因此,在该嵌入式模式下不存在 qApp。请注意,您始终可以使用 QtWidgets.QApplication.instance() 来代替。

废弃的替代方案#

我们还尝试了一个带有 qApp() 函数的替代实现,这个函数更加Pythonic且没有问题,但由于很多人更喜欢 qApp 宏的简洁性,所以这里就是它。

富比较#

在 PySide 类的 tp_richcompare 实现中存在一个长期存在的错误。

  • 当一个类没有实现它时,将使用 object 的默认实现。这实现类似于 ==!=is 操作符。

  • 当一个类只实现了一个像 < 的函数时,默认实现被禁用,并且类似 obj in sequence 的表达式会因 NotImplemented 而失败。

这个疏忽在版本 5.15.1 中得到了修复。

特性#

在 Qt for Python 中,我们首次开始支持更符合 Python 习惯的用户界面。通过一个特殊的导入语句,你可以开启一些特性来替换 Python 解释器的某些方面。这是通过在 PySide6 导入之后立即使用一个导入语句来实现的。

snake_case#

使用如下语句

from __feature__ import snake_case

当前模块中的所有方法将从 camelCase 切换到 snake_case。单个大写字母替换为大写字母下面的下划线和字母。

true_property#

使用如下语句

from __feature__ import true_property

所有在 Qt6 文档中标记为属性的获取器和设置器函数,都被 Python 属性对象所替换。属性也在类的相应 QMetaObject 中列出。

这两个特性的示例#

一些 Qt for Python 的代码段可能如下所示

self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)

在选择上述特性后,这将读作

self.table.horizontal_header().section_resize_mode = QHeaderView.Stretch

此外,您也可以在 Shiboken 中直接声明非 Qt 库的属性,请参阅 property-declare

更多关于特性的信息#

有关特性的详细信息,请参阅此处: 为什么我们有 __feature__?

工具#

Qt for Python 随带了某些 Qt 工具

新Python枚举#

使用新枚举的动机#

长期以来,只有一个Shiboken枚举,它们尽可能地模拟能够在Qt中找到的现有枚举。这些枚举是既继承int的小类。

与此同时,Python枚举经过多年发展,已经成为现代Python的天然部分。其实现完美地适应了Python用户的需求。因此,停止在同一应用程序中保留两种不同的枚举实现,而无一例外地使用新的Python实现是顺理成章的。

现有工作#

从PySide 6.3开始的新枚举,用Python变体替换了Shiboken枚举,这使内建枚举与已经在QEnum部分中展示的QEnum“宏”相适应。

PySide中的枚举行为#

PySide 6.3中,旧枚举和新枚举存在双重实现,其中默认为旧枚举。新的枚举方法是PySide 6.4的默认方法,并在PySide 6.6中变为强制性的。存在一个名为PYSIDE6_OPTION_PYTHON_ENUM的环境变量,默认值为“1”。也可以通过指定不同标志来选择各种变体,但值为“0”(关闭)不再受支持。

枚举功能集部分,可以找到仍然可用的用于关闭某些枚举功能的选项。

旧枚举和新枚举之间的差异#

Python枚举和Shiboken枚举相互之间几乎是兼容的。小小的不同出现在限制上

  • Python枚举不能相互继承,而Shiboken枚举可以

  • Python枚举不允许未定义的值,Shiboken枚举则可以

  • Python枚举总是需要精确的一个参数,而Shiboken枚举有一个默认的零值

  • Python枚举很少从int继承,而Shiboken枚举总是继承

以下内容显示标志之间的差异更为明显

Shiboken标志构造函数示例在PySide中先于6.3存在

flags = Qt.Alignment()
enum = Qt.AlignmentFlag

枚举快捷操作符如下

Qt.AlignLeft = Qt.AlignmentFlag.AlignLeft
Qt.AlignTop  = Qt.AlignmentFlag.AlignTop

在PySide 6.3中,这些快捷操作符和标志不再存在(官方上)。取而代之的是,Python有一个枚举的Flags类,它是枚举类的子类。但请不要太过担忧,下面是好消息……

从旧枚举中进行平稳过渡#

突然更改所有枚举代码以使用新语法是繁琐且容易出错的,因为这些必要的更改不容易找到。因此,开发了一种宽容模式

宽容模式允许您继续使用旧的结构,但将它们静默地转换为新的结构。例如,如果您写下

flags = Qt.Alignment()
enum = Qt.AlignLeft

item.setForeground(QColor(Qt.green))

flags_type = QPainter.RenderHints
flags = QPainter.RenderHints()

chart_view.setRenderHint(QPainter.Antialiasing)

实际上得到以下结构的构造,这是推荐编写Flags和枚举的方式

flags = Qt.AlignmentFlag(0)
enum = Qt.AlignmentFlag.AlignLeft

item.setForeground(QColor(Qt.GlobalColor.green))

flags_type = QPainter.RenderHint
flags = QPainter.RenderHint(0)

chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)

此功能允许您在初始阶段忽略旧枚举和新枚举之间的差异,只要新枚举是类的属性。这不能应用于没有类的全局枚举(请参阅下面的局限性。)

容错模式和类型提示#

当你检查例如QtCore.pyi时,你只会找到新枚举,尽管旧枚举仍然被允许。此外,行补全只能与新结构一起使用,永远不会建议旧的结构。

以这种方式实现容错模式的原因是

  • 尽可能使过渡平滑,但

  • 鼓励人们在使用新代码时使用新枚举。

因此,您可以继续写入

self.text.setAlignment(Qt.AlignCenter)

但这个结构被推荐用于未来

self.text.setAlignment(Qt.AlignmentFlag.AlignCenter)

局限性#

当枚举类嵌入在正常的PySide类中时,容错模式工作得非常好。但是有一些全局枚举,特别是QtMsgType存在问题

t = QtMsgType.QtDebugMsg

不能以快捷方式书写

t = QtDebugMsg

因为没有提供容错模式实现的周围PySide类。通常,所需更改很容易找到,因为它们通常出现在导入语句中。

权限API#

跨平台权限API是在Qt 6.5版本中引入的,目前与macOS、iOS、Android和WebAssembly平台相关。使用此API,您的Qt应用程序可以检查和请求某些功能(如摄像头、麦克风、位置、蓝牙、联系人、日历)的权限。更多关于权限API的信息,请参阅此博客文章

当使用权限API的PySide6应用程序在解释模式(即python <main_file>.py)下运行时,实现权限API的代码将不工作。使您的PySide6应用程序使用权限API工作的唯一方法是打包应用程序。对于Android,这意味着使用pyside6-android-deploy工具,对于macOS,这意味着使用pyside6-deploy工具。

在解释模式下运行时,您可以使用以下if条件跳过权限检查/请求

is_deployed = "__compiled__" in globals()
if not is_deployed and sys.platform == "darwin":
    # code implementing permission check and request

这也可以在PySide6摄像头示例中看到。* __compiled __*是Nuitka属性,用于检查应用程序是作为独立应用程序运行还是在Python解释模式下运行。

Android#

对于Android,pyside6-android-deploy负责识别应用程序需要的权限,并使用<uses-permission>元素将这些权限添加到AndroidManifest.xml

macOS#

由于Android平台没有自动捆绑Python解释器,因此很明显,为了使PySide6应用程序在Android上运行,必须对PySide6应用程序进行打包。这对于桌面平台(如macOS)并不适用,在macOS上,Python解释器和其包可以轻松安装和运行。

macOS的问题在于,为了使权限API正常工作,你需要一个包含Info.plist文件的macOS包,该文件列出了使用usage description字符串的所需权限。当你用Python的交互模式运行时,即当你运行Python时,默认情况下Qt权限API会从Python解释器中获取Info.plist文件,它不包含所需权限的usage description字符串。当然,你可以修改Python框架安装的Info.plist文件,以便在终端运行PySide6应用程序时Qt权限API能够正常工作。然而,这并不可取。因此,唯一可行的解决方案是使用pyside6-deploy将PySide6应用程序捆绑成macOS应用程序包。这个macOS应用程序包将有自己的Info.plist文件。