欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

同时安装PyQt4和PyQt5之sip版本区分

程序员文章站 2022-05-11 08:18:07
...

如果同时安装了PyQt4和PyQt5(自己编译或者安装预编译版本),在运行某个PyQt4或者PyQt5的Python脚本时,很有可能出现以下的错误提示:

RuntimeError: the sip module implements API vX.X but the PyQt5.QtCore module requires API vY.Y

本文帮助大家解决这个问题。

==========================================================

1. 什么是sip?

sip是RiverBank(也就是PyQt的开发商)开发的用于PyQt的Python/C++混合编程解决方案。由于Qt框架的复杂性,PyQt并没有使用Cython、SWIG的混合编程方案,而是自己单独做了一套框架。sip包括一个sip工具、SDK和Python Module。

与SWIG类似,使用sip也需要先编写一个『配置文件』,然后使用sip工具『编译』为C++源文件,最后,和Qt库一起编译形成适用于Python的PyQt。

与SWIG不同的是,sip同时以Python Module的形式存在,也就是说,作为Python Module的PyQt,依赖于作为Python Module的sip。而对于SWIG,一旦自动生成的C++生成完毕,整个流程就不再依赖SWIG了。

2. sip和PyQt的版本依赖

本文写作之时,PyQt4的最高版本为4.8.7,与Qt4的最高版本相同;PyQt5的最高版本为5.5.0,略低于Qt5的最高版本5.5.1;而sip的版本为4.16.9,和PyQt的版本在字面上无关联。

在sip内部还有sip的API版本,作为C API的宏SIP_API_MAJOR_NR和SIP_API_MINOR_NR定义于sip.h。每个sip版本的API的版本是固定的,每个PyQt版本所需要的sip API版本是一个范围。如果PyQt想正常运行,sip的API版本必须落在该PyQt版本所需要的sip API版本的范围内。这个范围是这样规定的:

  1. API的主版本号必须相同。(主版本号不互相兼容原则)
  2. API的副版本号,PyQt版本所需的值不可大于sip提供的值。(副版本号仅向下兼容原则)

例如,

  1. 某PyQt在编译时选取的sip的API版本号为8.0,那么它可以在API的版本号为8.1的sip的支持下运行
  2. 某PyQt在编译时选取的sip的API版本号为8.1,那么它不可以在API的版本号为8.0的sip的支持下运行
  3. 某PyQt在编译时选取的sip的API版本号为9.0,那么它不可以在API的版本号为10.0的sip的支持下运行
  4. 某PyQt在编译时选取的sip的API版本号为10.0,那么它不可以在API的版本号为9.0的sip的支持下运行

那么sip的API版本和sip自身的版本有何关系?RiverBank的文档是这么说的:

SIP_API_MAJOR_NR

This is a C preprocessor symbol that defines the major number of the SIP API. Its value is a number. There is no direct relationship between this and the SIP version number. 

SIP_API_MINOR_NR

This is a C preprocessor symbol that defines the minor number of the SIP API. Its value is a number. There is no direct relationship between this and the SIP version number.

……

某系统已经安装了sip,想知道可以支持哪些版本的PyQt?

最好的办法似乎是:

  1. 启动python, import sip 并 print sip.SIP_VERSION_STR,得到sip的版本
  2. PyQt download 观看与这个版本的sip发布年代相近的PyQt

(如果有读者能提供更好的经验欢迎指点)

3. PyQt4和PyQt5共享sip之不可能

我们假设这样一种情况:

某系统自带了版本为4.7.1的PyQt4,以及对应的sip(版本4.14,API版本9.0)。今欲安装最新的PyQt 5.5.0。发现编译过程需要sip 4.16,API版本11.2,于是下载和重新编译sip 4.16,勉强编译通过,但安装时犯了难,因为不存在同时能够支持PyQt4.7和PyQt5.5的sip!

当然,可能的解决方法有

  1. 换个系统
  2. virtualenv
  3. 升级PyQt4到最新4.8.7,feature是能够向下兼容4.7.1的

有没有不那么兴师动众的方法?

4. 问题之解决

事实上,只需要让PyQt4和PyQt5各用自己的sip就行了。

第一步,找到sip的安装位置。通常的位置是Python的site-packages,其文件名为sip.pyd(Windows)或者 sip.so (*nix)。例如,在Windows下,其位置为C:\Python27\Lib\site-packages\sip.pyd,在Linux(Ubuntu)下,其位置为/usr/lib/python2.7/dist-packages/sip.****.so;在macOS下,其位置为/Library/Python/2.7/site-packages/sip.so(对于系统自带的Python)

第二步,把sip移动到对应版本的PyQt目录内。例如,若sip和PyQt4匹配而与PyQt5不匹配,就把sip放到PyQt4的安装目录内。

第三步,编译不匹配的PyQt版本并安装,但不要安装sip,而是把sip的python module直接放入对应的PyQt的安装目录内。

到了这一步,PyQt4和PyQt5的安装目录有了各自对应的sip。此时若import PyQt4或者import PyQt5,会遇到sip无法找到的问题。没有关系。我们利用__init__.py这个文件,在import PyQtX之时自动找到对应版本的sip。方法是这样的:

打开PyQtX/__init__.py(X是4和5,每个PyQt都需要做),默认情况下这个文件只有PyQt的版权声明。在下面填上几句:

import sys
import os
sys.path.append(os.path.realpath(os.path.dirname(__file__)))

我们知道,PyQt的核心module都是以动态链接库形式存在。你import PyQt4的时候,在加载动态链接库之前,Python会发现PyQt4/__init__.py并加以执行。这样,当动态链接库(比如QtCore)寻找sip的时候,它总会在自己所在的目录找到正确版本的sip。

相关标签: pyqt