TLS支持
对传输层安全性(TLS)的支持变得越来越重要,特别是在移动平台上,其中应用程序通常是基于云的服务器的前端。由于Python和Qt都实现了支持TLS的不同API,因此PyQt应用程序可以选择使用哪种API。这在部署应用程序时尤其重要,因为支持可能必须包含在应用程序本身中或内置于应用程序本身中。
理想情况下,将使用目标提供的TLS实现(例如,Windows上的CryptoAPI,macOS和iOS上的安全传输)。这意味着安全更新(包括证书更新)将由目标操作系统的供应商处理,并且可以被应用程序忽略。不幸的是,没有常见的TLS API。
Python支持在OpenSSL v1.0中实现的TLS API(从Python v3.7.0开始,OpenSSL v1.1)。在Linux上,实际上是供应商提供的API,因此Python可以使用它。但是,在Windows和macOS上,标准Python二进制安装程序包含OpenSSL库的副本。该问题的解决方案是PEP 543的主题, 但尚未实施。
Qt支持macOS和iOS上的本机TLS实现,但在其他平台上(Linux除外),部署的应用程序必须包含它自己的OpenSSL实现。
枚举
版本5.11中的新功能。
PyQt(或者说SIP)使用专用的Python类型包装C / C ++枚举。枚举的成员在与枚举本身相同的范围内可见。
Qt正在越来越多地使用C ++ 11范围的枚举,并且对它们的支持被添加到SIP v4.19.4中。Scoped枚举使用标准enum.Enum
Python类型实现。在这种情况下,枚举的成员仅在枚举的范围内可见。
可见性的差异是不幸的,因为它需要Python程序员了解底层C ++枚举的本质。
使用SIP v4.19.9,传统C / C ++枚举的成员现在也可以在枚举范围内看到。强烈建议始终通过指定枚举的范围来引用枚举成员。PyQt6不允许任何其他访问方法。
溢出检查
在将数字Python对象转换为C ++类型时,PyQt(或更确切地说SIP)不检查溢出。更糟糕的是,溢出的值是未定义的 - 不能假设过多的高阶位被简单地丢弃。
应用程序可以通过调用该sip.enableoverflowchecking()
函数随时启用(并随后禁用)溢出检查。
计划在某些未来版本的PyQt中默认启用溢出检查。
退出时崩溃
当Python解释器离开 作用域时(例如,当它从函数返回时),它可能会垃圾收集该作用域的所有本地对象。它的完成顺序实际上是随机的。从理论上讲,这可能会导致问题,因为它可能意味着任何包装的Qt实例的C ++析构函数都是按照Qt不期望的顺序调用的,并且可能导致崩溃。
但是,实际上,当应用程序终止时,这可能只是一个问题。例如,最好只在销毁所有小部件后销毁任何QApplication实例。
作为一种减轻这种可能性的方法,PyQt5确保在应用程序终止时不调用任何模块级对象的析构函数。这意味着遵循以下模式的代码在退出时不太可能崩溃:
if __name__ == '__main__':
app = QApplication(sys.argv)
w = QWidget()
w.show()
app.exec()
另一种常见模式(以及使用setuptool入口点时需要的模式)是上面的代码放在一个单独的函数中,通常称为main()
。然后,当函数返回时会导致问题,因为可能以错误的顺序调用QApplication和 QWidget实例的析构函数。为尽量减少发生这种情况的可能性,建议采用以下模式:
app = None
def main():
global app
app = QApplication(sys.argv)
w = QWidget()
w.show()
app.exec()
if __name__ == '__main__':
main()
该QWidget的时候析构函数被调用 main()
的回报,但在模块级参考 QApplication的所有被调用的实例将阻止其析构函数。
关键字参数
PyQt5支持使用关键字参数作为可选参数。虽然PyQt5和Qt文档可能表明参数具有特定名称,但您可能会发现PyQt5实际上使用了不同的名称。这是因为参数的名称不是Qt API的一部分,并且在命名类似参数的方式上存在一些不一致。不同版本的Qt可能会为参数使用不同的名称,这些名称不会影响C ++ API,但会破坏Python API。
PyQt5为所有类,函数和方法生成的文档字符串将包含正确的参数名称。在PyQt5的未来版本中,文档也将保证包含正确的参数名称。
Python字符串,Qt字符串和
Qt使用QString
类来表示Unicode字符串,并使用它 QByteArray
来表示字节数组或字符串。在Python v3中,相应的本机对象类型是str
和bytes
。在Python v2中,相应的本机对象类型是unicode
和str
。
PyQt5尽力在各种类型的对象之间自动转换。必要时可以轻松进行显式转换。
在某些情况下,PyQt5不会执行自动转换,因为有必要区分不同的重载方法。
对于Python v3,默认情况下会进行以下转换。
- 如果Qt期望(或版本),那么PyQt5将接受 仅包含ASCII字符,a ,a 或实现缓冲协议的Python对象。
char*
const
str
bytesQByteArray
- 如果Qt期望一个
char
(或一个const
版本),那么PyQt5将接受与之相同的类型,并且还要求提供单个字符。char *
- 如果Qt期望a 或(或 版本),那么PyQt5将接受a 。
signed char *
unsigned char *
constbytes
- 如果Qt期望一个或一个(或一个 版本),那么PyQt5将接受长度为1的a。
signed char
unsigned char
constbytes
- 如果Qt期望a
QString
那么PyQt5将接受astr
,abytes
只包含ASCII字符,aQByteArray
或None
。 - 如果Qt期望一个,
QByteArray
那么PyQt5也会接受一个bytes
。 - 如果Qt期望a
QByteArray
那么PyQt5也会接受str
只包含Latin-1字符的那个。
对于Python v2,默认情况下会进行以下转换。
- 如果Qt的期望一个,或一个(或 版本),那么PyQt5将接受的是只包含ASCII字符,一,一个,或者一个Python实现该缓冲协议对象。
char *
signed char *
unsigned char *
const
unicode
strQByteArray
- 如果Qt的期望一个
char
,或一个(或 版本),那么PyQt5将接受相同类型为, 和,并且还需要一个单独的字符设置。signed char
unsigned char
const
char *
signed char *unsigned char *
- 如果Qt期望a
QString
那么PyQt5将接受aunicode
,astr
只包含ASCII字符,aQByteArray
或None
。 - 如果Qt期望
QByteArray
然后PyQt5将接受astr
。 - 如果Qt期望a
QByteArray
那么PyQt5将接受unicode
仅包含Latin-1字符的那个。
请注意,Python v2和v3之间的不同行为是由于v3减少了对缓冲区协议的支持。
历史上QString
区分空字符串和空字符串。当前版本的Qt将空字符串视为空字符串,但PyQt5应用程序可能会调用其他C ++代码来保持区别。因此PyQt5将转换None
为null QString
。不进行反向转换,将null和empty QString
都转换为空(即零长度)Python字符串。
垃圾收集
C ++不会垃圾收集未引用的类实例,而Python会这样做。在下面的C ++片段中,即使第一个不能再从程序中引用,也存在两种颜色:
col = new QColor();
col = new QColor();
在相应的Python片段中,第二种颜色在第二种颜色被分配时被销毁col
:
col = QColor()
col = QColor()
在Python中,每种颜色必须分配给不同的名称。通常这是在类定义中完成的,因此代码片段将类似于:
self.col1 = QColor()
self.col2 = QColor()
有时,Qt类实例将维护指向另一个实例的指针,并最终调用该第二个实例的析构函数。最常见的示例是QObject(及其任何子类)保持指向其子节点的指针,并将自动调用其析构函数。在这些情况下,相应的Python对象也将保留对相应子对象的引用。
因此,在以下Python片段中,第一个 QLabel在分配第二个QLabel时不会被销毁,lab
因为父QWidget 仍然具有对它的引用:
parent = QWidget()
lab = QLabel("First label", parent)
lab = QLabel("Second label", parent)
多重继承
不可能从多个Qt类定义一个新的Python类子类。例外是专门用作mixin类的类,例如实现Qt接口的那些(如QQmlParserStatus)。
访问受保护的成员函数
如果未从Python创建C ++类的实例,则无法访问该实例的受保护成员函数。尝试这样做会引发Python异常。此外,永远不会调用与实例的虚拟成员函数对应的任何Python方法。
None
和NULL
在整个PyQt5中,None
可以NULL
在底层C ++代码可接受的任何地方指定该值。
同样,无论何时由底层C ++代码返回它都会NULL
转换为None
。
支持void *
PyQt5(实际上是SIP)将值表示为sip.voidptr类型的对象。这些值通常用于在不同的Python模块之间传递外部对象的地址。为了简化这一过程,只要需要sip.voidptr,就可以使用Python整数(或Python可以转换为整数的任何东西)。void *
可以使用int()
内置函数将sip.voidptr转换为Python整数 。
可以使用sip.voidptr将sip.voidptr转换为Python字符串 asstring()
。该asstring()
方法采用可选的整数参数,该参数是以字节为单位的数据长度。
也可以通过调用其setsize()
方法给sip.voidptr一个大小(即,指向的内存块的大小)。如果它有一个大小,那么它也能够支持Python的缓冲协议,并且行为类似于Python memoryview
对象,因此可以将内存块视为可变的字节列表。它还意味着Python结构模块可用于在内存,内存映射文件或共享内存中解压缩和打包二进制数据结构。