同時安裝PyQt4和PyQt5之sip版本區分
如果同時安裝了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版本的範圍內。這個範圍是這樣規定的:
- API的主版本號必須相同。(主版本號不互相相容原則)
- API的副版本號,PyQt版本所需的值不可大於sip提供的值。(副版本號僅向下相容原則)
例如,
- 某PyQt在編譯時選取的sip的API版本號為8.0,那麼它可以在API的版本號為8.1的sip的支援下執行
- 某PyQt在編譯時選取的sip的API版本號為8.1,那麼它不可以在API的版本號為8.0的sip的支援下執行。
- 某PyQt在編譯時選取的sip的API版本號為9.0,那麼它不可以在API的版本號為10.0的sip的支援下執行。
- 某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?
最好的辦法似乎是:
- 啟動python, import sip 並 print sip.SIP_VERSION_STR,得到sip的版本
- 去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!
當然,可能的解決方法有
- 換個系統
- virtualenv
- 升級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。方法是這樣的:
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。