移植 PyQt5 到 PySide6
遭遇問題與解法, 小技巧備忘
當採 Python 作為程式語言, 如需要較複雜的圖形介面, 通常很容易考慮 Qt, 針對跨平台, Windows, Mac OS, Linux, ...的圖形介面, Qt 提供了高階C++ 程式庫 , 而Python 那就需要靠bindings 來呼叫. 比較常用的有 PyQt (一家小公司開發提供的)與 PySide(Qt官方提供)
在Qt5 時代, PyQt5 因為耕耘時間較長, 我們很容易就可以找到可參考的文件甚或程式碼. 相比之下Qt的親兒子 PySide2 資源就少很多.
如果不需要考慮 PyQt5 採 GPL 授權, 而 PySide2 採 LGPL, 似乎使用 PyQt5 相比PySide2會比較容易.
到了 Qt6 官方的 Qt for Python project 亦即我們會使用的 PySide, 似乎會加大投資, 而 LGPL 也是誘因, 同時兩者語法 99% 接近, 大多數只要把:
import PyQt5 或者 From PyQt5 改為
import PySide6 或者 From PySide6
#記得先用 pip install PySide6 安裝 PySide6 ;
#最好也 pip uninstall PyQt5 避免兩者混亂
就可輕鬆將 Qt5 移轉為 Qt6 同時改為官方提供的 Binding (PySide6), 當然也有一些小細節必須克服.
以下把程式從 PyQt5 移轉到 PySide6 過程中遇到的問題與解法紀錄備忘:
pyqtSignal 與 @pyqtslot 須改為 Signal() 與 @Slot
PyQt5 的 事件處理有 pyqtSignal 與 @pyqtslot 在 PySide 對應的稱為 Signal() 與 @Slot
from PySide6.QtCore import Signal, Slot
PySide6 無 QDesktopWidget
PyQt5 的 QDesktopWidget 在PySide6並無提供, 通常用來計算實際桌面的幾何尺寸, 可以替換成:
改用 QApplication.instance().screens() 來取得螢幕可用像素 (例如: Windows必須扣掉工作列高度; Mac OS必須扣掉Dock高度;)
screenRect = QApplication.instance().screens()[0].availableSize()
screenH= screenRect.height()
screenW = screenRect.width()
PySide6, QApplication 無 desktop()
與上述無 QDesktopWidget 屬同類問題, 我們改用
QApplication.instance().screens()
取得顯示相關的屬性, 可能有多顯示器, 所以可以用 index [0] 來取得預設的顯示器
取得 QScreen Class 後就可以進行 原來desktop的作業了.
PySide6 無 sip, 需改用 shiboken C/C++ 綁定程式產生器
要刪除GUI物件, 於 PyQt5
from PyQt5 import sip
sip.delete(someGuiObject)
於 PySide6並無 sip, 可考慮用 shiboken (在安裝 PySide6 時應已連帶安裝好了), shiboken 作為 C/C++ 綁定程式產生器, 是PySide 與Qt 的橋樑如下圖, 通常我們不會直接使用, 以此例來說, 由於 PyQt 採用 sip (需額外安裝 pip install PyQt5 sip), 但 PySide 並無sip, 只能以 Shiboken 替代, 也許就以shiboken6 替代它.
import shiboken6 as sip
sip.delete(someGuiObject)
PySide6 QApplication只能 exec(), 不再 exec_()
於早期 為了 支援 Python2, 避免與關鍵字exec衝突, 原先 exec() 改成 exec_(), 一直到 PyQt5 一直保留. 但是到 PySide6 就不允許了. 例如我們必須將 sys.exit(app.exec_()) 改成:
app = QApplication(sys.argv)
#...
sys.exit(app.exec())
以下分享一些小技巧備忘:
PySide6 想保有 Python 經典蛇形命名法寫法
無論PyQt 還是 PySide, 其命名方式都是駝峰型(camelCase)的, 例如addWidget 方法就是駝峰型, 具體說是 lower camel case:
button = QPushButton("Test Naming")
layout = QVBoxLayout()
layout.addWidget(button)
如果, 一定要較接近傳統 Python 蛇形命名法(snake_case), 例如add_widget為蛇形命名法, 只要 加上這一行 from __feature__ import snake_case, true_property 即可, 同樣的方法, 就 Python 典型命名法:
from __feature__ import snake_case, true_property
button = QPushButton("Test Naming")
layout = QVBoxLayout()
layout.add_widget(button)
QCoreApplication, QGuiApplication 與 QApplication 使用時機
如果程式沒有要顯示圖形介面, 例如: 照片轉檔處理的 web Service, 就不需要大費周章 使用 QApplication, 啟動了用不到的服務占用資源, 建議改用 QCoreApplication
他們之間的繼承關係如下:
QCoreApplication -> QGuiApplication -> QApplication
但其套件所屬位置不同.
from PySide6.QtCore import QCoreApplication
from PySide6.QtWidgets import QApplication