4. PyQt与PySide的兼容性封装
任务目标
编写一个轻量抽象层,让同一份代码无需修改即可在 PyQt5 和 PySide6 下运行,并验证窗口能够正常创建与显示。
核心实现
两种 Qt 绑定的 API 高度相似,主要差异集中在:exec() 与 exec_()、Signal 与 pyqtSignal、Slot 与 pyqtSlot、枚举类型访问方式。以下封装层统一处理这些差异:
# qt_compat.py
import sys
# --- 绑定选择与导入 ---
try:
from PySide6 import QtCore, QtWidgets
from PySide6.QtCore import Signal, Slot
QT_BINDING = "PySide6"
# PySide6 中 exec() 可直接使用,无需别名
except ImportError:
try:
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import pyqtSignal as Signal, pyqtSlot as Slot
QT_BINDING = "PyQt5"
# PyQt5 中 exec() 在部分版本不可用,需提供别名
if not hasattr(QtCore.QCoreApplication, "exec"):
QtCore.QCoreApplication.exec = QtCore.QCoreApplication.exec_
except ImportError:
raise ImportError("需要安装 PySide6 或 PyQt5")
# --- 统一枚举访问 ---
# PyQt5 使用 QFlag 包装,PySide6 直接暴露 IntEnum
def enum_value(enum_class, name):
"""跨绑定获取枚举值,如 enum_value(Qt.AlignmentFlag, 'AlignCenter')"""
if QT_BINDING == "PySide6":
return getattr(enum_class, name)
else:
# PyQt5 中枚举值通过 __getattr__ 直接访问
return getattr(enum_class, name)
# --- 统一应用执行入口 ---
def run_app(app):
"""兼容两种绑定的事件循环启动"""
if QT_BINDING == "PySide6":
return app.exec()
else:
return app.exec_()
# --- 统一信号定义辅助 ---
# 用法:class MyWidget(QtWidgets.QWidget):
# value_changed = qt_signal(int)
def qt_signal(*types):
"""返回当前绑定对应的信号类"""
return Signal(*types)
# --- 统一槽装饰器 ---
# 用法:@qt_slot(int)
def qt_slot(*types):
"""返回当前绑定对应的槽装饰器"""
return Slot(*types)
最小验证程序
# demo.py
from qt_compat import QtWidgets, QT_BINDING, run_app, enum_value
def main():
app = QtWidgets.QApplication([])
window = QtWidgets.QWidget()
window.setWindowTitle(f"兼容层验证 - {QT_BINDING}")
window.resize(320, 200)
layout = QtWidgets.QVBoxLayout(window)
label = QtWidgets.QLabel(f"当前绑定: {QT_BINDING}")
layout.addWidget(label)
# 验证枚举访问兼容性
align = enum_value(QtCore.Qt.AlignmentFlag, "AlignCenter")
label.setAlignment(align)
window.show()
return run_app(app)
if __name__ == "__main__":
sys.exit(main())
注意:demo.py 中使用了 QtCore,需在文件顶部补充 from qt_compat import QtCore 或直接通过 qt_compat 暴露。为保持简洁,可将 QtCore 一并导入:
# 修正后的导入行
from qt_compat import QtCore, QtWidgets, QT_BINDING, run_app, enum_value
运行与验证
- 安装依赖(任选其一):
pip install PySide6或pip install PyQt5 - 运行程序:
python demo.py - 通过条件:
窗口标题显示当前实际绑定的名称(如“兼容层验证 - PySide6”),标签文字居中显示,窗口可正常关闭。分别在两种绑定环境下运行同一份代码,均无报错即验证通过。
验证要点
- 若先安装 PySide6 再安装 PyQt5,
qt_compat.py会优先选择 PySide6;反之亦然。可通过QT_BINDING变量确认当前生效的绑定。 - PyQt5 下若遇到
QApplication.exec()不存在的情况,封装层已自动回退到exec_()。 - 枚举访问统一通过
enum_value()完成,避免直接使用Qt.AlignCenter这类在 PySide6 中已废弃的写法。