移植 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)

Python-Shiboken-Qt5

Shiboken Binding Generator

取自 Qt for Python

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